Merge "ultrahdr: do not select less accurate integer method during dct" into main
diff --git a/Android.bp b/Android.bp
index 3992f82..7f1ef67 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,6 +36,14 @@
],
}
+cc_library_headers {
+ name: "native_headers",
+ host_supported: true,
+ export_include_dirs: [
+ "include/",
+ ],
+}
+
ndk_headers {
name: "libandroid_headers",
from: "include/android",
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index f1d8c72..fc0801c 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -228,10 +228,6 @@
chmod 0666 /sys/kernel/debug/tracing/events/thermal/cdev_update/enable
chmod 0666 /sys/kernel/tracing/events/thermal/cdev_update/enable
-# Tracing disabled by default
- write /sys/kernel/debug/tracing/tracing_on 0
- write /sys/kernel/tracing/tracing_on 0
-
# Read and truncate the kernel trace.
chmod 0666 /sys/kernel/debug/tracing/trace
chmod 0666 /sys/kernel/tracing/trace
@@ -310,18 +306,9 @@
chmod 0666 /sys/kernel/tracing/events/synthetic/suspend_resume_minimal/enable
chmod 0666 /sys/kernel/debug/tracing/events/synthetic/suspend_resume_minimal/enable
-on late-init && property:ro.boot.fastboot.boottrace=enabled
- setprop debug.atrace.tags.enableflags 802922
- setprop persist.traced.enable 0
- write /sys/kernel/tracing/events/binder/binder_transaction/enable 1
- write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 1
- write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 1
- write /sys/kernel/tracing/events/binder/binder_set_priority/enable 1
- write /sys/kernel/tracing/events/binder/binder_lock/enable 1
- write /sys/kernel/tracing/events/binder/binder_locked/enable 1
- write /sys/kernel/tracing/events/binder/binder_unlock/enable 1
- write /sys/kernel/debug/tracing/tracing_on 1
- write /sys/kernel/tracing/tracing_on 1
+on late-init && property:ro.boot.fastboot.boottrace=
+ write /sys/kernel/debug/tracing/tracing_on 0
+ write /sys/kernel/tracing/tracing_on 0
# Only create the tracing instance if persist.mm_events.enabled
# Attempting to remove the tracing instance after it has been created
@@ -534,7 +521,6 @@
chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/id
chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/id
-
on property:persist.debug.atrace.boottrace=1
start boottrace
@@ -543,17 +529,3 @@
user root
disabled
oneshot
-
-on property:sys.boot_completed=1 && property:ro.boot.fastboot.boottrace=enabled
- setprop debug.atrace.tags.enableflags 0
- setprop persist.traced.enable 1
- write /sys/kernel/tracing/events/binder/binder_transaction/enable 0
- write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 0
- write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 0
- write /sys/kernel/tracing/events/binder/binder_set_priority/enable 0
- write /sys/kernel/tracing/events/binder/binder_lock/enable 0
- write /sys/kernel/tracing/events/binder/binder_locked/enable 0
- write /sys/kernel/tracing/events/binder/binder_unlock/enable 0
- write /sys/kernel/debug/tracing/tracing_on 0
- write /sys/kernel/tracing/tracing_on 0
-
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index 790556c..cd2652c 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -75,23 +75,6 @@
return EXIT_FAILURE;
}
- // Wait a little while for dumpstatez to stop if it is running
- bool dumpstate_running = false;
- for (int i = 0; i < 20; i++) {
- char buf[PROPERTY_VALUE_MAX];
- property_get("init.svc.dumpstatez", buf, "");
- dumpstate_running = strcmp(buf, "running") == 0;
-
- if (!dumpstate_running) break;
-
- sleep(1);
- }
-
- if (dumpstate_running) {
- fprintf(stderr, "FAIL:dumpstatez service is already running\n");
- return EXIT_FAILURE;
- }
-
// TODO: code below was copy-and-pasted from bugreport.cpp (except by the
// timeout value);
// should be reused instead.
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index aa42541..4842312 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -207,6 +207,9 @@
int PropertiesHelper::dry_run_ = -1;
int PropertiesHelper::unroot_ = -1;
int PropertiesHelper::parallel_run_ = -1;
+#if !defined(__ANDROID_VNDK__)
+int PropertiesHelper::strict_run_ = -1;
+#endif
bool PropertiesHelper::IsUserBuild() {
if (build_type_.empty()) {
@@ -237,6 +240,16 @@
return parallel_run_ == 1;
}
+#if !defined(__ANDROID_VNDK__)
+bool PropertiesHelper::IsStrictRun() {
+ if (strict_run_ == -1) {
+ // Defaults to using stricter timeouts.
+ strict_run_ = android::base::GetBoolProperty("dumpstate.strict_run", true) ? 1 : 0;
+ }
+ return strict_run_ == 1;
+}
+#endif
+
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b00c46e..6049e3e 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -193,11 +193,23 @@
*/
static bool IsParallelRun();
+ /*
+ * Strict-run mode is determined by the `dumpstate.strict_run` sysprop which
+ * will default to true. This results in shortened timeouts for flaky
+ * sections.
+ */
+#if !defined(__ANDROID_VNDK__)
+ static bool IsStrictRun();
+#endif
+
private:
static std::string build_type_;
static int dry_run_;
static int unroot_;
static int parallel_run_;
+#if !defined(__ANDROID_VNDK__)
+ static int strict_run_;
+#endif
};
/*
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 7e3d273..e132b35 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -822,9 +822,12 @@
RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
CommandOptions::WithTimeout(1).Always().Build());
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
- id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
- options_->args.c_str(), options_->bugreport_mode_string.c_str());
+ printf(
+ "Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d strict_run=%d args=%s "
+ "bugreport_mode=%s\n",
+ id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
+ PropertiesHelper::IsStrictRun(), options_->args.c_str(),
+ options_->bugreport_mode_string.c_str());
printf("\n");
}
@@ -1046,7 +1049,8 @@
MYLOGE("Could not open %s to dump incident report.\n", path.c_str());
return;
}
- RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(20).Build());
+ RunCommandToFd(fd, "", {"incident", "-u"},
+ CommandOptions::WithTimeout(PropertiesHelper::IsStrictRun() ? 20 : 120).Build());
bool empty = 0 == lseek(fd, 0, SEEK_END);
if (!empty) {
// Use a different name from "incident.proto"
@@ -1416,12 +1420,12 @@
auto ret = sm->list([&](const auto& interfaces) {
for (const std::string& interface : interfaces) {
std::string cleanName = interface;
- std::replace_if(cleanName.begin(),
- cleanName.end(),
- [](char c) {
- return !isalnum(c) &&
- std::string("@-_:.").find(c) == std::string::npos;
- }, '_');
+ std::replace_if(
+ cleanName.begin(), cleanName.end(),
+ [](char c) {
+ return !isalnum(c) && std::string("@-_.").find(c) == std::string::npos;
+ },
+ '_');
const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName;
bool empty = false;
@@ -1754,6 +1758,20 @@
RunCommand("SYSTEM PROPERTIES", {"getprop"});
+ DumpFile("SYSTEM BUILD-TIME RELEASE FLAGS", "/system/etc/build_flags.json");
+ DumpFile("SYSTEM_EXT BUILD-TIME RELEASE FLAGS", "/system_ext/etc/build_flags.json");
+ DumpFile("PRODUCT BUILD-TIME RELEASE FLAGS", "/product/etc/build_flags.json");
+ DumpFile("VENDOR BUILD-TIME RELEASE FLAGS", "/vendor/etc/build_flags.json");
+
+ DumpFile("SYSTEM BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
+ "/system/etc/aconfig_flags.textproto");
+ DumpFile("SYSTEM_EXT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime"
+ " values)", "/system_ext/etc/aconfig_flags.textproto");
+ DumpFile("PRODUCT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
+ "/product/etc/aconfig_flags.textproto");
+ DumpFile("VENDOR BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
+ "/vendor/etc/aconfig_flags.textproto");
+
RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"});
RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
@@ -2602,11 +2620,7 @@
}
static void register_sig_handler() {
- signal(SIGPIPE, [](int) {
- MYLOGE("Connection with client lost, canceling.");
- ds.Cancel();
- abort();
- });
+ signal(SIGPIPE, SIG_IGN);
}
bool Dumpstate::FinishZipFile() {
@@ -3131,6 +3145,12 @@
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
+ if (PropertiesHelper::IsStrictRun()) {
+ MYLOGI(
+ "Running on strict-run mode, which has shorter timeouts "
+ "(to disable, call 'setprop dumpstate.strict_run false')\n");
+ }
+
MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
id_, options_->args.c_str(), options_->bugreport_mode_string.c_str(), version_.c_str());
@@ -3590,7 +3610,7 @@
// an app; they are irrelevant here because bugreport is triggered via command line.
// Update Last ID before calling Run().
Initialize();
- status = Run(-1 /* calling_uid */, "" /* calling_package */);
+ status = Run(0 /* calling_uid */, "" /* calling_package */);
}
return status;
}
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
index b17cba1..fd1df35 100644
--- a/cmds/installd/CrateManager.cpp
+++ b/cmds/installd/CrateManager.cpp
@@ -29,9 +29,10 @@
#include <sys/xattr.h>
#include <unistd.h>
-#include <fstream>
-#include <string>
#include <utils.h>
+#include <fstream>
+#include <functional>
+#include <string>
#include "utils.h"
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
index 1f30b5d..d9b590f 100644
--- a/cmds/installd/CrateManager.h
+++ b/cmds/installd/CrateManager.h
@@ -25,6 +25,7 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <functional>
#include <optional>
#include <string>
#include <vector>
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index bb6639e..b302f52 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -236,6 +236,16 @@
} \
}
+// we could have tighter checks, but this is only to avoid hard errors. Negative values are defined
+// in UserHandle.java and carry specific meanings that may not be handled by certain APIs here.
+#define ENFORCE_VALID_USER(userId) \
+ { \
+ if (static_cast<uid_t>(std::abs(userId)) >= \
+ std::numeric_limits<uid_t>::max() / AID_USER_OFFSET) { \
+ return error("userId invalid: " + std::to_string(userId)); \
+ } \
+ }
+
#define CHECK_ARGUMENT_UUID(uuid) { \
binder::Status status = checkArgumentUuid((uuid)); \
if (!status.isOk()) { \
@@ -416,10 +426,12 @@
*/
static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid,
bool existing) {
+ ScopedTrace tracer("restorecon-lazy");
int res = 0;
char* before = nullptr;
char* after = nullptr;
if (!existing) {
+ ScopedTrace tracer("new-path");
if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
PLOG(ERROR) << "Failed recursive restorecon for " << path;
@@ -446,6 +458,7 @@
// If the initial top-level restorecon above changed the label, then go
// back and restorecon everything recursively
if (strcmp(before, after)) {
+ ScopedTrace tracer("label-change");
if (existing) {
LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
<< path << "; running recursive restorecon";
@@ -480,11 +493,15 @@
static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid,
long project_id) {
- if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
- PLOG(ERROR) << "Failed to prepare " << path;
- return -1;
+ {
+ ScopedTrace tracer("prepare-dir");
+ if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << path;
+ return -1;
+ }
}
if (internal_storage_has_project_id()) {
+ ScopedTrace tracer("set-quota");
return set_quota_project_id(path, project_id, true);
}
return 0;
@@ -493,14 +510,20 @@
static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
uid_t uid, gid_t gid, long project_id) {
auto path = StringPrintf("%s/%s", parent.c_str(), name);
- int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid);
+ int ret;
+ {
+ ScopedTrace tracer("prepare-cache-dir");
+ ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid);
+ }
if (ret == 0 && internal_storage_has_project_id()) {
+ ScopedTrace tracer("set-quota-cache-dir");
return set_quota_project_id(path, project_id, true);
}
return ret;
}
static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
+ ScopedTrace tracer("prepare-app-profile");
int32_t uid = multiuser_get_uid(userId, appId);
int shared_app_gid = multiuser_get_shared_gid(userId, appId);
if (shared_app_gid == -1) {
@@ -633,6 +656,7 @@
int32_t previousUid, int32_t cacheGid,
const std::string& seInfo, mode_t targetMode,
long projectIdApp, long projectIdCache) {
+ ScopedTrace tracer("create-dirs");
struct stat st{};
bool parent_dir_exists = (stat(path.c_str(), &st) == 0);
@@ -682,6 +706,7 @@
int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
int32_t targetSdkVersion, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -709,6 +734,7 @@
long projectIdCache = get_project_id(uid, PROJECT_ID_APP_CACHE_START);
if (flags & FLAG_STORAGE_CE) {
+ ScopedTrace tracer("ce");
auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode,
@@ -735,6 +761,7 @@
}
}
if (flags & FLAG_STORAGE_DE) {
+ ScopedTrace tracer("de");
auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode,
@@ -752,13 +779,14 @@
}
if (flags & FLAG_STORAGE_SDK) {
+ ScopedTrace tracer("sdk");
// Safe to ignore status since we can retry creating this by calling reconcileSdkData
auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags);
if (!ignore.isOk()) {
PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName;
}
-
} else {
+ ScopedTrace tracer("destroy-sdk");
// Package does not need sdk storage. Remove it.
destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
}
@@ -773,6 +801,8 @@
binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory(
const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
int32_t appId, int32_t flags) {
+ ENFORCE_VALID_USER(userId);
+
int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
if (sdkSandboxUid == -1) {
// There no valid sdk sandbox process for this app. Skip creation of data directory
@@ -811,6 +841,7 @@
int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
int32_t targetSdkVersion, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -822,6 +853,7 @@
const android::os::CreateAppDataArgs& args,
android::os::CreateAppDataResult* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(args.userId);
// Locking is performed depeer in the callstack.
int64_t ceDataInode = -1;
@@ -837,6 +869,10 @@
const std::vector<android::os::CreateAppDataArgs>& args,
std::vector<android::os::CreateAppDataResult>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ for (const auto& arg : args) {
+ ENFORCE_VALID_USER(arg.userId);
+ }
+
// Locking is performed depeer in the callstack.
std::vector<android::os::CreateAppDataResult> results;
@@ -851,6 +887,7 @@
binder::Status InstalldNativeService::reconcileSdkData(
const android::os::ReconcileSdkDataArgs& args) {
+ ENFORCE_VALID_USER(args.userId);
// Locking is performed depeer in the callstack.
return reconcileSdkData(args.uuid, args.packageName, args.subDirNames, args.userId, args.appId,
@@ -874,6 +911,7 @@
int userId, int appId, int previousAppId,
const std::string& seInfo, int flags) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -957,6 +995,7 @@
binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -1024,6 +1063,7 @@
binder::Status InstalldNativeService::clearAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -1115,6 +1155,7 @@
binder::Status InstalldNativeService::clearSdkSandboxDataPackageDirectory(
const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
int32_t flags) {
+ ENFORCE_VALID_USER(userId);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -1201,6 +1242,7 @@
binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -1271,6 +1313,8 @@
binder::Status InstalldNativeService::destroySdkSandboxDataPackageDirectory(
const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
int32_t flags) {
+ ENFORCE_VALID_USER(userId);
+
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -1418,6 +1462,7 @@
int32_t userId, int32_t snapshotId,
int32_t storageFlags, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -1552,6 +1597,7 @@
const int32_t appId, const std::string& seInfo, const int32_t userId,
const int32_t snapshotId, int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -1624,6 +1670,7 @@
const int32_t userId, const int64_t ceSnapshotInode, const int32_t snapshotId,
int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -1657,6 +1704,7 @@
const std::optional<std::string>& volumeUuid, const int32_t userId,
const std::vector<int32_t>& retainSnapshotIds) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
LOCK_USER();
@@ -1847,9 +1895,12 @@
binder::Status InstalldNativeService::createUserData(const std::optional<std::string>& uuid,
int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
LOCK_USER();
+ ScopedTrace tracer("create-user-data");
+
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
if (flags & FLAG_STORAGE_DE) {
if (uuid_ == nullptr) {
@@ -1865,6 +1916,7 @@
binder::Status InstalldNativeService::destroyUserData(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
LOCK_USER();
@@ -2355,11 +2407,15 @@
p->fts_number = p->fts_parent->fts_number;
switch (p->fts_info) {
case FTS_D:
- if (p->fts_level == 4
+ if (p->fts_level == 3
+ && !strcmp(p->fts_parent->fts_name, "obb")
+ && !strcmp(p->fts_parent->fts_parent->fts_name, "Android")) {
+ p->fts_number = 1;
+ } else if (p->fts_level == 4
&& !strcmp(p->fts_name, "cache")
&& !strcmp(p->fts_parent->fts_parent->fts_name, "data")
&& !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) {
- p->fts_number = 1;
+ p->fts_number = 2;
}
[[fallthrough]]; // to count the directory
case FTS_DEFAULT:
@@ -2368,9 +2424,13 @@
case FTS_SLNONE:
int64_t size = (p->fts_statp->st_blocks * 512);
if (p->fts_number == 1) {
- stats->cacheSize += size;
+ stats->codeSize += size;
+ } else {
+ if (p->fts_number == 2) {
+ stats->cacheSize += size;
+ }
+ stats->dataSize += size;
}
- stats->dataSize += size;
break;
}
}
@@ -2644,6 +2704,7 @@
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
// NOTE: Locking is relaxed on this method, since it's limited to
// read-only measurements without mutation.
@@ -2716,11 +2777,6 @@
extStats.dataSize = dataSize;
atrace_pm_end();
} else {
- atrace_pm_begin("obb");
- auto obbPath = create_data_path(uuid_) + "/media/obb";
- calculate_tree_size(obbPath, &extStats.codeSize);
- atrace_pm_end();
-
atrace_pm_begin("code");
calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize);
atrace_pm_end();
@@ -2751,9 +2807,10 @@
atrace_pm_begin("external");
auto dataMediaPath = create_data_media_path(uuid_, userId);
collectManualExternalStatsForUser(dataMediaPath, &extStats);
+
#if MEASURE_DEBUG
LOG(DEBUG) << "Measured external data " << extStats.dataSize << " cache "
- << extStats.cacheSize;
+ << extStats.cacheSize << " code " << extStats.codeSize;
#endif
atrace_pm_end();
@@ -2783,6 +2840,7 @@
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
// NOTE: Locking is relaxed on this method, since it's limited to
// read-only measurements without mutation.
@@ -2903,6 +2961,7 @@
const std::vector<std::string>& packageNames, int32_t userId,
std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
for (const auto& packageName : packageNames) {
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -2952,6 +3011,7 @@
const std::optional<std::string>& uuid, int32_t userId,
std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
#ifdef ENABLE_STORAGE_CRATES
LOCK_USER();
@@ -2995,6 +3055,7 @@
binder::Status InstalldNativeService::setAppQuota(const std::optional<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
std::lock_guard<std::recursive_mutex> lock(mQuotasLock);
@@ -3238,6 +3299,7 @@
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
LOCK_PACKAGE_USER();
@@ -3248,6 +3310,7 @@
const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
int32_t flags, int32_t appId, const std::string& seInfo) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -3279,6 +3342,7 @@
const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
int32_t flags, int32_t appId, const std::string& seInfo) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -3555,22 +3619,22 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
- if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+ if (fs_prepare_dir(mirrorVolCePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) {
return error("Failed to create CE data mirror");
}
std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
- if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+ if (fs_prepare_dir(mirrorVolDePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) {
return error("Failed to create DE data mirror");
}
std::string mirrorVolMiscCePath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
- if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+ if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) {
return error("Failed to create CE misc mirror");
}
std::string mirrorVolMiscDePath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
- if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+ if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) {
return error("Failed to create DE misc mirror");
}
@@ -3730,6 +3794,7 @@
int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath,
const std::optional<std::string>& dexMetadata, bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ ENFORCE_VALID_USER(userId);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(codePath);
LOCK_PACKAGE_USER();
@@ -3752,6 +3817,7 @@
binder::Status InstalldNativeService::cleanupInvalidPackageDirs(
const std::optional<std::string>& uuid, int32_t userId, int32_t flags) {
+ ENFORCE_VALID_USER(userId);
const char* uuid_cstr = uuid ? uuid->c_str() : nullptr;
if (flags & FLAG_STORAGE_CE) {
diff --git a/cmds/installd/SysTrace.h b/cmds/installd/SysTrace.h
index 18506a9..0deaeb4 100644
--- a/cmds/installd/SysTrace.h
+++ b/cmds/installd/SysTrace.h
@@ -19,4 +19,16 @@
namespace android::installd {
void atrace_pm_begin(const char*);
void atrace_pm_end();
+
+class ScopedTrace {
+public:
+ explicit ScopedTrace(const char* label) { atrace_pm_begin(label); }
+ ~ScopedTrace() { atrace_pm_end(); }
+
+private:
+ ScopedTrace(const ScopedTrace&) = delete;
+ ScopedTrace& operator=(const ScopedTrace&) = delete;
+ ScopedTrace(ScopedTrace&&) = delete;
+ ScopedTrace& operator=(ScopedTrace&&) = delete;
+};
} /* namespace android::installd */
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 1b7acab..c86993c 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -165,7 +165,8 @@
// Bind mount necessary directories.
constexpr const char* kBindMounts[] = {
- "/data", "/dev", "/proc", "/sys"
+ "/data", "/dev", "/proc", "/sys",
+ "/sys/fs/selinux" /* Required for apexd which includes libselinux */
};
for (size_t i = 0; i < arraysize(kBindMounts); ++i) {
std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]);
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index ecea1d2..c43fdbd 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -18,6 +18,7 @@
#ifndef UTILS_H_
#define UTILS_H_
+#include <functional>
#include <string>
#include <vector>
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index fb69513..d73a30b 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -93,22 +93,9 @@
libfuzzer_options: [
"max_len=50000",
],
- },
-}
-
-// Adding this new fuzzer to test the corpus generated by record_binder
-cc_fuzz {
- name: "servicemanager_test_fuzzer",
- defaults: [
- "servicemanager_defaults",
- "service_fuzzer_defaults",
- ],
- host_supported: true,
- srcs: ["fuzzers/ServiceManagerTestFuzzer.cpp"],
- fuzz_config: {
- libfuzzer_options: [
- "max_len=50000",
+ cc: [
+ "smoreland@google.com",
+ "waghpawan@google.com",
],
},
- corpus: ["fuzzers/servicemamanager_fuzzer_corpus/*"],
}
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index e30cbd5..4081514 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -291,6 +291,8 @@
service = &(it->second);
if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) {
+ LOG(WARNING) << "Isolated app with UID " << ctx.uid << " requested '" << name
+ << "', but the service is not allowed for isolated apps.";
return nullptr;
}
out = service->binder;
@@ -404,14 +406,13 @@
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
.hasClients = prevClients, // see b/279898063, matters if existing callbacks
- .guaranteeClient = false, // handled below
+ .guaranteeClient = false,
.ctx = ctx,
};
if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) {
- // TODO: this is only needed once
- // See also getService - handles case where client never gets the service,
- // we want the service to quit.
+ // If someone is currently waiting on the service, notify the service that
+ // we're waiting and flush it to the service.
mNameToService[name].guaranteeClient = true;
CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
mNameToService[name].guaranteeClient = true;
@@ -724,6 +725,11 @@
mNameToClientCallback[name].push_back(cb);
+ // Flush updated info to client callbacks (especially if guaranteeClient
+ // and !hasClient, see b/285202885). We may or may not have clients at
+ // this point, so ignore the return value.
+ (void)handleServiceClientCallback(2 /* sm + transaction */, name, false);
+
return Status::ok();
}
diff --git a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
deleted file mode 100644
index e19b6eb..0000000
--- a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fuzzbinder/libbinder_driver.h>
-#include <utils/StrongPointer.h>
-
-#include "Access.h"
-#include "ServiceManager.h"
-
-using ::android::Access;
-using ::android::Parcel;
-using ::android::ServiceManager;
-using ::android::sp;
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider provider(data, size);
- auto accessPtr = std::make_unique<Access>();
- auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr));
-
- // Reserved bytes
- provider.ConsumeBytes<uint8_t>(8);
- uint32_t code = provider.ConsumeIntegral<uint32_t>();
- uint32_t flag = provider.ConsumeIntegral<uint32_t>();
- std::vector<uint8_t> parcelData = provider.ConsumeRemainingBytes<uint8_t>();
-
- Parcel inputParcel;
- inputParcel.setData(parcelData.data(), parcelData.size());
-
- Parcel reply;
- serviceManager->transact(code, inputParcel, &reply, flag);
-
- serviceManager->clear();
-
- return 0;
-}
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
deleted file mode 100644
index 07319f8..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
deleted file mode 100644
index 07319f8..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
deleted file mode 100644
index 07319f8..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
deleted file mode 100644
index 07319f8..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
deleted file mode 100644
index 88ad474..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
deleted file mode 100644
index fae15a2..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
deleted file mode 100644
index e69ab49..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
deleted file mode 100644
index 88ad474..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
deleted file mode 100644
index fae15a2..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
deleted file mode 100644
index 88ad474..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
deleted file mode 100644
index fae15a2..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
deleted file mode 100644
index 88ad474..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
deleted file mode 100644
index fae15a2..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
deleted file mode 100644
index 88ad474..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
deleted file mode 100644
index fae15a2..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
deleted file mode 100644
index 88ad474..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
deleted file mode 100644
index fae15a2..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
deleted file mode 100644
index 88ad474..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
deleted file mode 100644
index fae15a2..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
deleted file mode 100644
index b326907..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
deleted file mode 100644
index 05b27bf..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
deleted file mode 100644
index b326907..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
deleted file mode 100644
index cdaa1f0..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
deleted file mode 100644
index ff0941b..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
deleted file mode 100644
index cdaa1f0..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
deleted file mode 100644
index 7e5f948..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
deleted file mode 100644
index 07319f8..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
deleted file mode 100644
index 07319f8..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
deleted file mode 100644
index 39e5104..0000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
+++ /dev/null
Binary files differ
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 754e7b2..3ce586b 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -263,6 +263,12 @@
}
prebuilt_etc {
+ name: "android.hardware.threadnetwork.prebuilt.xml",
+ src: "android.hardware.threadnetwork.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.usb.accessory.prebuilt.xml",
src: "android.hardware.usb.accessory.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -329,6 +335,18 @@
}
prebuilt_etc {
+ name: "android.software.opengles.deqp.level-2023-03-01.prebuilt.xml",
+ src: "android.software.opengles.deqp.level-2023-03-01.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.software.opengles.deqp.level-latest.prebuilt.xml",
+ src: "android.software.opengles.deqp.level-latest.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.software.sip.voip.prebuilt.xml",
src: "android.software.sip.voip.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -353,6 +371,18 @@
}
prebuilt_etc {
+ name: "android.software.vulkan.deqp.level-2023-03-01.prebuilt.xml",
+ src: "android.software.vulkan.deqp.level-2023-03-01.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.software.vulkan.deqp.level-latest.prebuilt.xml",
+ src: "android.software.vulkan.deqp.level-latest.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "aosp_excluded_hardware.prebuilt.xml",
src: "aosp_excluded_hardware.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.threadnetwork.xml b/data/etc/android.hardware.threadnetwork.xml
new file mode 100644
index 0000000..9cbdc90
--- /dev/null
+++ b/data/etc/android.hardware.threadnetwork.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- Adds the feature indicating support for the ThreadNetwork API -->
+<permissions>
+ <feature name="android.hardware.threadnetwork" />
+</permissions>
diff --git a/data/etc/android.software.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml
new file mode 100644
index 0000000..bd15eb6
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-latest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+ dEQP tests associated with the most recent level for this Android version. -->
+<permissions>
+ <feature name="android.software.opengles.deqp.level" version="132580097" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml
new file mode 100644
index 0000000..87be070
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-latest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan
+ dEQP tests associated with the most recent level for this Android version. -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132580097" />
+</permissions>
diff --git a/data/etc/input/Android.bp b/data/etc/input/Android.bp
index 90f3c6b..b662491 100644
--- a/data/etc/input/Android.bp
+++ b/data/etc/input/Android.bp
@@ -3,12 +3,21 @@
}
filegroup {
- name: "motion_predictor_model.fb",
- srcs: ["motion_predictor_model.fb"],
+ name: "motion_predictor_model",
+ srcs: [
+ "motion_predictor_model.tflite",
+ "motion_predictor_config.xml",
+ ],
}
prebuilt_etc {
name: "motion_predictor_model_prebuilt",
filename_from_src: true,
- src: "motion_predictor_model.fb",
+ src: "motion_predictor_model.tflite",
+}
+
+prebuilt_etc {
+ name: "motion_predictor_model_config",
+ filename_from_src: true,
+ src: "motion_predictor_config.xml",
}
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
new file mode 100644
index 0000000..39772ae
--- /dev/null
+++ b/data/etc/input/motion_predictor_config.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<motion-predictor>
+ <!-- The time interval (ns) between the model's predictions. -->
+ <prediction-interval>4166666</prediction-interval> <!-- 4.167 ms = ~240 Hz -->
+ <!-- The noise floor (px) for predicted distances.
+
+ As the model is trained stochastically, there is some expected minimum
+ variability in its output. This can be a UX issue when the input device
+ is moving slowly and the variability is large relative to the magnitude
+ of the motion. In these cases, it is better to inhibit the prediction,
+ rather than show noisy predictions (and there is little benefit to
+ prediction anyway).
+
+ The value for this parameter should at least be close to the maximum
+ predicted distance when the input device is held stationary (i.e. the
+ expected minimum variability), and perhaps a little larger to capture
+ the UX issue mentioned above.
+ -->
+ <distance-noise-floor>0.2</distance-noise-floor>
+</motion-predictor>
+
diff --git a/data/etc/input/motion_predictor_model.fb b/data/etc/input/motion_predictor_model.fb
deleted file mode 100644
index 10b3c8b..0000000
--- a/data/etc/input/motion_predictor_model.fb
+++ /dev/null
Binary files differ
diff --git a/data/etc/input/motion_predictor_model.tflite b/data/etc/input/motion_predictor_model.tflite
new file mode 100644
index 0000000..45fc162
--- /dev/null
+++ b/data/etc/input/motion_predictor_model.tflite
Binary files differ
diff --git a/include/android/input.h b/include/android/input.h
index 9a0eb4d..16d86af 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -54,16 +54,12 @@
#include <stdint.h>
#include <sys/types.h>
#include <android/keycodes.h>
-
-// This file is included by modules that have host support but android/looper.h is not supported
-// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
-#ifndef __BIONIC__
-#define __REMOVED_IN(x) __attribute__((deprecated))
-#endif
#include <android/looper.h>
#include <jni.h>
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// definitions are provided.
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
diff --git a/include/android/looper.h b/include/android/looper.h
index 4fe142a..e50730d 100644
--- a/include/android/looper.h
+++ b/include/android/looper.h
@@ -26,10 +26,18 @@
#ifndef ANDROID_LOOPER_H
#define ANDROID_LOOPER_H
+#include <sys/cdefs.h>
+
#ifdef __cplusplus
extern "C" {
#endif
+// This file may also be built on glibc or on Windows/MacOS libc's, so
+// deprecated definitions are provided.
+#if !defined(__REMOVED_IN)
+#define __REMOVED_IN(__api_level) __attribute__((__deprecated__))
+#endif
+
struct ALooper;
/**
* ALooper
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index b494f89..cedd361 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -171,6 +171,7 @@
* @return 0 on success.
* EINVAL if the list of thread ids is empty or if any of the thread ids is not part of the thread group.
* EPIPE if communication with the system service has failed.
+ * EPERM if any thread id doesn't belong to the application.
*/
int APerformanceHint_setThreads(
APerformanceHintSession* session,
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 16c5dde..a618393 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -29,6 +29,8 @@
#ifndef ANDROID_SENSOR_H
#define ANDROID_SENSOR_H
+#include <sys/cdefs.h>
+
/******************************************************************
*
* IMPORTANT NOTICE:
@@ -45,11 +47,6 @@
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
-// This file is included by modules that have host support but android/looper.h is not supported
-// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
-#ifndef __BIONIC__
-#define __REMOVED_IN(x) __attribute__((deprecated))
-#endif
#include <android/looper.h>
#include <stdbool.h>
@@ -57,6 +54,8 @@
#include <math.h>
#include <stdint.h>
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// and deprecated definitions are provided.
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
@@ -658,7 +657,7 @@
uint32_t flags;
int32_t reserved1[3];
} ASensorEvent;
-// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h)
+// LINT.ThenChange(hardware/libhardware/include/hardware/sensors.h)
struct ASensorManager;
/**
diff --git a/include/input/Input.h b/include/input/Input.h
index ea856c8..64ee473 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -493,14 +493,17 @@
toolType = ToolType::UNKNOWN;
}
- bool operator==(const PointerProperties& other) const;
+ bool operator==(const PointerProperties& other) const = default;
inline bool operator!=(const PointerProperties& other) const {
return !(*this == other);
}
- void copyFrom(const PointerProperties& other);
+ PointerProperties& operator=(const PointerProperties&) = default;
};
+// TODO(b/211379801) : Use a strong type from ftl/mixins.h instead
+using DeviceId = int32_t;
+
/*
* Input events.
*/
@@ -512,7 +515,7 @@
inline int32_t getId() const { return mId; }
- inline int32_t getDeviceId() const { return mDeviceId; }
+ inline DeviceId getDeviceId() const { return mDeviceId; }
inline uint32_t getSource() const { return mSource; }
@@ -527,13 +530,13 @@
static int32_t nextId();
protected:
- void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac);
void initialize(const InputEvent& from);
int32_t mId;
- int32_t mDeviceId;
+ DeviceId mDeviceId;
uint32_t mSource;
int32_t mDisplayId;
std::array<uint8_t, 32> mHmac;
@@ -548,7 +551,7 @@
public:
virtual ~KeyEvent() { }
- virtual InputEventType getType() const { return InputEventType::KEY; }
+ InputEventType getType() const override { return InputEventType::KEY; }
inline int32_t getAction() const { return mAction; }
@@ -571,7 +574,7 @@
static const char* getLabel(int32_t keyCode);
static std::optional<int> getKeyCodeFromLabel(const char* label);
- void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
nsecs_t eventTime);
@@ -599,7 +602,7 @@
public:
virtual ~MotionEvent() { }
- virtual InputEventType getType() const { return InputEventType::MOTION; }
+ InputEventType getType() const override { return InputEventType::MOTION; }
inline int32_t getAction() const { return mAction; }
@@ -835,7 +838,7 @@
ssize_t findPointerIndex(int32_t pointerId) const;
- void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
@@ -927,7 +930,7 @@
public:
virtual ~FocusEvent() {}
- virtual InputEventType getType() const override { return InputEventType::FOCUS; }
+ InputEventType getType() const override { return InputEventType::FOCUS; }
inline bool getHasFocus() const { return mHasFocus; }
@@ -946,7 +949,7 @@
public:
virtual ~CaptureEvent() {}
- virtual InputEventType getType() const override { return InputEventType::CAPTURE; }
+ InputEventType getType() const override { return InputEventType::CAPTURE; }
inline bool getPointerCaptureEnabled() const { return mPointerCaptureEnabled; }
@@ -965,7 +968,7 @@
public:
virtual ~DragEvent() {}
- virtual InputEventType getType() const override { return InputEventType::DRAG; }
+ InputEventType getType() const override { return InputEventType::DRAG; }
inline bool isExiting() const { return mIsExiting; }
@@ -989,7 +992,7 @@
public:
virtual ~TouchModeEvent() {}
- virtual InputEventType getType() const override { return InputEventType::TOUCH_MODE; }
+ InputEventType getType() const override { return InputEventType::TOUCH_MODE; }
inline bool isInTouchMode() const { return mIsInTouchMode; }
@@ -1013,7 +1016,7 @@
};
Type type;
- int32_t deviceId;
+ DeviceId deviceId;
nsecs_t eventTimeNanos;
uint32_t source;
int32_t displayId;
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
new file mode 100644
index 0000000..9c0c10e
--- /dev/null
+++ b/include/input/InputEventBuilders.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/input.h>
+#include <attestation/HmacKeyManager.h>
+#include <gui/constants.h>
+#include <input/Input.h>
+#include <utils/Timers.h> // for nsecs_t, systemTime
+
+#include <vector>
+
+namespace android {
+
+// An arbitrary device id.
+static constexpr uint32_t DEFAULT_DEVICE_ID = 1;
+
+// The default policy flags to use for event injection by tests.
+static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+
+class PointerBuilder {
+public:
+ PointerBuilder(int32_t id, ToolType toolType) {
+ mProperties.clear();
+ mProperties.id = id;
+ mProperties.toolType = toolType;
+ mCoords.clear();
+ }
+
+ PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
+
+ PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+
+ PointerBuilder& axis(int32_t axis, float value) {
+ mCoords.setAxisValue(axis, value);
+ return *this;
+ }
+
+ PointerProperties buildProperties() const { return mProperties; }
+
+ PointerCoords buildCoords() const { return mCoords; }
+
+private:
+ PointerProperties mProperties;
+ PointerCoords mCoords;
+};
+
+class MotionEventBuilder {
+public:
+ MotionEventBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mDownTime = mEventTime;
+ }
+
+ MotionEventBuilder& deviceId(int32_t deviceId) {
+ mDeviceId = deviceId;
+ return *this;
+ }
+
+ MotionEventBuilder& downTime(nsecs_t downTime) {
+ mDownTime = downTime;
+ return *this;
+ }
+
+ MotionEventBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ MotionEventBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ MotionEventBuilder& actionButton(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionEventBuilder& buttonState(int32_t buttonState) {
+ mButtonState = buttonState;
+ return *this;
+ }
+
+ MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
+ mRawXCursorPosition = rawXCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
+ mRawYCursorPosition = rawYCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& pointer(PointerBuilder pointer) {
+ mPointers.push_back(pointer);
+ return *this;
+ }
+
+ MotionEventBuilder& addFlag(uint32_t flags) {
+ mFlags |= flags;
+ return *this;
+ }
+
+ MotionEvent build() {
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+ for (const PointerBuilder& pointer : mPointers) {
+ pointerProperties.push_back(pointer.buildProperties());
+ pointerCoords.push_back(pointer.buildCoords());
+ }
+
+ // Set mouse cursor position for the most common cases to avoid boilerplate.
+ if (mSource == AINPUT_SOURCE_MOUSE &&
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
+ mRawXCursorPosition = pointerCoords[0].getX();
+ mRawYCursorPosition = pointerCoords[0].getY();
+ }
+
+ MotionEvent event;
+ static const ui::Transform kIdentityTransform;
+ event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
+ mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
+ MotionClassification::NONE, kIdentityTransform,
+ /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
+ mRawYCursorPosition, kIdentityTransform, mDownTime, mEventTime,
+ mPointers.size(), pointerProperties.data(), pointerCoords.data());
+ return event;
+ }
+
+private:
+ int32_t mAction;
+ int32_t mDeviceId{DEFAULT_DEVICE_ID};
+ int32_t mSource;
+ nsecs_t mDownTime;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ int32_t mActionButton{0};
+ int32_t mButtonState{0};
+ int32_t mFlags{0};
+ float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+
+ std::vector<PointerBuilder> mPointers;
+};
+
+} // namespace android
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 9dedd2b..44247c1 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -40,7 +40,16 @@
// then you must add it to InputEventLabels.cpp.
class InputEventLookup {
+ /**
+ * This class is not purely static, but uses a singleton pattern in order to delay the
+ * initialization of the maps that it contains. If it were purely static, the maps could be
+ * created early, and would cause sanitizers to report memory leaks.
+ */
public:
+ InputEventLookup(InputEventLookup& other) = delete;
+
+ void operator=(const InputEventLookup&) = delete;
+
static std::optional<int> lookupValueByLabel(const std::unordered_map<std::string, int>& map,
const char* literal);
@@ -60,18 +69,31 @@
static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value);
+ static std::optional<int> getLinuxEvdevEventTypeByLabel(const char* label);
+
+ static std::optional<int> getLinuxEvdevEventCodeByLabel(int32_t type, const char* label);
+
+ static std::optional<int> getLinuxEvdevInputPropByLabel(const char* label);
+
private:
- static const std::unordered_map<std::string, int> KEYCODES;
+ InputEventLookup();
- static const std::vector<InputEventLabel> KEY_NAMES;
+ static const InputEventLookup& get() {
+ static InputEventLookup sLookup;
+ return sLookup;
+ }
- static const std::unordered_map<std::string, int> AXES;
+ const std::unordered_map<std::string, int> KEYCODES;
- static const std::vector<InputEventLabel> AXES_NAMES;
+ const std::vector<InputEventLabel> KEY_NAMES;
- static const std::unordered_map<std::string, int> LEDS;
+ const std::unordered_map<std::string, int> AXES;
- static const std::unordered_map<std::string, int> FLAGS;
+ const std::vector<InputEventLabel> AXES_NAMES;
+
+ const std::unordered_map<std::string, int> LEDS;
+
+ const std::unordered_map<std::string, int> FLAGS;
};
} // namespace android
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
index d4589f5..b857482 100644
--- a/include/input/InputVerifier.h
+++ b/include/input/InputVerifier.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,13 +16,25 @@
#pragma once
+#include <android-base/result.h>
#include <input/Input.h>
-#include <map>
+#include "rust/cxx.h"
namespace android {
+namespace input {
+namespace verifier {
+struct InputVerifier;
+}
+} // namespace input
+
/*
* Crash if the provided touch stream is inconsistent.
+ * This class is a pass-through to the rust implementation of InputVerifier.
+ * The rust class could also be used directly, but it would be less convenient.
+ * We can't directly invoke the rust methods on a rust object. So, there's no way to do:
+ * mVerifier.process_movement(...).
+ * This C++ class makes it a bit easier to use.
*
* TODO(b/211379801): Add support for hover events:
* - No hover move without enter
@@ -34,16 +46,15 @@
public:
InputVerifier(const std::string& name);
- void processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords, int32_t flags);
+ android::base::Result<void> processMovement(int32_t deviceId, int32_t action,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, int32_t flags);
+
+ void resetDevice(int32_t deviceId);
private:
- const std::string mName;
- std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> mTouchingPointerIdsByDevice;
- void ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const char* action) const;
+ rust::Box<android::input::verifier::InputVerifier> mVerifier;
};
} // namespace android
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 8c3c74a..b126abe 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -17,13 +17,13 @@
#pragma once
#include <android-base/result.h>
+#include <input/InputDevice.h>
+
#include <stdint.h>
#include <utils/Errors.h>
#include <utils/Tokenizer.h>
#include <set>
-#include <input/InputDevice.h>
-
namespace android {
struct AxisInfo {
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index de8ddca..8797962 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -26,7 +26,9 @@
#include <android-base/thread_annotations.h>
#include <android/sysprop/InputProperties.sysprop.h>
#include <input/Input.h>
+#include <input/MotionPredictorMetricsManager.h>
#include <input/TfLiteMotionPredictor.h>
+#include <utils/Timers.h> // for nsecs_t
namespace android {
@@ -69,6 +71,7 @@
*/
MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled);
+
/**
* Record the actual motion received by the view. This event will be used for calculating the
* predictions.
@@ -77,7 +80,9 @@
* consistent with the previously recorded events.
*/
android::base::Result<void> record(const MotionEvent& event);
+
std::unique_ptr<MotionEvent> predict(nsecs_t timestamp);
+
bool isPredictionAvailable(int32_t deviceId, int32_t source);
private:
@@ -88,6 +93,8 @@
std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
std::optional<MotionEvent> mLastEvent;
+
+ std::optional<MotionPredictorMetricsManager> mMetricsManager;
};
} // namespace android
diff --git a/include/input/MotionPredictorMetricsManager.h b/include/input/MotionPredictorMetricsManager.h
new file mode 100644
index 0000000..6284f07
--- /dev/null
+++ b/include/input/MotionPredictorMetricsManager.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Class to handle computing and reporting metrics for MotionPredictor.
+ *
+ * Currently an empty implementation, containing only the API.
+ */
+class MotionPredictorMetricsManager {
+public:
+ // Note: the MetricsManager assumes that the input interval equals the prediction interval.
+ MotionPredictorMetricsManager(nsecs_t /*predictionInterval*/, size_t /*maxNumPredictions*/) {}
+
+ void onRecord(const MotionEvent& /*inputEvent*/) {}
+
+ void onPredict(const MotionEvent& /*predictionEvent*/) {}
+};
+
+} // namespace android
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 0ca6fa3..0e3fbb1 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -27,6 +27,9 @@
template <size_t N>
std::string bitsetToString(const std::bitset<N>& bitset) {
+ if (bitset.none()) {
+ return "<none>";
+ }
return bitset.to_string();
}
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index a340bd0..2edc138 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -25,6 +25,7 @@
#include <android-base/mapped_file.h>
#include <input/RingBuffer.h>
+#include <utils/Timers.h>
#include <tensorflow/lite/core/api/error_reporter.h>
#include <tensorflow/lite/interpreter.h>
@@ -98,6 +99,14 @@
// A TFLite model for generating motion predictions.
class TfLiteMotionPredictorModel {
public:
+ struct Config {
+ // The time between predictions.
+ nsecs_t predictionInterval = 0;
+ // The noise floor for predictions.
+ // Distances (r) less than this should be discarded as noise.
+ float distanceNoiseFloor = 0;
+ };
+
// Creates a model from an encoded Flatbuffer model.
static std::unique_ptr<TfLiteMotionPredictorModel> create();
@@ -109,6 +118,8 @@
// Returns the length of the model's output buffers.
size_t outputLength() const;
+ const Config& config() const { return mConfig; }
+
// Executes the model.
// Returns true if the model successfully executed and the output tensors can be read.
bool invoke();
@@ -127,7 +138,8 @@
std::span<const float> outputPressure() const;
private:
- explicit TfLiteMotionPredictorModel(std::unique_ptr<android::base::MappedFile> model);
+ explicit TfLiteMotionPredictorModel(std::unique_ptr<android::base::MappedFile> model,
+ Config config);
void allocateTensors();
void attachInputTensors();
@@ -148,6 +160,8 @@
std::unique_ptr<tflite::FlatBufferModel> mModel;
std::unique_ptr<tflite::Interpreter> mInterpreter;
tflite::SignatureRunner* mRunner = nullptr;
+
+ const Config mConfig = {};
};
} // namespace android
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index 71a36d0..9e426d3 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -17,11 +17,11 @@
#ifndef ANDROID_POWERHALCONTROLLER_H
#define ANDROID_POWERHALCONTROLLER_H
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android-base/thread_annotations.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
#include <powermanager/PowerHalWrapper.h>
namespace android {
@@ -53,13 +53,15 @@
: mHalConnector(std::move(connector)) {}
virtual ~PowerHalController() = default;
- void init();
+ virtual void init();
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
- int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) override;
+ virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode,
+ bool enabled) override;
+ virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+ createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
virtual HalResult<int64_t> getHintSessionPreferredRate() override;
private:
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index e0384f3..cbbfa59 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -17,11 +17,11 @@
#ifndef ANDROID_POWERHALLOADER_H
#define ANDROID_POWERHALLOADER_H
+#include <aidl/android/hardware/power/IPower.h>
#include <android-base/thread_annotations.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/IPower.h>
namespace android {
@@ -31,7 +31,7 @@
class PowerHalLoader {
public:
static void unloadAll();
- static sp<hardware::power::IPower> loadAidl();
+ static std::shared_ptr<aidl::android::hardware::power::IPower> loadAidl();
static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
@@ -39,7 +39,7 @@
private:
static std::mutex gHalMutex;
- static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
+ static std::shared_ptr<aidl::android::hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_2::IPower> gHalHidlV1_2 GUARDED_BY(gHalMutex);
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 8028aa8..4e4a1b0 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -17,14 +17,15 @@
#ifndef ANDROID_POWERHALWRAPPER_H
#define ANDROID_POWERHALWRAPPER_H
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android-base/thread_annotations.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <binder/Status.h>
namespace android {
@@ -47,7 +48,7 @@
}
static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
- static HalResult<T> fromStatus(binder::Status status, T data) {
+ static HalResult<T> fromStatus(const binder::Status& status, T data) {
if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
return HalResult<T>::unsupported();
}
@@ -56,14 +57,28 @@
}
return HalResult<T>::failed(std::string(status.toString8().c_str()));
}
- static HalResult<T> fromStatus(hardware::power::V1_0::Status status, T data);
+
+ static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T data) {
+ if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(data);
+ }
+ return HalResult<T>::failed(std::string(status.getDescription()));
+ }
template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
+ }
template <typename R>
static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
- T data);
+ T data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+ : HalResult<T>::failed(ret.description());
+ }
// This will throw std::bad_optional_access if this result is not ok.
const T& value() const { return mValue.value(); }
@@ -91,12 +106,30 @@
static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
- static HalResult<void> fromStatus(status_t status);
- static HalResult<void> fromStatus(binder::Status status);
- static HalResult<void> fromStatus(hardware::power::V1_0::Status status);
+ static HalResult<void> fromStatus(const binder::Status& status) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+ }
+
+ static HalResult<void> fromStatus(const ndk::ScopedAStatus& status) {
+ if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.getDescription()));
+ }
template <typename R>
- static HalResult<void> fromReturn(hardware::Return<R>& ret);
+ static HalResult<void> fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+ }
bool isOk() const { return !mUnsupported && !mFailed; }
bool isFailed() const { return !mUnsupported && mFailed; }
@@ -119,11 +152,12 @@
public:
virtual ~HalWrapper() = default;
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) = 0;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
- int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) = 0;
+ virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) = 0;
+ virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0;
+ virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+ createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) = 0;
virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
};
@@ -131,14 +165,15 @@
class EmptyHalWrapper : public HalWrapper {
public:
EmptyHalWrapper() = default;
- ~EmptyHalWrapper() = default;
+ ~EmptyHalWrapper() override = default;
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+ HalResult<int64_t> getHintSessionPreferredRate() override;
};
// Wrapper for the HIDL Power HAL v1.0.
@@ -146,14 +181,15 @@
public:
explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0)
: mHandleV1_0(std::move(handleV1_0)) {}
- virtual ~HidlHalWrapperV1_0() = default;
+ ~HidlHalWrapperV1_0() override = default;
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+ HalResult<int64_t> getHintSessionPreferredRate() override;
protected:
const sp<hardware::power::V1_0::IPower> mHandleV1_0;
@@ -167,67 +203,71 @@
// Wrapper for the HIDL Power HAL v1.1.
class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
public:
- HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1)
+ explicit HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1)
: HidlHalWrapperV1_0(std::move(handleV1_1)) {}
- virtual ~HidlHalWrapperV1_1() = default;
+ ~HidlHalWrapperV1_1() override = default;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
- uint32_t data) override;
+ HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
};
// Wrapper for the HIDL Power HAL v1.2.
class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
public:
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
+ HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ explicit HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
: HidlHalWrapperV1_1(std::move(handleV1_2)) {}
- virtual ~HidlHalWrapperV1_2() = default;
+ ~HidlHalWrapperV1_2() override = default;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
- uint32_t data) override;
+ HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
};
// Wrapper for the HIDL Power HAL v1.3.
class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
public:
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ explicit HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
: HidlHalWrapperV1_2(std::move(handleV1_3)) {}
- virtual ~HidlHalWrapperV1_3() = default;
+ ~HidlHalWrapperV1_3() override = default;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
- uint32_t data) override;
+ HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
};
// Wrapper for the AIDL Power HAL.
class AidlHalWrapper : public HalWrapper {
public:
- explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
- virtual ~AidlHalWrapper() = default;
+ explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle)
+ : mHandle(std::move(handle)) {}
+ ~AidlHalWrapper() override = default;
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+ HalResult<int64_t> getHintSessionPreferredRate() override;
private:
// Control access to the boost and mode supported arrays.
std::mutex mBoostMutex;
std::mutex mModeMutex;
- sp<hardware::power::IPower> mHandle;
+ std::shared_ptr<aidl::android::hardware::power::IPower> mHandle;
// Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
// Need to increase the array size if more boost supported.
- std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+ std::array<
+ std::atomic<HalSupport>,
+ static_cast<int32_t>(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) +
+ 1>
mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(*(android::enum_range<hardware::power::Mode>().end() - 1)) + 1>
+ static_cast<int32_t>(
+ *(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) +
+ 1>
mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
};
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index aca5009..5264276 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -21,6 +21,7 @@
#include <binder/ActivityManager.h>
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <utils/SystemClock.h>
@@ -33,27 +34,36 @@
sp<IActivityManager> ActivityManager::getService()
{
std::lock_guard<Mutex> scoped_lock(mLock);
- int64_t startTime = 0;
sp<IActivityManager> service = mService;
- while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
- sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity"));
- if (binder == nullptr) {
- // Wait for the activity service to come back...
- if (startTime == 0) {
- startTime = uptimeMillis();
- ALOGI("Waiting for activity service");
- } else if ((uptimeMillis() - startTime) > 1000000) {
- ALOGW("Waiting too long for activity service, giving up");
- service = nullptr;
- break;
- }
- usleep(25000);
- } else {
+ if (ProcessState::self()->isThreadPoolStarted()) {
+ if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->waitForService(String16("activity"));
service = interface_cast<IActivityManager>(binder);
mService = service;
}
+ } else {
+ ALOGI("Thread pool not started. Polling for activity service.");
+ int64_t startTime = 0;
+ while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity"));
+ if (binder == nullptr) {
+ // Wait for the activity service to come back...
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ ALOGI("Waiting for activity service");
+ } else if ((uptimeMillis() - startTime) > 1000000) {
+ ALOGW("Waiting too long for activity service, giving up");
+ service = nullptr;
+ break;
+ }
+ usleep(25000);
+ } else {
+ service = interface_cast<IActivityManager>(binder);
+ mService = service;
+ }
+ }
}
- return service;
+ return mService;
}
int ActivityManager::openContentUri(const String16& stringUri)
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 1dff38c..3f1fc33 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -519,7 +519,6 @@
"libbase",
"libbinder",
"libbinder_ndk",
- "libcutils_sockets",
"liblog",
"libutils",
],
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 0aca163..bbaa419 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -69,6 +69,10 @@
typedef uintptr_t binder_uintptr_t;
#endif // BINDER_WITH_KERNEL_IPC
+#ifdef __BIONIC__
+#include <android/fdsan.h>
+#endif
+
#define LOG_REFS(...)
// #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOG_ALLOC(...)
@@ -109,6 +113,37 @@
// Maximum size of a blob to transfer in-place.
static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
+#if defined(__BIONIC__)
+static void FdTag(int fd, const void* old_addr, const void* new_addr) {
+ if (android_fdsan_exchange_owner_tag) {
+ uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+ reinterpret_cast<uint64_t>(old_addr));
+ uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+ reinterpret_cast<uint64_t>(new_addr));
+ android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
+ }
+}
+static void FdTagClose(int fd, const void* addr) {
+ if (android_fdsan_close_with_tag) {
+ uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+ reinterpret_cast<uint64_t>(addr));
+ android_fdsan_close_with_tag(fd, tag);
+ } else {
+ close(fd);
+ }
+}
+#else
+static void FdTag(int fd, const void* old_addr, const void* new_addr) {
+ (void)fd;
+ (void)old_addr;
+ (void)new_addr;
+}
+static void FdTagClose(int fd, const void* addr) {
+ (void)addr;
+ close(fd);
+}
+#endif
+
enum {
BLOB_INPLACE = 0,
BLOB_ASHMEM_IMMUTABLE = 1,
@@ -134,6 +169,9 @@
return;
}
case BINDER_TYPE_FD: {
+ if (obj.cookie != 0) { // owned
+ FdTag(obj.handle, nullptr, who);
+ }
return;
}
}
@@ -159,8 +197,10 @@
return;
}
case BINDER_TYPE_FD: {
+ // note: this path is not used when mOwner, so the tag is also released
+ // in 'closeFileDescriptors'
if (obj.cookie != 0) { // owned
- close(obj.handle);
+ FdTagClose(obj.handle, who);
}
return;
}
@@ -554,7 +594,6 @@
kernelFields->mObjectsSize++;
flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off);
- acquire_object(proc, *flat, this);
if (flat->hdr.type == BINDER_TYPE_FD) {
// If this is a file descriptor, we need to dup it so the
@@ -567,6 +606,8 @@
err = FDS_NOT_ALLOWED;
}
}
+
+ acquire_object(proc, *flat, this);
}
}
#else
@@ -947,7 +988,10 @@
threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
// vendor header
int32_t header = readInt32();
- if (header != kHeader) {
+
+ // fuzzers skip this check, because it is for protecting the underlying ABI, but
+ // we don't want it to reduce our coverage
+ if (header != kHeader && !mServiceFuzzing) {
ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader,
header);
return false;
@@ -966,10 +1010,18 @@
(!len || !memcmp(parcel_interface, interface, len * sizeof (char16_t)))) {
return true;
} else {
- ALOGW("**** enforceInterface() expected '%s' but read '%s'",
- String8(interface, len).string(),
- String8(parcel_interface, parcel_interface_len).string());
- return false;
+ if (mServiceFuzzing) {
+ // ignore. Theoretically, this could cause a few false positives, because
+ // people could assume things about getInterfaceDescriptor if they pass
+ // this point, but it would be extremely fragile. It's more important that
+ // we fuzz with the above things read from the Parcel.
+ return true;
+ } else {
+ ALOGW("**** enforceInterface() expected '%s' but read '%s'",
+ String8(interface, len).string(),
+ String8(parcel_interface, parcel_interface_len).string());
+ return false;
+ }
}
}
@@ -977,6 +1029,14 @@
mEnforceNoDataAvail = enforceNoDataAvail;
}
+void Parcel::setServiceFuzzing() {
+ mServiceFuzzing = true;
+}
+
+bool Parcel::isServiceFuzzing() const {
+ return mServiceFuzzing;
+}
+
binder::Status Parcel::enforceNoDataAvail() const {
if (!mEnforceNoDataAvail) {
return binder::Status::ok();
@@ -1722,7 +1782,9 @@
do {
if (mDataPos < kernelFields->mObjects[nextObject] + sizeof(flat_binder_object)) {
// Requested info overlaps with an object
- ALOGE("Attempt to read from protected data in Parcel %p", this);
+ if (!mServiceFuzzing) {
+ ALOGE("Attempt to read from protected data in Parcel %p", this);
+ }
return PERMISSION_DENIED;
}
nextObject++;
@@ -2092,7 +2154,11 @@
size_t len;
const char* str = readString8Inplace(&len);
if (str) return String8(str, len);
- ALOGE("Reading a NULL string not supported here.");
+
+ if (!mServiceFuzzing) {
+ ALOGE("Reading a NULL string not supported here.");
+ }
+
return String8();
}
@@ -2132,7 +2198,11 @@
size_t len;
const char16_t* str = readString16Inplace(&len);
if (str) return String16(str, len);
- ALOGE("Reading a NULL string not supported here.");
+
+ if (!mServiceFuzzing) {
+ ALOGE("Reading a NULL string not supported here.");
+ }
+
return String16();
}
@@ -2172,7 +2242,9 @@
{
status_t status = readNullableStrongBinder(val);
if (status == OK && !val->get()) {
- ALOGW("Expecting binder but got null!");
+ if (!mServiceFuzzing) {
+ ALOGW("Expecting binder but got null!");
+ }
status = UNEXPECTED_NULL;
}
return status;
@@ -2237,9 +2309,11 @@
if (const auto* rpcFields = maybeRpcFields()) {
if (!std::binary_search(rpcFields->mObjectPositions.begin(),
rpcFields->mObjectPositions.end(), mDataPos)) {
- ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in the "
- "object list",
- this, mDataPos);
+ if (!mServiceFuzzing) {
+ ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in "
+ "the object list",
+ this, mDataPos);
+ }
return BAD_TYPE;
}
@@ -2497,8 +2571,11 @@
return obj;
}
}
- ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object list",
- this, DPOS);
+ if (!mServiceFuzzing) {
+ ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object "
+ "list",
+ this, DPOS);
+ }
}
return nullptr;
}
@@ -2517,7 +2594,8 @@
reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
if (flat->hdr.type == BINDER_TYPE_FD) {
// ALOGI("Closing fd: %ld", flat->handle);
- close(flat->handle);
+ // FDs from the kernel are always owned
+ FdTagClose(flat->handle, this);
}
}
#else // BINDER_WITH_KERNEL_IPC
@@ -2598,6 +2676,10 @@
kernelFields->mObjectsSize = 0;
break;
}
+ if (type == BINDER_TYPE_FD) {
+ // FDs from the kernel are always owned
+ FdTag(flat->handle, 0, this);
+ }
minOffset = offset + sizeof(flat_binder_object);
}
scanForFds();
@@ -3093,6 +3175,7 @@
mDeallocZero = false;
mOwner = nullptr;
mEnforceNoDataAvail = true;
+ mServiceFuzzing = false;
}
void Parcel::scanForFds() const {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 3fa6867..02b0447 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -192,6 +192,7 @@
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
if (mMaxThreads == 0) {
+ // see also getThreadPoolMaxTotalThreadCount
ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
"*startThreadPool when zero threads are requested.");
}
@@ -407,6 +408,11 @@
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
+ // binder threads, and additional won't be started. This is likely to
+ // cause deadlocks, and it will also cause getThreadPoolMaxTotalThreadCount
+ // to return too high of a value.
}
status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) {
@@ -426,12 +432,32 @@
pthread_mutex_lock(&mThreadCountLock);
base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); };
- // may actually be one more than this, if join is called
if (mThreadPoolStarted) {
- return mCurrentThreads < mKernelStartedThreads
- ? mMaxThreads
- : mMaxThreads + mCurrentThreads - mKernelStartedThreads;
+ LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1,
+ "too many kernel-started threads: %zu > %zu + 1", mKernelStartedThreads,
+ mMaxThreads);
+
+ // calling startThreadPool starts a thread
+ size_t threads = 1;
+
+ // the kernel is configured to start up to mMaxThreads more threads
+ threads += mMaxThreads;
+
+ // Users may call IPCThreadState::joinThreadPool directly. We don't
+ // currently have a way to count this directly (it could be added by
+ // adding a separate private joinKernelThread method in IPCThreadState).
+ // So, if we are in a race between the kernel thread variable being
+ // incremented in this file and mCurrentThreads being incremented
+ // 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;
+ }
+
+ return threads;
}
+
// must not be initialized or maybe has poll thread setup, we
// currently don't track this in libbinder
LOG_ALWAYS_FATAL_IF(mKernelStartedThreads != 0,
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index ff35f5f..bac2808 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -34,6 +34,10 @@
#include <inttypes.h>
+#ifdef __ANDROID__
+#include <cutils/properties.h>
+#endif
+
namespace android {
using base::StringPrintf;
@@ -59,6 +63,7 @@
case RpcSession::FileDescriptorTransportMode::TRUSTY:
return true;
}
+ LOG_ALWAYS_FATAL("Invalid FileDescriptorTransportMode: %d", static_cast<int>(mode));
}
RpcState::RpcState() {}
@@ -399,14 +404,27 @@
}
bool RpcState::validateProtocolVersion(uint32_t version) {
- if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
- version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+ if (version == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+#if defined(__ANDROID__)
+ char codename[PROPERTY_VALUE_MAX];
+ property_get("ro.build.version.codename", codename, "");
+ if (!strcmp(codename, "REL")) {
+ ALOGE("Cannot use experimental RPC binder protocol on a release branch.");
+ return false;
+ }
+#else
+ // don't restrict on other platforms, though experimental should
+ // only really be used for testing, we don't have a good way to see
+ // what is shipping outside of Android
+#endif
+ } else if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT) {
ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol "
"version "
"is %u).",
version, RPC_WIRE_PROTOCOL_VERSION);
return false;
}
+
return true;
}
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 199574e..2b3ff44 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -64,6 +64,9 @@
"name": "libbinderthreadstateutils_test"
},
{
+ "name": "fuzz_service_test"
+ },
+ {
"name": "CtsOsTestCases",
"options": [
{
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index d261c21..9347ce4 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -147,7 +147,12 @@
void flushCommands();
bool flushIfNeeded();
- // For main functions - dangerous for libraries to use
+ // Adds the current thread into the binder threadpool.
+ //
+ // This is in addition to any threads which are started
+ // with startThreadPool. Libraries should not call this
+ // function, as they may be loaded into processes which
+ // try to configure the threadpool differently.
void joinThreadPool(bool isMain = true);
// Stop the local process.
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index e28d374..4e231ed 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -149,6 +149,11 @@
// This Api is used by fuzzers to skip dataAvail checks.
void setEnforceNoDataAvail(bool enforceNoDataAvail);
+ // When fuzzing, we want to remove certain ABI checks that cause significant
+ // lost coverage, and we also want to avoid logs that cost too much to write.
+ void setServiceFuzzing();
+ bool isServiceFuzzing() const;
+
void freeData();
size_t objectsCount() const;
@@ -261,7 +266,8 @@
status_t writeEnumVector(const std::optional<std::vector<T>>& val)
{ return writeData(val); }
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
- status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")))
+ [[deprecated("use std::optional version instead")]] //
+ status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val)
{ return writeData(val); }
// Write an Enum vector with underlying type != int8_t.
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
@@ -271,17 +277,20 @@
status_t writeEnumVector(const std::optional<std::vector<T>>& val)
{ return writeData(val); }
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
- status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")))
+ [[deprecated("use std::optional version instead")]] //
+ status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val)
{ return writeData(val); }
template<typename T>
status_t writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val)
{ return writeData(val); }
template<typename T>
- status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")))
+ [[deprecated("use std::optional version instead")]] //
+ status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val)
{ return writeData(val); }
template<typename T>
- status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")))
+ [[deprecated("use std::optional version instead")]] //
+ status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val)
{ return writeData(val); }
template<typename T>
status_t writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val)
@@ -417,7 +426,8 @@
status_t readEnumVector(std::vector<T>* val) const
{ return readData(val); }
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
- status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")))
+ [[deprecated("use std::optional version instead")]] //
+ status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const
{ return readData(val); }
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::optional<std::vector<T>>* val) const
@@ -427,7 +437,8 @@
status_t readEnumVector(std::vector<T>* val) const
{ return readData(val); }
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
- status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")))
+ [[deprecated("use std::optional version instead")]] //
+ status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const
{ return readData(val); }
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::optional<std::vector<T>>* val) const
@@ -438,8 +449,9 @@
std::optional<std::vector<std::optional<T>>>* val) const
{ return readData(val); }
template<typename T>
+ [[deprecated("use std::optional version instead")]] //
status_t readParcelableVector(
- std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead")))
+ std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const
{ return readData(val); }
template<typename T>
status_t readParcelableVector(std::vector<T>* val) const
@@ -1330,6 +1342,7 @@
// Set this to false to skip dataAvail checks.
bool mEnforceNoDataAvail;
+ bool mServiceFuzzing;
release_func mOwner;
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 81391e9..9dc370b 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -52,7 +52,26 @@
sp<IBinder> getContextObject(const sp<IBinder>& caller);
- // For main functions - dangerous for libraries to use
+ // This should be called before startThreadPool at the beginning
+ // of a program, and libraries should never call it because programs
+ // should configure their own threadpools. The threadpool size can
+ // never be decreased.
+ //
+ // The 'maxThreads' value refers to the total number of threads
+ // that will be started by the kernel. This is in addition to any
+ // threads started by 'startThreadPool' or 'joinRpcThreadpool'.
+ status_t setThreadPoolMaxThreadCount(size_t maxThreads);
+
+ // Libraries should not call this, as processes should configure
+ // threadpools themselves. Should be called in the main function
+ // directly before any code executes or joins the threadpool.
+ //
+ // Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount,
+ // PLUS those manually requested in joinThreadPool.
+ //
+ // For instance, if setThreadPoolMaxCount(3) is called and
+ // startThreadpPool (+1 thread) and joinThreadPool (+1 thread)
+ // are all called, then up to 5 threads can be started.
void startThreadPool();
[[nodiscard]] bool becomeContextManager();
@@ -63,8 +82,6 @@
// TODO: deprecate.
void spawnPooledThread(bool isMain);
- // For main functions - dangerous for libraries to use
- status_t setThreadPoolMaxThreadCount(size_t maxThreads);
status_t enableOnewaySpamDetection(bool enable);
// Set the name of the current thread to look like a threadpool
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index a157792..7d0acd1 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -40,12 +40,13 @@
[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid,
unsigned int port);
-// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
+// Starts a Unix domain RPC server with an open raw socket file descriptor
// and a given root IBinder object.
-// The socket should be created in init.rc with the same `name`.
+// The socket should be created and bound to an address.
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
-[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name);
+// The socket will be closed by the server once the server goes out of scope.
+[[nodiscard]] ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd);
// Starts an RPC server that bootstraps sessions using an existing Unix domain
// socket pair, with a given root IBinder object.
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index a167f23..f51cd9b 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -105,22 +105,15 @@
return createObjectHandle<ARpcServer>(server);
}
-ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) {
+ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd) {
auto server = RpcServer::make();
- auto fd = unique_fd(android_get_control_socket(name));
+ auto fd = unique_fd(socketFd);
if (!fd.ok()) {
- LOG(ERROR) << "Failed to get fd for the socket:" << name;
+ LOG(ERROR) << "Invalid socket fd " << socketFd;
return nullptr;
}
- // Control socket fds are inherited from init, so they don't have O_CLOEXEC set.
- // But we don't want any child processes to inherit the socket we are running
- // the server on, so attempt to set the flag now.
- if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
- LOG(WARNING) << "Failed to set CLOEXEC on control socket with name " << name
- << " error: " << errno;
- }
if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
- LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
+ LOG(ERROR) << "Failed to set up RPC server with fd " << socketFd
<< " error: " << statusToString(status).c_str();
return nullptr;
}
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index 63679c2..50f7deb 100644
--- a/libs/binder/libbinder_rpc_unstable.map.txt
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -3,7 +3,7 @@
ARpcServer_free;
ARpcServer_join;
ARpcServer_newInet;
- ARpcServer_newInitUnixDomain;
+ ARpcServer_newBoundSocket;
ARpcServer_newVsock;
ARpcServer_shutdown;
ARpcServer_start;
diff --git a/libs/binder/ndk/.clang-format b/libs/binder/ndk/.clang-format
index 9a9d936..6077414 100644
--- a/libs/binder/ndk/.clang-format
+++ b/libs/binder/ndk/.clang-format
@@ -2,9 +2,7 @@
ColumnLimit: 100
IndentWidth: 4
ContinuationIndentWidth: 8
-PointerAlignment: Left
TabWidth: 4
AllowShortFunctionsOnASingleLine: Inline
PointerAlignment: Left
-TabWidth: 4
UseTab: Never
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index d0de7b9..f7dd9c9 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -137,7 +137,7 @@
// since it's an error condition. Do the comparison after we take the lock and
// check the pointer equality fast path. By always taking the lock, it's also
// more flake-proof. However, the check is not dependent on the lock.
- if (descriptor != newDescriptor) {
+ if (descriptor != newDescriptor && !(asABpBinder() && asABpBinder()->isServiceFuzzing())) {
if (getBinder()->isBinderAlive()) {
LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
<< "' but descriptor is actually '" << SanitizeString(descriptor) << "'.";
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 67bb092..9d5368f 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -104,10 +104,14 @@
::android::sp<::android::IBinder> getBinder() override { return mRemote; }
ABpBinder* asABpBinder() override { return this; }
+ bool isServiceFuzzing() const { return mServiceFuzzing; }
+ void setServiceFuzzing() { mServiceFuzzing = true; }
+
private:
friend android::sp<ABpBinder>;
explicit ABpBinder(const ::android::sp<::android::IBinder>& binder);
::android::sp<::android::IBinder> mRemote;
+ bool mServiceFuzzing = false;
};
struct AIBinder_Class {
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index d6937c2..ed53891 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -115,17 +115,29 @@
*/
AIBinder** getR() { return &mBinder; }
- bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); }
- bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); }
- bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); }
- bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); }
- bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); }
- bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); }
-
private:
AIBinder* mBinder = nullptr;
};
+#define SP_AIBINDER_COMPARE(_op_) \
+ static inline bool operator _op_(const SpAIBinder& lhs, const SpAIBinder& rhs) { \
+ return lhs.get() _op_ rhs.get(); \
+ } \
+ static inline bool operator _op_(const SpAIBinder& lhs, const AIBinder* rhs) { \
+ return lhs.get() _op_ rhs; \
+ } \
+ static inline bool operator _op_(const AIBinder* lhs, const SpAIBinder& rhs) { \
+ return lhs _op_ rhs.get(); \
+ }
+
+SP_AIBINDER_COMPARE(!=)
+SP_AIBINDER_COMPARE(<)
+SP_AIBINDER_COMPARE(<=)
+SP_AIBINDER_COMPARE(==)
+SP_AIBINDER_COMPARE(>)
+SP_AIBINDER_COMPARE(>=)
+#undef SP_AIBINDER_COMPARE
+
namespace impl {
/**
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 9949de2..6273804 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -138,6 +138,8 @@
/**
* Dumps information about the interface. By default, dumps nothing.
+ *
+ * This method is not given ownership of the FD.
*/
virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs);
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index 3fbe90d..68528e1 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -24,7 +24,14 @@
__BEGIN_DECLS
/**
- * This creates a threadpool for incoming binder transactions if it has not already been created.
+ * This creates a threadpool for incoming binder transactions if it has not already been created,
+ * spawning one thread, and allowing the kernel to lazily start threads according to the count
+ * that is specified in ABinderProcess_setThreadPoolMaxThreadCount.
+ *
+ * For instance, if ABinderProcess_setThreadPoolMaxThreadCount(3) is called,
+ * ABinderProcess_startThreadPool() is called (+1 thread) then the main thread calls
+ * ABinderProcess_joinThreadPool() (+1 thread), up to *5* total threads will be started
+ * (2 directly, and 3 more if the kernel starts them lazily).
*
* When using this, it is expected that ABinderProcess_setupPolling and
* ABinderProcess_handlePolledCommands are not used.
@@ -36,7 +43,12 @@
/**
* This sets the maximum number of threads that can be started in the threadpool. By default, after
* startThreadPool is called, this is 15. If it is called additional times, it will only prevent
- * the kernel from starting new threads and will not delete already existing threads.
+ * the kernel from starting new threads and will not delete already existing threads. This should
+ * be called once before startThreadPool. The number of threads can never decrease.
+ *
+ * This count refers to the number of threads that will be created lazily by the kernel, in
+ * addition to the threads created by ABinderProcess_startThreadPool or
+ * ABinderProcess_joinThreadPool.
*
* Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
* function should be responsible for configuring the threadpool for the entire application.
@@ -50,8 +62,9 @@
*/
bool ABinderProcess_isThreadPoolStarted(void);
/**
- * This adds the current thread to the threadpool. This may cause the threadpool to exceed the
- * maximum size.
+ * This adds the current thread to the threadpool. This thread will be in addition to the thread
+ * started by ABinderProcess_startThreadPool and the lazy kernel-started threads specified by
+ * ABinderProcess_setThreadPoolMaxThreadCount.
*
* Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
* function should be responsible for configuring the threadpool for the entire application.
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index b5a2e2f..037aa2e 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -270,6 +270,13 @@
}
sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(readBinder);
AIBinder_incStrong(ret.get());
+
+ if (ret.get() != nullptr && parcel->get()->isServiceFuzzing()) {
+ if (auto bp = ret->asABpBinder(); bp != nullptr) {
+ bp->setServiceFuzzing();
+ }
+ }
+
*binder = ret.get();
return PruneStatusT(status);
}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 8c9844c..25b8e97 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -53,7 +53,7 @@
constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
constexpr char kBinderNdkUnitTestServiceFlagged[] = "BinderNdkUnitTestFlagged";
-constexpr unsigned int kShutdownWaitTime = 20;
+constexpr unsigned int kShutdownWaitTime = 11;
constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715;
class MyTestFoo : public IFoo {
@@ -107,11 +107,13 @@
}
static bool activeServicesCallback(bool hasClients, void* context) {
if (hasClients) {
+ LOG(INFO) << "hasClients, so not unregistering.";
return false;
}
// Unregister all services
if (!AServiceManager_tryUnregister()) {
+ LOG(INFO) << "Could not unregister service the first time.";
// Prevent shutdown (test will fail)
return false;
}
@@ -121,6 +123,7 @@
// Unregister again before shutdown
if (!AServiceManager_tryUnregister()) {
+ LOG(INFO) << "Could not unregister service the second time.";
// Prevent shutdown (test will fail)
return false;
}
@@ -128,6 +131,7 @@
// Check if the context was passed correctly
MyBinderNdkUnitTest* service = static_cast<MyBinderNdkUnitTest*>(context);
if (service->contextTestValue != kContextTestValue) {
+ LOG(INFO) << "Incorrect context value.";
// Prevent shutdown (test will fail)
return false;
}
@@ -279,8 +283,8 @@
TEST(NdkBinder, CheckServiceThatDoesExist) {
AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService);
- EXPECT_NE(nullptr, binder);
- EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
+ ASSERT_NE(nullptr, binder) << "Could not get " << kExistingNonNdkService;
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)) << "Could not ping " << kExistingNonNdkService;
AIBinder_decStrong(binder);
}
@@ -373,18 +377,24 @@
}
TEST(NdkBinder, GetTestServiceStressTest) {
- // libbinder has some complicated logic to make sure only one instance of
- // ABpBinder is associated with each binder.
-
constexpr size_t kNumThreads = 10;
constexpr size_t kNumCalls = 1000;
std::vector<std::thread> threads;
+ // this is not a lazy service, but we must make sure that it's started before calling
+ // checkService on it, since the other process serving it might not be started yet.
+ {
+ // getService, not waitForService, to take advantage of timeout
+ auto binder = ndk::SpAIBinder(AServiceManager_getService(IFoo::kSomeInstanceName));
+ ASSERT_NE(nullptr, binder.get());
+ }
+
for (size_t i = 0; i < kNumThreads; i++) {
threads.push_back(std::thread([&]() {
for (size_t j = 0; j < kNumCalls; j++) {
auto binder =
ndk::SpAIBinder(AServiceManager_checkService(IFoo::kSomeInstanceName));
+ ASSERT_NE(nullptr, binder.get());
EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
}
}));
@@ -479,6 +489,8 @@
}
TEST(NdkBinder, ActiveServicesCallbackTest) {
+ LOG(INFO) << "ActiveServicesCallbackTest starting";
+
ndk::SpAIBinder binder(AServiceManager_waitForService(kActiveServicesNdkUnitTestService));
std::shared_ptr<aidl::IBinderNdkUnitTest> service =
aidl::IBinderNdkUnitTest::fromBinder(binder);
@@ -489,6 +501,7 @@
service = nullptr;
IPCThreadState::self()->flushCommands();
+ LOG(INFO) << "ActiveServicesCallbackTest about to sleep";
sleep(kShutdownWaitTime);
ASSERT_FALSE(isServiceRunning(kActiveServicesNdkUnitTestService))
@@ -748,9 +761,9 @@
// local
ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
// convert to platform binder
- EXPECT_NE(binder.get(), nullptr);
+ EXPECT_NE(binder, nullptr);
sp<IBinder> platformBinder = AIBinder_toPlatformBinder(binder.get());
- EXPECT_NE(platformBinder.get(), nullptr);
+ EXPECT_NE(platformBinder, nullptr);
auto proxy = interface_cast<IBinderNdkUnitTest>(platformBinder);
EXPECT_NE(proxy, nullptr);
@@ -761,7 +774,7 @@
// convert back
ndk::SpAIBinder backBinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(platformBinder));
- EXPECT_EQ(backBinder.get(), binder.get());
+ EXPECT_EQ(backBinder, binder);
}
}
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 2d2bf7c..1dc0b24 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -103,7 +103,12 @@
//
// This shouldn't cause issues with blocking the thread as only one task will run in a
// call to `block_on`, so there aren't other tasks to block.
- let result = spawn_me();
+ //
+ // If the `block_in_place` call fails, then you are driving a current-thread runtime on
+ // the binder threadpool. Instead, it is recommended to use `TokioRuntime<Handle>` when
+ // the runtime is a current-thread runtime, as the current-thread runtime can be driven
+ // only by `Runtime::block_on` calls and not by `Handle::block_on`.
+ let result = tokio::task::block_in_place(spawn_me);
Box::pin(after_spawn(result))
} else {
let handle = tokio::task::spawn_blocking(spawn_me);
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 0067a20..788abc4 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -75,7 +75,6 @@
visibility: [":__subpackages__"],
source_stem: "bindings",
bindgen_flags: [
- "--size_t-is-usize",
"--blocklist-type",
"AIBinder",
"--raw-line",
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index c87876a..6fda878 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -33,9 +33,9 @@
pub struct RpcServerRef;
}
-/// SAFETY - The opaque handle can be cloned freely.
+/// SAFETY: The opaque handle can be cloned freely.
unsafe impl Send for RpcServer {}
-/// SAFETY - The underlying C++ RpcServer class is thread-safe.
+/// SAFETY: The underlying C++ RpcServer class is thread-safe.
unsafe impl Sync for RpcServer {}
impl RpcServer {
@@ -57,26 +57,21 @@
}
/// Creates a binder RPC server, serving the supplied binder service implementation on the given
- /// socket file name. The socket should be initialized in init.rc with the same name.
- pub fn new_init_unix_domain(
+ /// socket file descriptor. The socket should be bound to an address before calling this
+ /// function.
+ pub fn new_bound_socket(
mut service: SpIBinder,
- socket_name: &str,
+ socket_fd: OwnedFd,
) -> Result<RpcServer, Error> {
- let socket_name = match CString::new(socket_name) {
- Ok(s) => s,
- Err(e) => {
- log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
- return Err(Error::from(ErrorKind::InvalidInput));
- }
- };
let service = service.as_native_mut();
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
+ // The server takes ownership of the socket FD.
unsafe {
- Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain(
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket(
service,
- socket_name.as_ptr(),
+ socket_fd.into_raw_fd(),
))
}
}
@@ -129,7 +124,9 @@
if ptr.is_null() {
return Err(Error::new(ErrorKind::Other, "Failed to start server"));
}
- Ok(RpcServer::from_ptr(ptr))
+ // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not
+ // null.
+ Ok(unsafe { RpcServer::from_ptr(ptr) })
}
}
@@ -139,7 +136,7 @@
&self,
modes: &[FileDescriptorTransportMode],
) {
- // SAFETY - Does not keep the pointer after returning does, nor does it
+ // SAFETY: Does not keep the pointer after returning does, nor does it
// read past its boundary. Only passes the 'self' pointer as an opaque handle.
unsafe {
binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes(
@@ -152,18 +149,21 @@
/// Starts a new background thread and calls join(). Returns immediately.
pub fn start(&self) {
+ // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) };
}
/// Joins the RpcServer thread. The call blocks until the server terminates.
/// This must be called from exactly one thread.
pub fn join(&self) {
+ // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) };
}
/// Shuts down the running RpcServer. Can be called multiple times and from
/// multiple threads. Called automatically during drop().
pub fn shutdown(&self) -> Result<(), Error> {
+ // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } {
Ok(())
} else {
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 28c5390..79a9510 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -36,15 +36,15 @@
pub struct RpcSessionRef;
}
-/// SAFETY - The opaque handle can be cloned freely.
+/// SAFETY: The opaque handle can be cloned freely.
unsafe impl Send for RpcSession {}
-/// SAFETY - The underlying C++ RpcSession class is thread-safe.
+/// SAFETY: The underlying C++ RpcSession class is thread-safe.
unsafe impl Sync for RpcSession {}
impl RpcSession {
/// Allocates a new RpcSession object.
pub fn new() -> RpcSession {
- // SAFETY - Takes ownership of the returned handle, which has correct refcount.
+ // SAFETY: Takes ownership of the returned handle, which has correct refcount.
unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) }
}
}
@@ -58,7 +58,7 @@
impl RpcSessionRef {
/// Sets the file descriptor transport mode for this session.
pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) {
- // SAFETY - Only passes the 'self' pointer as an opaque handle.
+ // SAFETY: Only passes the 'self' pointer as an opaque handle.
unsafe {
binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode(
self.as_ptr(),
@@ -69,7 +69,7 @@
/// Sets the maximum number of incoming threads.
pub fn set_max_incoming_threads(&self, threads: usize) {
- // SAFETY - Only passes the 'self' pointer as an opaque handle.
+ // SAFETY: Only passes the 'self' pointer as an opaque handle.
unsafe {
binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads)
};
@@ -77,7 +77,7 @@
/// Sets the maximum number of outgoing connections.
pub fn set_max_outgoing_connections(&self, connections: usize) {
- // SAFETY - Only passes the 'self' pointer as an opaque handle.
+ // SAFETY: Only passes the 'self' pointer as an opaque handle.
unsafe {
binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections(
self.as_ptr(),
@@ -210,10 +210,10 @@
type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
+ let request_fd_ptr = param as *mut RequestFd;
// SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
// BinderFdFactory reference, with param being a properly aligned non-null pointer to an
// initialized instance.
- let request_fd_ptr = param as *mut RequestFd;
- let request_fd = request_fd_ptr.as_mut().unwrap();
+ let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() };
request_fd().unwrap_or(-1)
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index b90b40b..463c210 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -97,8 +97,8 @@
/// Interface stability promise
///
-/// An interface can promise to be a stable vendor interface ([`Vintf`]), or
-/// makes no stability guarantees ([`Local`]). [`Local`] is
+/// An interface can promise to be a stable vendor interface ([`Stability::Vintf`]),
+/// or makes no stability guarantees ([`Stability::Local`]). [`Stability::Local`] is
/// currently the default stability.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Stability {
@@ -139,8 +139,8 @@
/// via `Binder::new(object)`.
///
/// This is a low-level interface that should normally be automatically
-/// generated from AIDL via the [`declare_binder_interface!`] macro. When using
-/// the AIDL backend, users need only implement the high-level AIDL-defined
+/// generated from AIDL via the [`crate::declare_binder_interface!`] macro.
+/// When using the AIDL backend, users need only implement the high-level AIDL-defined
/// interface. The AIDL compiler then generates a container struct that wraps
/// the user-defined service and implements `Remotable`.
pub trait Remotable: Send + Sync {
@@ -260,7 +260,14 @@
/// Trying to use this function on a local binder will result in an
/// INVALID_OPERATION code being returned and nothing happening.
///
- /// This link always holds a weak reference to its recipient.
+ /// This link only holds a weak reference to its recipient. If the
+ /// `DeathRecipient` is dropped then it will be unlinked.
+ ///
+ /// Note that the notifications won't work if you don't first start at least
+ /// one Binder thread by calling
+ /// [`ProcessState::start_thread_pool`](crate::ProcessState::start_thread_pool)
+ /// or
+ /// [`ProcessState::join_thread_pool`](crate::ProcessState::join_thread_pool).
fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
/// Remove a previously registered death notification.
@@ -290,18 +297,17 @@
/// Note: the returned pointer will not be constant. Calling this method
/// multiple times for the same type will result in distinct class
/// pointers. A static getter for this value is implemented in
- /// [`declare_binder_interface!`].
+ /// [`crate::declare_binder_interface!`].
pub fn new<I: InterfaceClassMethods>() -> InterfaceClass {
let descriptor = CString::new(I::get_descriptor()).unwrap();
+ // Safety: `AIBinder_Class_define` expects a valid C string, and three
+ // valid callback functions, all non-null pointers. The C string is
+ // copied and need not be valid for longer than the call, so we can drop
+ // it after the call. We can safely assign null to the onDump and
+ // handleShellCommand callbacks as long as the class pointer was
+ // non-null. Rust None for a Option<fn> is guaranteed to be a NULL
+ // pointer. Rust retains ownership of the pointer after it is defined.
let ptr = unsafe {
- // Safety: `AIBinder_Class_define` expects a valid C string, and
- // three valid callback functions, all non-null pointers. The C
- // string is copied and need not be valid for longer than the call,
- // so we can drop it after the call. We can safely assign null to
- // the onDump and handleShellCommand callbacks as long as the class
- // pointer was non-null. Rust None for a Option<fn> is guaranteed to
- // be a NULL pointer. Rust retains ownership of the pointer after it
- // is defined.
let class = sys::AIBinder_Class_define(
descriptor.as_ptr(),
Some(I::on_create),
@@ -331,13 +337,12 @@
/// Get the interface descriptor string of this class.
pub fn get_descriptor(&self) -> String {
+ // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor is
+ // always a two-byte null terminated sequence of u16s. Thus, we can
+ // continue reading from the pointer until we hit a null value, and this
+ // pointer can be a valid slice if the slice length is <= the number of
+ // u16 elements before the null terminator.
unsafe {
- // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor
- // is always a two-byte null terminated sequence of u16s. Thus, we
- // can continue reading from the pointer until we hit a null value,
- // and this pointer can be a valid slice if the slice length is <=
- // the number of u16 elements before the null terminator.
-
let raw_descriptor: *const c_char = sys::AIBinder_Class_getDescriptor(self.0);
CStr::from_ptr(raw_descriptor)
.to_str()
@@ -535,17 +540,15 @@
static CLASS_INIT: std::sync::Once = std::sync::Once::new();
static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
+ // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
+ // variable, and therefore is thread-safe, as it can only occur
+ // once.
CLASS_INIT.call_once(|| unsafe {
- // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
- // variable, and therefore is thread-safe, as it can only occur
- // once.
CLASS = Some($constructor);
});
- unsafe {
- // Safety: The `CLASS` variable can only be mutated once, above,
- // and is subsequently safe to read from any thread.
- CLASS.unwrap()
- }
+ // Safety: The `CLASS` variable can only be mutated once, above, and
+ // is subsequently safe to read from any thread.
+ unsafe { CLASS.unwrap() }
}
};
}
@@ -657,6 +660,8 @@
fn as_native_mut(&mut self) -> *mut T;
}
+// Safety: If V is a valid Android C++ type then we can either use that or a
+// null pointer.
unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> {
fn as_native(&self) -> *const T {
self.as_ref().map_or(ptr::null(), |v| v.as_native())
@@ -917,15 +922,15 @@
static CLASS_INIT: std::sync::Once = std::sync::Once::new();
static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
+ // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
+ // variable, and therefore is thread-safe, as it can only occur
+ // once.
CLASS_INIT.call_once(|| unsafe {
- // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
- // variable, and therefore is thread-safe, as it can only occur
- // once.
CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>());
});
+ // Safety: The `CLASS` variable can only be mutated once, above,
+ // and is subsequently safe to read from any thread.
unsafe {
- // Safety: The `CLASS` variable can only be mutated once, above,
- // and is subsequently safe to read from any thread.
CLASS.unwrap()
}
}
@@ -1018,17 +1023,7 @@
}
if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
- let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
- std::convert::TryFrom::try_from(ibinder.clone());
- if let Ok(service) = service {
- // We were able to associate with our expected class and
- // the service is local.
- todo!()
- //return Ok($crate::Strong::new(Box::new(service)));
- } else {
- // Service is remote
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
- }
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
Err($crate::StatusCode::BAD_TYPE.into())
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index ba26062..8d9ce0e 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -112,41 +112,35 @@
impl Status {
/// Create a status object representing a successful transaction.
pub fn ok() -> Self {
- let ptr = unsafe {
- // Safety: `AStatus_newOk` always returns a new, heap allocated
- // pointer to an `ASTatus` object, so we know this pointer will be
- // valid.
- //
- // Rust takes ownership of the returned pointer.
- sys::AStatus_newOk()
- };
+ // Safety: `AStatus_newOk` always returns a new, heap allocated
+ // pointer to an `ASTatus` object, so we know this pointer will be
+ // valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ let ptr = unsafe { sys::AStatus_newOk() };
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
/// Create a status object from a service specific error
pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
let ptr = if let Some(message) = message {
- unsafe {
- // Safety: Any i32 is a valid service specific error for the
- // error code parameter. We construct a valid, null-terminated
- // `CString` from the message, which must be a valid C-style
- // string to pass as the message. This function always returns a
- // new, heap allocated pointer to an `AStatus` object, so we
- // know the returned pointer will be valid.
- //
- // Rust takes ownership of the returned pointer.
- sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr())
- }
+ // Safety: Any i32 is a valid service specific error for the
+ // error code parameter. We construct a valid, null-terminated
+ // `CString` from the message, which must be a valid C-style
+ // string to pass as the message. This function always returns a
+ // new, heap allocated pointer to an `AStatus` object, so we
+ // know the returned pointer will be valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ unsafe { sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) }
} else {
- unsafe {
- // Safety: Any i32 is a valid service specific error for the
- // error code parameter. This function always returns a new,
- // heap allocated pointer to an `AStatus` object, so we know the
- // returned pointer will be valid.
- //
- // Rust takes ownership of the returned pointer.
- sys::AStatus_fromServiceSpecificError(err)
- }
+ // Safety: Any i32 is a valid service specific error for the
+ // error code parameter. This function always returns a new,
+ // heap allocated pointer to an `AStatus` object, so we know the
+ // returned pointer will be valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ unsafe { sys::AStatus_fromServiceSpecificError(err) }
};
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
@@ -159,6 +153,8 @@
/// Create a status object from an exception code
pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
if let Some(message) = message {
+ // Safety: the C string pointer is valid and not retained by the
+ // function.
let ptr = unsafe {
sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
};
@@ -187,37 +183,31 @@
/// Returns `true` if this status represents a successful transaction.
pub fn is_ok(&self) -> bool {
- unsafe {
- // Safety: `Status` always contains a valid `AStatus` pointer, so we
- // are always passing a valid pointer to `AStatus_isOk` here.
- sys::AStatus_isOk(self.as_native())
- }
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_isOk` here.
+ unsafe { sys::AStatus_isOk(self.as_native()) }
}
/// Returns a description of the status.
pub fn get_description(&self) -> String {
- let description_ptr = unsafe {
- // Safety: `Status` always contains a valid `AStatus` pointer, so we
- // are always passing a valid pointer to `AStatus_getDescription`
- // here.
- //
- // `AStatus_getDescription` always returns a valid pointer to a null
- // terminated C string. Rust is responsible for freeing this pointer
- // via `AStatus_deleteDescription`.
- sys::AStatus_getDescription(self.as_native())
- };
- let description = unsafe {
- // Safety: `AStatus_getDescription` always returns a valid C string,
- // which can be safely converted to a `CStr`.
- CStr::from_ptr(description_ptr)
- };
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getDescription`
+ // here.
+ //
+ // `AStatus_getDescription` always returns a valid pointer to a null
+ // terminated C string. Rust is responsible for freeing this pointer
+ // via `AStatus_deleteDescription`.
+ let description_ptr = unsafe { sys::AStatus_getDescription(self.as_native()) };
+ // Safety: `AStatus_getDescription` always returns a valid C string,
+ // which can be safely converted to a `CStr`.
+ let description = unsafe { CStr::from_ptr(description_ptr) };
let description = description.to_string_lossy().to_string();
+ // Safety: `description_ptr` was returned from
+ // `AStatus_getDescription` above, and must be freed via
+ // `AStatus_deleteDescription`. We must not access the pointer after
+ // this call, so we copy it into an owned string above and return
+ // that string.
unsafe {
- // Safety: `description_ptr` was returned from
- // `AStatus_getDescription` above, and must be freed via
- // `AStatus_deleteDescription`. We must not access the pointer after
- // this call, so we copy it into an owned string above and return
- // that string.
sys::AStatus_deleteDescription(description_ptr);
}
description
@@ -225,12 +215,10 @@
/// Returns the exception code of the status.
pub fn exception_code(&self) -> ExceptionCode {
- let code = unsafe {
- // Safety: `Status` always contains a valid `AStatus` pointer, so we
- // are always passing a valid pointer to `AStatus_getExceptionCode`
- // here.
- sys::AStatus_getExceptionCode(self.as_native())
- };
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getExceptionCode`
+ // here.
+ let code = unsafe { sys::AStatus_getExceptionCode(self.as_native()) };
parse_exception_code(code)
}
@@ -241,11 +229,9 @@
/// exception or a service specific error. To find out if this transaction
/// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
pub fn transaction_error(&self) -> StatusCode {
- let code = unsafe {
- // Safety: `Status` always contains a valid `AStatus` pointer, so we
- // are always passing a valid pointer to `AStatus_getStatus` here.
- sys::AStatus_getStatus(self.as_native())
- };
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getStatus` here.
+ let code = unsafe { sys::AStatus_getStatus(self.as_native()) };
parse_status_code(code)
}
@@ -258,12 +244,10 @@
/// find out if this transaction as a whole is okay, use
/// [`is_ok`](Self::is_ok) instead.
pub fn service_specific_error(&self) -> i32 {
- unsafe {
- // Safety: `Status` always contains a valid `AStatus` pointer, so we
- // are always passing a valid pointer to
- // `AStatus_getServiceSpecificError` here.
- sys::AStatus_getServiceSpecificError(self.as_native())
- }
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to
+ // `AStatus_getServiceSpecificError` here.
+ unsafe { sys::AStatus_getServiceSpecificError(self.as_native()) }
}
/// Calls `op` if the status was ok, otherwise returns an `Err` value of
@@ -321,24 +305,20 @@
impl From<status_t> for Status {
fn from(status: status_t) -> Status {
- let ptr = unsafe {
- // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
- // this is a safe FFI call. Unknown values will be coerced into
- // UNKNOWN_ERROR.
- sys::AStatus_fromStatus(status)
- };
+ // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
+ // this is a safe FFI call. Unknown values will be coerced into
+ // UNKNOWN_ERROR.
+ let ptr = unsafe { sys::AStatus_fromStatus(status) };
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
}
impl From<ExceptionCode> for Status {
fn from(code: ExceptionCode) -> Status {
- let ptr = unsafe {
- // Safety: `AStatus_fromExceptionCode` expects any
- // `binder_exception_t` (i32) integer, so this is a safe FFI call.
- // Unknown values will be coerced into EX_TRANSACTION_FAILED.
- sys::AStatus_fromExceptionCode(code as i32)
- };
+ // Safety: `AStatus_fromExceptionCode` expects any
+ // `binder_exception_t` (i32) integer, so this is a safe FFI call.
+ // Unknown values will be coerced into EX_TRANSACTION_FAILED.
+ let ptr = unsafe { sys::AStatus_fromExceptionCode(code as i32) };
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
}
@@ -363,20 +343,18 @@
impl Drop for Status {
fn drop(&mut self) {
+ // Safety: `Status` manages the lifetime of its inner `AStatus`
+ // pointee, so we need to delete it here. We know that the pointer
+ // will be valid here since `Status` always contains a valid pointer
+ // while it is alive.
unsafe {
- // Safety: `Status` manages the lifetime of its inner `AStatus`
- // pointee, so we need to delete it here. We know that the pointer
- // will be valid here since `Status` always contains a valid pointer
- // while it is alive.
sys::AStatus_delete(self.0.as_mut());
}
}
}
-/// # Safety
-///
-/// `Status` always contains a valid pointer to an `AStatus` object, so we can
-/// trivially convert it to a correctly-typed raw pointer.
+/// Safety: `Status` always contains a valid pointer to an `AStatus` object, so
+/// we can trivially convert it to a correctly-typed raw pointer.
///
/// Care must be taken that the returned pointer is only dereferenced while the
/// `Status` object is still alive.
@@ -386,11 +364,9 @@
}
fn as_native_mut(&mut self) -> *mut sys::AStatus {
- unsafe {
- // Safety: The pointer will be valid here since `Status` always
- // contains a valid and initialized pointer while it is alive.
- self.0.as_mut()
- }
+ // Safety: The pointer will be valid here since `Status` always contains
+ // a valid and initialized pointer while it is alive.
+ unsafe { self.0.as_mut() }
}
}
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 5557168..b248f5e 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -42,7 +42,7 @@
rust_object: *mut T,
}
-/// # Safety
+/// Safety:
///
/// A `Binder<T>` is a pair of unique owning pointers to two values:
/// * a C++ ABBinder which the C++ API guarantees can be passed between threads
@@ -54,7 +54,7 @@
/// to how `Box<T>` is `Send` if `T` is `Send`.
unsafe impl<T: Remotable> Send for Binder<T> {}
-/// # Safety
+/// Safety:
///
/// A `Binder<T>` is a pair of unique owning pointers to two values:
/// * a C++ ABBinder which is thread-safe, i.e. `Send + Sync`
@@ -89,15 +89,13 @@
pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> {
let class = T::get_class();
let rust_object = Box::into_raw(Box::new(rust_object));
- let ibinder = unsafe {
- // Safety: `AIBinder_new` expects a valid class pointer (which we
- // initialize via `get_class`), and an arbitrary pointer
- // argument. The caller owns the returned `AIBinder` pointer, which
- // is a strong reference to a `BBinder`. This reference should be
- // decremented via `AIBinder_decStrong` when the reference lifetime
- // ends.
- sys::AIBinder_new(class.into(), rust_object as *mut c_void)
- };
+ // Safety: `AIBinder_new` expects a valid class pointer (which we
+ // initialize via `get_class`), and an arbitrary pointer
+ // argument. The caller owns the returned `AIBinder` pointer, which
+ // is a strong reference to a `BBinder`. This reference should be
+ // decremented via `AIBinder_decStrong` when the reference lifetime
+ // ends.
+ let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) };
let mut binder = Binder { ibinder, rust_object };
binder.mark_stability(stability);
binder
@@ -176,15 +174,14 @@
/// }
/// # }
pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> {
- let status = unsafe {
- // Safety: `AIBinder_setExtension` expects two valid, mutable
- // `AIBinder` pointers. We are guaranteed that both `self` and
- // `extension` contain valid `AIBinder` pointers, because they
- // cannot be initialized without a valid
- // pointer. `AIBinder_setExtension` does not take ownership of
- // either parameter.
- sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut())
- };
+ let status =
+ // Safety: `AIBinder_setExtension` expects two valid, mutable
+ // `AIBinder` pointers. We are guaranteed that both `self` and
+ // `extension` contain valid `AIBinder` pointers, because they
+ // cannot be initialized without a valid
+ // pointer. `AIBinder_setExtension` does not take ownership of
+ // either parameter.
+ unsafe { sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut()) };
status_result(status)
}
@@ -199,9 +196,9 @@
match stability {
Stability::Local => self.mark_local_stability(),
Stability::Vintf => {
+ // Safety: Self always contains a valid `AIBinder` pointer, so
+ // we can always call this C API safely.
unsafe {
- // Safety: Self always contains a valid `AIBinder` pointer, so
- // we can always call this C API safely.
sys::AIBinder_markVintfStability(self.as_native_mut());
}
}
@@ -212,9 +209,9 @@
/// building for android_vendor and system otherwise.
#[cfg(android_vendor)]
fn mark_local_stability(&mut self) {
+ // Safety: Self always contains a valid `AIBinder` pointer, so we can
+ // always call this C API safely.
unsafe {
- // Safety: Self always contains a valid `AIBinder` pointer, so
- // we can always call this C API safely.
sys::AIBinder_markVendorStability(self.as_native_mut());
}
}
@@ -223,9 +220,9 @@
/// building for android_vendor and system otherwise.
#[cfg(not(android_vendor))]
fn mark_local_stability(&mut self) {
+ // Safety: Self always contains a valid `AIBinder` pointer, so we can
+ // always call this C API safely.
unsafe {
- // Safety: Self always contains a valid `AIBinder` pointer, so
- // we can always call this C API safely.
sys::AIBinder_markSystemStability(self.as_native_mut());
}
}
@@ -239,13 +236,13 @@
/// remotable object, which will prevent the object from being dropped while
/// the `SpIBinder` is alive.
fn as_binder(&self) -> SpIBinder {
+ // Safety: `self.ibinder` is guaranteed to always be a valid pointer
+ // to an `AIBinder` by the `Binder` constructor. We are creating a
+ // copy of the `self.ibinder` strong reference, but
+ // `SpIBinder::from_raw` assumes it receives an owned pointer with
+ // its own strong reference. We first increment the reference count,
+ // so that the new `SpIBinder` will be tracked as a new reference.
unsafe {
- // Safety: `self.ibinder` is guaranteed to always be a valid pointer
- // to an `AIBinder` by the `Binder` constructor. We are creating a
- // copy of the `self.ibinder` strong reference, but
- // `SpIBinder::from_raw` assumes it receives an owned pointer with
- // its own strong reference. We first increment the reference count,
- // so that the new `SpIBinder` will be tracked as a new reference.
sys::AIBinder_incStrong(self.ibinder);
SpIBinder::from_raw(self.ibinder).unwrap()
}
@@ -275,10 +272,20 @@
reply: *mut sys::AParcel,
) -> status_t {
let res = {
- let mut reply = BorrowedParcel::from_raw(reply).unwrap();
- let data = BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap();
- let object = sys::AIBinder_getUserData(binder);
- let binder: &T = &*(object as *const T);
+ // Safety: The caller must give us a parcel pointer which is either
+ // null or valid at least for the duration of this function call. We
+ // don't keep the resulting value beyond the function.
+ let mut reply = unsafe { BorrowedParcel::from_raw(reply).unwrap() };
+ // Safety: The caller must give us a parcel pointer which is either
+ // null or valid at least for the duration of this function call. We
+ // don't keep the resulting value beyond the function.
+ let data = unsafe { BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap() };
+ // Safety: Our caller promised that `binder` is a non-null, valid
+ // pointer to a local `AIBinder`.
+ let object = unsafe { sys::AIBinder_getUserData(binder) };
+ // Safety: Our caller promised that the binder has a `T` pointer in
+ // its user data.
+ let binder: &T = unsafe { &*(object as *const T) };
binder.on_transact(code, &data, &mut reply)
};
match res {
@@ -295,7 +302,9 @@
/// Must be called with a valid pointer to a `T` object. After this call,
/// the pointer will be invalid and should not be dereferenced.
unsafe extern "C" fn on_destroy(object: *mut c_void) {
- drop(Box::from_raw(object as *mut T));
+ // Safety: Our caller promised that `object` is a valid pointer to a
+ // `T`.
+ drop(unsafe { Box::from_raw(object as *mut T) });
}
/// Called whenever a new, local `AIBinder` object is needed of a specific
@@ -320,7 +329,7 @@
/// Must be called with a non-null, valid pointer to a local `AIBinder` that
/// contains a `T` pointer in its user data. fd should be a non-owned file
/// descriptor, and args must be an array of null-terminated string
- /// poiinters with length num_args.
+ /// pointers with length num_args.
unsafe extern "C" fn on_dump(
binder: *mut sys::AIBinder,
fd: i32,
@@ -330,8 +339,9 @@
if fd < 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
}
- // We don't own this file, so we need to be careful not to drop it.
- let file = ManuallyDrop::new(File::from_raw_fd(fd));
+ // Safety: Our caller promised that fd is a file descriptor. We don't
+ // own this file descriptor, so we need to be careful not to drop it.
+ let file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
if args.is_null() && num_args != 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
@@ -340,14 +350,22 @@
let args = if args.is_null() || num_args == 0 {
vec![]
} else {
- slice::from_raw_parts(args, num_args as usize)
- .iter()
- .map(|s| CStr::from_ptr(*s))
- .collect()
+ // Safety: Our caller promised that `args` is an array of
+ // null-terminated string pointers with length `num_args`.
+ unsafe {
+ slice::from_raw_parts(args, num_args as usize)
+ .iter()
+ .map(|s| CStr::from_ptr(*s))
+ .collect()
+ }
};
- let object = sys::AIBinder_getUserData(binder);
- let binder: &T = &*(object as *const T);
+ // Safety: Our caller promised that `binder` is a non-null, valid
+ // pointer to a local `AIBinder`.
+ let object = unsafe { sys::AIBinder_getUserData(binder) };
+ // Safety: Our caller promised that the binder has a `T` pointer in its
+ // user data.
+ let binder: &T = unsafe { &*(object as *const T) };
let res = binder.on_dump(&file, &args);
match res {
@@ -363,11 +381,11 @@
// actually destroys the object, it calls `on_destroy` and we can drop the
// `rust_object` then.
fn drop(&mut self) {
+ // Safety: When `self` is dropped, we can no longer access the
+ // reference, so can decrement the reference count. `self.ibinder` is
+ // always a valid `AIBinder` pointer, so is valid to pass to
+ // `AIBinder_decStrong`.
unsafe {
- // Safety: When `self` is dropped, we can no longer access the
- // reference, so can decrement the reference count. `self.ibinder`
- // is always a valid `AIBinder` pointer, so is valid to pass to
- // `AIBinder_decStrong`.
sys::AIBinder_decStrong(self.ibinder);
}
}
@@ -377,14 +395,11 @@
type Target = T;
fn deref(&self) -> &Self::Target {
- unsafe {
- // Safety: While `self` is alive, the reference count of the
- // underlying object is > 0 and therefore `on_destroy` cannot be
- // called. Therefore while `self` is alive, we know that
- // `rust_object` is still a valid pointer to a heap allocated object
- // of type `T`.
- &*self.rust_object
- }
+ // Safety: While `self` is alive, the reference count of the underlying
+ // object is > 0 and therefore `on_destroy` cannot be called. Therefore
+ // while `self` is alive, we know that `rust_object` is still a valid
+ // pointer to a heap allocated object of type `T`.
+ unsafe { &*self.rust_object }
}
}
@@ -405,13 +420,10 @@
if Some(class) != ibinder.get_class() {
return Err(StatusCode::BAD_TYPE);
}
- let userdata = unsafe {
- // Safety: `SpIBinder` always holds a valid pointer pointer to an
- // `AIBinder`, which we can safely pass to
- // `AIBinder_getUserData`. `ibinder` retains ownership of the
- // returned pointer.
- sys::AIBinder_getUserData(ibinder.as_native_mut())
- };
+ // Safety: `SpIBinder` always holds a valid pointer pointer to an
+ // `AIBinder`, which we can safely pass to `AIBinder_getUserData`.
+ // `ibinder` retains ownership of the returned pointer.
+ let userdata = unsafe { sys::AIBinder_getUserData(ibinder.as_native_mut()) };
if userdata.is_null() {
return Err(StatusCode::UNEXPECTED_NULL);
}
@@ -422,12 +434,10 @@
}
}
-/// # Safety
-///
-/// The constructor for `Binder` guarantees that `self.ibinder` will contain a
-/// valid, non-null pointer to an `AIBinder`, so this implementation is type
-/// safe. `self.ibinder` will remain valid for the entire lifetime of `self`
-/// because we hold a strong reference to the `AIBinder` until `self` is
+/// Safety: The constructor for `Binder` guarantees that `self.ibinder` will
+/// contain a valid, non-null pointer to an `AIBinder`, so this implementation
+/// is type safe. `self.ibinder` will remain valid for the entire lifetime of
+/// `self` because we hold a strong reference to the `AIBinder` until `self` is
/// dropped.
unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> {
fn as_native(&self) -> *const sys::AIBinder {
@@ -447,14 +457,12 @@
/// This function will panic if the identifier contains a 0 byte (NUL).
pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
let instance = CString::new(identifier).unwrap();
- let status = unsafe {
- // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
- // string pointers. Caller retains ownership of both
- // pointers. `AServiceManager_addService` creates a new strong reference
- // and copies the string, so both pointers need only be valid until the
- // call returns.
- sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr())
- };
+ let status =
+ // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both pointers.
+ // `AServiceManager_addService` creates a new strong reference and copies
+ // the string, so both pointers need only be valid until the call returns.
+ unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) };
status_result(status)
}
@@ -470,13 +478,12 @@
/// This function will panic if the identifier contains a 0 byte (NUL).
pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
let instance = CString::new(identifier).unwrap();
+ // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both
+ // pointers. `AServiceManager_registerLazyService` creates a new strong reference
+ // and copies the string, so both pointers need only be valid until the
+ // call returns.
let status = unsafe {
- // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
- // string pointers. Caller retains ownership of both
- // pointers. `AServiceManager_registerLazyService` creates a new strong reference
- // and copies the string, so both pointers need only be valid until the
- // call returns.
-
sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr())
};
status_result(status)
@@ -491,10 +498,8 @@
///
/// Consider using [`LazyServiceGuard`] rather than calling this directly.
pub fn force_lazy_services_persist(persist: bool) {
- unsafe {
- // Safety: No borrowing or transfer of ownership occurs here.
- sys::AServiceManager_forceLazyServicesPersist(persist)
- }
+ // Safety: No borrowing or transfer of ownership occurs here.
+ unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) }
}
/// An RAII object to ensure a process which registers lazy services is not killed. During the
@@ -576,8 +581,6 @@
/// Determine whether the current thread is currently executing an incoming
/// transaction.
pub fn is_handling_transaction() -> bool {
- unsafe {
- // Safety: This method is always safe to call.
- sys::AIBinder_isHandlingTransaction()
- }
+ // Safety: This method is always safe to call.
+ unsafe { sys::AIBinder_isHandlingTransaction() }
}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index e4c568e..3c615ed 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -52,11 +52,8 @@
ptr: NonNull<sys::AParcel>,
}
-/// # Safety
-///
-/// This type guarantees that it owns the AParcel and that all access to
-/// the AParcel happens through the Parcel, so it is ok to send across
-/// threads.
+/// Safety: This type guarantees that it owns the AParcel and that all access to
+/// the AParcel happens through the Parcel, so it is ok to send across threads.
unsafe impl Send for Parcel {}
/// Container for a message (data and object references) that can be sent
@@ -73,11 +70,9 @@
impl Parcel {
/// Create a new empty `Parcel`.
pub fn new() -> Parcel {
- let ptr = unsafe {
- // Safety: If `AParcel_create` succeeds, it always returns
- // a valid pointer. If it fails, the process will crash.
- sys::AParcel_create()
- };
+ // Safety: If `AParcel_create` succeeds, it always returns
+ // a valid pointer. If it fails, the process will crash.
+ let ptr = unsafe { sys::AParcel_create() };
Self { ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") }
}
@@ -171,10 +166,8 @@
}
}
-/// # Safety
-///
-/// The `Parcel` constructors guarantee that a `Parcel` object will always
-/// contain a valid pointer to an `AParcel`.
+/// Safety: The `Parcel` constructors guarantee that a `Parcel` object will
+/// always contain a valid pointer to an `AParcel`.
unsafe impl AsNative<sys::AParcel> for Parcel {
fn as_native(&self) -> *const sys::AParcel {
self.ptr.as_ptr()
@@ -185,10 +178,8 @@
}
}
-/// # Safety
-///
-/// The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` object
-/// will always contain a valid pointer to an `AParcel`.
+/// Safety: The `BorrowedParcel` constructors guarantee that a `BorrowedParcel`
+/// object will always contain a valid pointer to an `AParcel`.
unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> {
fn as_native(&self) -> *const sys::AParcel {
self.ptr.as_ptr()
@@ -203,10 +194,8 @@
impl<'a> BorrowedParcel<'a> {
/// Data written to parcelable is zero'd before being deleted or reallocated.
pub fn mark_sensitive(&mut self) {
- unsafe {
- // Safety: guaranteed to have a parcel object, and this method never fails
- sys::AParcel_markSensitive(self.as_native())
- }
+ // Safety: guaranteed to have a parcel object, and this method never fails
+ unsafe { sys::AParcel_markSensitive(self.as_native()) }
}
/// Write a type that implements [`Serialize`] to the parcel.
@@ -265,11 +254,15 @@
f(&mut subparcel)?;
}
let end = self.get_data_position();
+ // Safety: start is less than the current size of the parcel data
+ // buffer, because we just got it with `get_data_position`.
unsafe {
self.set_data_position(start)?;
}
assert!(end >= start);
self.write(&(end - start))?;
+ // Safety: end is less than the current size of the parcel data
+ // buffer, because we just got it with `get_data_position`.
unsafe {
self.set_data_position(end)?;
}
@@ -278,20 +271,16 @@
/// Returns the current position in the parcel data.
pub fn get_data_position(&self) -> i32 {
- unsafe {
- // Safety: `BorrowedParcel` always contains a valid pointer to an
- // `AParcel`, and this call is otherwise safe.
- sys::AParcel_getDataPosition(self.as_native())
- }
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and this call is otherwise safe.
+ unsafe { sys::AParcel_getDataPosition(self.as_native()) }
}
/// Returns the total size of the parcel.
pub fn get_data_size(&self) -> i32 {
- unsafe {
- // Safety: `BorrowedParcel` always contains a valid pointer to an
- // `AParcel`, and this call is otherwise safe.
- sys::AParcel_getDataSize(self.as_native())
- }
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and this call is otherwise safe.
+ unsafe { sys::AParcel_getDataSize(self.as_native()) }
}
/// Move the current read/write position in the parcel.
@@ -304,7 +293,9 @@
/// accesses are bounds checked, this call is still safe, but we can't rely
/// on that.
pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
- status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and the caller guarantees that `pos` is within bounds.
+ status_result(unsafe { sys::AParcel_setDataPosition(self.as_native(), pos) })
}
/// Append a subset of another parcel.
@@ -317,10 +308,10 @@
start: i32,
size: i32,
) -> Result<()> {
+ // Safety: `Parcel::appendFrom` from C++ checks that `start`
+ // and `size` are in bounds, and returns an error otherwise.
+ // Both `self` and `other` always contain valid pointers.
let status = unsafe {
- // Safety: `Parcel::appendFrom` from C++ checks that `start`
- // and `size` are in bounds, and returns an error otherwise.
- // Both `self` and `other` always contain valid pointers.
sys::AParcel_appendFrom(other.as_native(), self.as_native_mut(), start, size)
};
status_result(status)
@@ -418,7 +409,9 @@
/// accesses are bounds checked, this call is still safe, but we can't rely
/// on that.
pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
- self.borrowed_ref().set_data_position(pos)
+ // Safety: We have the same safety requirements as
+ // `BorrowedParcel::set_data_position`.
+ unsafe { self.borrowed_ref().set_data_position(pos) }
}
/// Append a subset of another parcel.
@@ -461,7 +454,7 @@
/// and call a closure with the sub-parcel as its parameter.
/// The closure can keep reading data from the sub-parcel
/// until it runs out of input data. The closure is responsible
- /// for calling [`ReadableSubParcel::has_more_data`] to check for
+ /// for calling `ReadableSubParcel::has_more_data` to check for
/// more data before every read, at least until Rust generators
/// are stabilized.
/// After the closure returns, skip to the end of the current
@@ -504,7 +497,10 @@
f(subparcel)?;
// Advance the data position to the actual end,
- // in case the closure read less data than was available
+ // in case the closure read less data than was available.
+ //
+ // Safety: end must be less than the current size of the parcel, because
+ // we checked above against `get_data_size`.
unsafe {
self.set_data_position(end)?;
}
@@ -595,7 +591,7 @@
/// and call a closure with the sub-parcel as its parameter.
/// The closure can keep reading data from the sub-parcel
/// until it runs out of input data. The closure is responsible
- /// for calling [`ReadableSubParcel::has_more_data`] to check for
+ /// for calling `ReadableSubParcel::has_more_data` to check for
/// more data before every read, at least until Rust generators
/// are stabilized.
/// After the closure returns, skip to the end of the current
@@ -649,17 +645,17 @@
// Internal APIs
impl<'a> BorrowedParcel<'a> {
pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
+ // null or a valid pointer to an `AIBinder`, both of which are
+ // valid, safe inputs to `AParcel_writeStrongBinder`.
+ //
+ // This call does not take ownership of the binder. However, it does
+ // require a mutable pointer, which we cannot extract from an
+ // immutable reference, so we clone the binder, incrementing the
+ // refcount before the call. The refcount will be immediately
+ // decremented when this temporary is dropped.
unsafe {
- // Safety: `BorrowedParcel` always contains a valid pointer to an
- // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
- // null or a valid pointer to an `AIBinder`, both of which are
- // valid, safe inputs to `AParcel_writeStrongBinder`.
- //
- // This call does not take ownership of the binder. However, it does
- // require a mutable pointer, which we cannot extract from an
- // immutable reference, so we clone the binder, incrementing the
- // refcount before the call. The refcount will be immediately
- // decremented when this temporary is dropped.
status_result(sys::AParcel_writeStrongBinder(
self.as_native_mut(),
binder.cloned().as_native_mut(),
@@ -669,33 +665,28 @@
pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> {
let mut binder = ptr::null_mut();
- let status = unsafe {
- // Safety: `BorrowedParcel` always contains a valid pointer to an
- // `AParcel`. We pass a valid, mutable out pointer to the `binder`
- // parameter. After this call, `binder` will be either null or a
- // valid pointer to an `AIBinder` owned by the caller.
- sys::AParcel_readStrongBinder(self.as_native(), &mut binder)
- };
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid, mutable out pointer to the `binder`
+ // parameter. After this call, `binder` will be either null or a
+ // valid pointer to an `AIBinder` owned by the caller.
+ let status = unsafe { sys::AParcel_readStrongBinder(self.as_native(), &mut binder) };
status_result(status)?;
- Ok(unsafe {
- // Safety: `binder` is either null or a valid, owned pointer at this
- // point, so can be safely passed to `SpIBinder::from_raw`.
- SpIBinder::from_raw(binder)
- })
+ // Safety: `binder` is either null or a valid, owned pointer at this
+ // point, so can be safely passed to `SpIBinder::from_raw`.
+ Ok(unsafe { SpIBinder::from_raw(binder) })
}
}
impl Drop for Parcel {
fn drop(&mut self) {
// Run the C++ Parcel complete object destructor
- unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. Since we own the parcel, we can safely delete it
- // here.
- sys::AParcel_delete(self.ptr.as_ptr())
- }
+ //
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. Since we own the parcel, we can safely delete it
+ // here.
+ unsafe { sys::AParcel_delete(self.ptr.as_ptr()) }
}
}
@@ -732,6 +723,8 @@
parcel.write(&1i32).unwrap();
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
parcel.set_data_position(start).unwrap();
}
@@ -748,6 +741,8 @@
parcel.write(&b"Hello, Binder!\0"[..]).unwrap();
// Skip over string length
+ // SAFETY: str_start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(str_start).is_ok());
}
@@ -756,42 +751,56 @@
assert!(parcel.read::<bool>().unwrap());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<i8>().unwrap(), 72i8);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<u16>().unwrap(), 25928);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<i32>().unwrap(), 1819043144);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<u32>().unwrap(), 1819043144);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<i64>().unwrap(), 4764857262830019912);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<u64>().unwrap(), 4764857262830019912);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -799,6 +808,8 @@
assert_eq!(parcel.read::<f32>().unwrap(), 1143139100000000000000000000.0);
assert_eq!(parcel.read::<f32>().unwrap(), 40.043392);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -806,6 +817,8 @@
assert_eq!(parcel.read::<f64>().unwrap(), 34732488246.197815);
// Skip back to before the string length
+ // SAFETY: str_start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(str_start).is_ok());
}
@@ -819,15 +832,21 @@
let start = parcel.get_data_position();
assert!(parcel.write("Hello, Binder!").is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<Option<String>>().unwrap().unwrap(), "Hello, Binder!",);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(parcel.write("Embedded null \0 inside a string").is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -835,6 +854,8 @@
parcel.read::<Option<String>>().unwrap().unwrap(),
"Embedded null \0 inside a string",
);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -849,6 +870,8 @@
let s3 = "Some more text here.";
assert!(parcel.write(&[s1, s2, s3][..]).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -874,6 +897,8 @@
assert_eq!(parcel.get_data_position(), start + expected_len);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
parcel.set_data_position(start).unwrap();
}
@@ -893,6 +918,8 @@
assert_eq!(4, parcel2.get_data_size());
assert_eq!(Ok(()), parcel2.append_all_from(&parcel1));
assert_eq!(8, parcel2.get_data_size());
+ // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not
+ // empty.
unsafe {
parcel2.set_data_position(0).unwrap();
}
@@ -903,6 +930,8 @@
assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2));
assert_eq!(Ok(()), parcel2.append_from(&parcel1, 2, 2));
assert_eq!(4, parcel2.get_data_size());
+ // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not
+ // empty.
unsafe {
parcel2.set_data_position(0).unwrap();
}
@@ -911,6 +940,8 @@
let mut parcel2 = Parcel::new();
assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2));
assert_eq!(2, parcel2.get_data_size());
+ // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not
+ // empty.
unsafe {
parcel2.set_data_position(0).unwrap();
}
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 7fe37f3..5c688fa 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -73,14 +73,12 @@
impl Serialize for ParcelFileDescriptor {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let fd = self.0.as_raw_fd();
- let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a
- // valid file, so we can borrow a valid file
- // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take
- // ownership of the fd, so we need not duplicate it first.
- sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd)
- };
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a
+ // valid file, so we can borrow a valid file
+ // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take
+ // ownership of the fd, so we need not duplicate it first.
+ let status = unsafe { sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd) };
status_result(status)
}
}
@@ -92,13 +90,12 @@
if let Some(f) = this {
f.serialize(parcel)
} else {
- let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the
- // value `-1` as the file descriptor to signify serializing a
- // null file descriptor.
- sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32)
- };
+ let status =
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the
+ // value `-1` as the file descriptor to signify serializing a
+ // null file descriptor.
+ unsafe { sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32) };
status_result(status)
}
}
@@ -107,25 +104,23 @@
impl DeserializeOption for ParcelFileDescriptor {
fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
let mut fd = -1i32;
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid mutable pointer to an i32, which
+ // `AParcel_readParcelFileDescriptor` assigns the valid file
+ // descriptor into, or `-1` if deserializing a null file
+ // descriptor. The read function passes ownership of the file
+ // descriptor to its caller if it was non-null, so we must take
+ // ownership of the file and ensure that it is eventually closed.
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. We pass a valid mutable pointer to an i32, which
- // `AParcel_readParcelFileDescriptor` assigns the valid file
- // descriptor into, or `-1` if deserializing a null file
- // descriptor. The read function passes ownership of the file
- // descriptor to its caller if it was non-null, so we must take
- // ownership of the file and ensure that it is eventually closed.
status_result(sys::AParcel_readParcelFileDescriptor(parcel.as_native(), &mut fd))?;
}
if fd < 0 {
Ok(None)
} else {
- let file = unsafe {
- // Safety: At this point, we know that the file descriptor was
- // not -1, so must be a valid, owned file descriptor which we
- // can safely turn into a `File`.
- File::from_raw_fd(fd)
- };
+ // Safety: At this point, we know that the file descriptor was
+ // not -1, so must be a valid, owned file descriptor which we
+ // can safely turn into a `File`.
+ let file = unsafe { File::from_raw_fd(fd) };
Ok(Some(ParcelFileDescriptor::new(file)))
}
}
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 5d8c11c..9008a3c 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -50,14 +50,14 @@
fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>;
}
-/// A struct whose instances can be written to a [`Parcel`].
+/// A struct whose instances can be written to a [`crate::parcel::Parcel`].
// Might be able to hook this up as a serde backend in the future?
pub trait Serialize {
- /// Serialize this instance into the given [`Parcel`].
+ /// Serialize this instance into the given [`crate::parcel::Parcel`].
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>;
}
-/// A struct whose instances can be restored from a [`Parcel`].
+/// A struct whose instances can be restored from a [`crate::parcel::Parcel`].
// Might be able to hook this up as a serde backend in the future?
pub trait Deserialize: Sized {
/// Type for the uninitialized value of this type. Will be either `Self`
@@ -80,10 +80,10 @@
/// Convert an initialized value of type `Self` into `Self::UninitType`.
fn from_init(value: Self) -> Self::UninitType;
- /// Deserialize an instance from the given [`Parcel`].
+ /// Deserialize an instance from the given [`crate::parcel::Parcel`].
fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>;
- /// Deserialize an instance from the given [`Parcel`] onto the
+ /// Deserialize an instance from the given [`crate::parcel::Parcel`] onto the
/// current object. This operation will overwrite the old value
/// partially or completely, depending on how much data is available.
fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
@@ -102,8 +102,8 @@
pub trait SerializeArray: Serialize + Sized {
/// Serialize an array of this type into the given parcel.
fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ // Safety: Safe FFI, slice will always be a safe pointer to pass.
let res = unsafe {
- // Safety: Safe FFI, slice will always be a safe pointer to pass.
sys::AParcel_writeParcelableArray(
parcel.as_native_mut(),
slice.as_ptr() as *const c_void,
@@ -117,7 +117,9 @@
/// Callback to serialize an element of a generic parcelable array.
///
-/// Safety: We are relying on binder_ndk to not overrun our slice. As long as it
+/// # Safety
+///
+/// We are relying on binder_ndk to not overrun our slice. As long as it
/// doesn't provide an index larger than the length of the original slice in
/// serialize_array, this operation is safe. The index provided is zero-based.
unsafe extern "C" fn serialize_element<T: Serialize>(
@@ -125,9 +127,14 @@
array: *const c_void,
index: usize,
) -> status_t {
- let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1);
+ // Safety: The caller guarantees that `array` is a valid pointer of the
+ // appropriate type.
+ let slice: &[T] = unsafe { slice::from_raw_parts(array.cast(), index + 1) };
- let mut parcel = match BorrowedParcel::from_raw(parcel) {
+ // Safety: The caller must give us a parcel pointer which is either null or
+ // valid at least for the duration of this function call. We don't keep the
+ // resulting value beyond the function.
+ let mut parcel = match unsafe { BorrowedParcel::from_raw(parcel) } {
None => return StatusCode::UNEXPECTED_NULL as status_t,
Some(p) => p,
};
@@ -142,9 +149,9 @@
/// Deserialize an array of type from the given parcel.
fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
let mut vec: Option<Vec<Self::UninitType>> = None;
+ // Safety: Safe FFI, vec is the correct opaque type expected by
+ // allocate_vec and deserialize_element.
let res = unsafe {
- // Safety: Safe FFI, vec is the correct opaque type expected by
- // allocate_vec and deserialize_element.
sys::AParcel_readParcelableArray(
parcel.as_native(),
&mut vec as *mut _ as *mut c_void,
@@ -153,21 +160,21 @@
)
};
status_result(res)?;
- let vec: Option<Vec<Self>> = unsafe {
- // Safety: We are assuming that the NDK correctly initialized every
- // element of the vector by now, so we know that all the
- // UninitTypes are now properly initialized. We can transmute from
- // Vec<T::UninitType> to Vec<T> because T::UninitType has the same
- // alignment and size as T, so the pointer to the vector allocation
- // will be compatible.
- mem::transmute(vec)
- };
+ // Safety: We are assuming that the NDK correctly initialized every
+ // element of the vector by now, so we know that all the
+ // UninitTypes are now properly initialized. We can transmute from
+ // Vec<T::UninitType> to Vec<T> because T::UninitType has the same
+ // alignment and size as T, so the pointer to the vector allocation
+ // will be compatible.
+ let vec: Option<Vec<Self>> = unsafe { mem::transmute(vec) };
Ok(vec)
}
}
/// Callback to deserialize a parcelable element.
///
+/// # Safety
+///
/// The opaque array data pointer must be a mutable pointer to an
/// `Option<Vec<T::UninitType>>` with at least enough elements for `index` to be valid
/// (zero-based).
@@ -176,13 +183,18 @@
array: *mut c_void,
index: usize,
) -> status_t {
- let vec = &mut *(array as *mut Option<Vec<T::UninitType>>);
+ // Safety: The caller guarantees that `array` is a valid pointer of the
+ // appropriate type.
+ let vec = unsafe { &mut *(array as *mut Option<Vec<T::UninitType>>) };
let vec = match vec {
Some(v) => v,
None => return StatusCode::BAD_INDEX as status_t,
};
- let parcel = match BorrowedParcel::from_raw(parcel as *mut _) {
+ // Safety: The caller must give us a parcel pointer which is either null or
+ // valid at least for the duration of this function call. We don't keep the
+ // resulting value beyond the function.
+ let parcel = match unsafe { BorrowedParcel::from_raw(parcel as *mut _) } {
None => return StatusCode::UNEXPECTED_NULL as status_t,
Some(p) => p,
};
@@ -254,16 +266,21 @@
///
/// The opaque data pointer passed to the array read function must be a mutable
/// pointer to an `Option<Vec<T::UninitType>>`. `buffer` will be assigned a mutable pointer
-/// to the allocated vector data if this function returns true.
+/// to the allocated vector data if this function returns true. `buffer` must be a valid pointer.
unsafe extern "C" fn allocate_vec_with_buffer<T: Deserialize>(
data: *mut c_void,
len: i32,
buffer: *mut *mut T,
) -> bool {
- let res = allocate_vec::<T>(data, len);
- let vec = &mut *(data as *mut Option<Vec<T::UninitType>>);
+ // Safety: We have the same safety requirements as `allocate_vec` for `data`.
+ let res = unsafe { allocate_vec::<T>(data, len) };
+ // Safety: The caller guarantees that `data` is a valid mutable pointer to the appropriate type.
+ let vec = unsafe { &mut *(data as *mut Option<Vec<T::UninitType>>) };
if let Some(new_vec) = vec {
- *buffer = new_vec.as_mut_ptr() as *mut T;
+ // Safety: The caller guarantees that `buffer` is a valid pointer.
+ unsafe {
+ *buffer = new_vec.as_mut_ptr() as *mut T;
+ }
}
res
}
@@ -275,7 +292,8 @@
/// The opaque data pointer passed to the array read function must be a mutable
/// pointer to an `Option<Vec<T::UninitType>>`.
unsafe extern "C" fn allocate_vec<T: Deserialize>(data: *mut c_void, len: i32) -> bool {
- let vec = &mut *(data as *mut Option<Vec<T::UninitType>>);
+ // Safety: The caller guarantees that `data` is a valid mutable pointer to the appropriate type.
+ let vec = unsafe { &mut *(data as *mut Option<Vec<T::UninitType>>) };
if len < 0 {
*vec = None;
return true;
@@ -286,7 +304,10 @@
let mut new_vec: Vec<T::UninitType> = Vec::with_capacity(len as usize);
new_vec.resize_with(len as usize, T::uninit);
- ptr::write(vec, Some(new_vec));
+ // Safety: The caller guarantees that vec is a valid mutable pointer to the appropriate type.
+ unsafe {
+ ptr::write(vec, Some(new_vec));
+ }
true
}
@@ -305,21 +326,21 @@
// Assert at compile time that `T` and `T::UninitType` have the same size and alignment.
let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT;
- // We can convert from Vec<T::UninitType> to Vec<T> because T::UninitType
- // has the same alignment and size as T, so the pointer to the vector
- // allocation will be compatible.
let mut vec = ManuallyDrop::new(vec);
- Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity())
+ // Safety: We can convert from Vec<T::UninitType> to Vec<T> because
+ // T::UninitType has the same alignment and size as T, so the pointer to the
+ // vector allocation will be compatible.
+ unsafe { Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) }
}
macro_rules! impl_parcelable {
{Serialize, $ty:ty, $write_fn:path} => {
impl Serialize for $ty {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`, and any `$ty` literal value is safe to pass to
+ // `$write_fn`.
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`, and any `$ty` literal value is safe to pass to
- // `$write_fn`.
status_result($write_fn(parcel.as_native_mut(), *self))
}
}
@@ -333,11 +354,11 @@
fn from_init(value: Self) -> Self::UninitType { value }
fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut val = Self::default();
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid, mutable pointer to `val`, a
+ // literal of type `$ty`, and `$read_fn` will write the
+ // value read into `val` if successful
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. We pass a valid, mutable pointer to `val`, a
- // literal of type `$ty`, and `$read_fn` will write the
- // value read into `val` if successful
status_result($read_fn(parcel.as_native(), &mut val))?
};
Ok(val)
@@ -348,13 +369,13 @@
{SerializeArray, $ty:ty, $write_array_fn:path} => {
impl SerializeArray for $ty {
fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
+ // will be a valid pointer to an array of elements of type
+ // `$ty`. If the slice length is 0, `slice.as_ptr()` may be
+ // dangling, but this is safe since the pointer is not
+ // dereferenced if the length parameter is 0.
let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
- // will be a valid pointer to an array of elements of type
- // `$ty`. If the slice length is 0, `slice.as_ptr()` may be
- // dangling, but this is safe since the pointer is not
- // dereferenced if the length parameter is 0.
$write_array_fn(
parcel.as_native_mut(),
slice.as_ptr(),
@@ -373,11 +394,11 @@
impl DeserializeArray for $ty {
fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
let mut vec: Option<Vec<Self::UninitType>> = None;
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `allocate_vec<T>` expects the opaque pointer to
+ // be of type `*mut Option<Vec<T::UninitType>>`, so `&mut vec` is
+ // correct for it.
let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. `allocate_vec<T>` expects the opaque pointer to
- // be of type `*mut Option<Vec<T::UninitType>>`, so `&mut vec` is
- // correct for it.
$read_array_fn(
parcel.as_native(),
&mut vec as *mut _ as *mut c_void,
@@ -385,11 +406,11 @@
)
};
status_result(status)?;
+ // Safety: We are assuming that the NDK correctly
+ // initialized every element of the vector by now, so we
+ // know that all the UninitTypes are now properly
+ // initialized.
let vec: Option<Vec<Self>> = unsafe {
- // Safety: We are assuming that the NDK correctly
- // initialized every element of the vector by now, so we
- // know that all the UninitTypes are now properly
- // initialized.
vec.map(|vec| vec_assume_init(vec))
};
Ok(vec)
@@ -479,13 +500,13 @@
impl SerializeArray for u8 {
fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+ // valid pointer to an array of elements of type `$ty`. If the slice
+ // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+ // since the pointer is not dereferenced if the length parameter is
+ // 0.
let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
- // valid pointer to an array of elements of type `$ty`. If the slice
- // length is 0, `slice.as_ptr()` may be dangling, but this is safe
- // since the pointer is not dereferenced if the length parameter is
- // 0.
sys::AParcel_writeByteArray(
parcel.as_native_mut(),
slice.as_ptr() as *const i8,
@@ -518,13 +539,13 @@
impl SerializeArray for i16 {
fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+ // valid pointer to an array of elements of type `$ty`. If the slice
+ // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+ // since the pointer is not dereferenced if the length parameter is
+ // 0.
let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
- // valid pointer to an array of elements of type `$ty`. If the slice
- // length is 0, `slice.as_ptr()` may be dangling, but this is safe
- // since the pointer is not dereferenced if the length parameter is
- // 0.
sys::AParcel_writeCharArray(
parcel.as_native_mut(),
slice.as_ptr() as *const u16,
@@ -538,22 +559,22 @@
impl SerializeOption for str {
fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
match this {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the string pointer is null,
+ // `AParcel_writeString` requires that the length is -1 to
+ // indicate that we want to serialize a null string.
None => unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. If the string pointer is null,
- // `AParcel_writeString` requires that the length is -1 to
- // indicate that we want to serialize a null string.
status_result(sys::AParcel_writeString(parcel.as_native_mut(), ptr::null(), -1))
},
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8
+ // string pointer of `length` bytes, which is what str in Rust
+ // is. The docstring for `AParcel_writeString` says that the
+ // string input should be null-terminated, but it doesn't
+ // actually rely on that fact in the code. If this ever becomes
+ // necessary, we will need to null-terminate the str buffer
+ // before sending it.
Some(s) => unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8
- // string pointer of `length` bytes, which is what str in Rust
- // is. The docstring for `AParcel_writeString` says that the
- // string input should be null-terminated, but it doesn't
- // actually rely on that fact in the code. If this ever becomes
- // necessary, we will need to null-terminate the str buffer
- // before sending it.
status_result(sys::AParcel_writeString(
parcel.as_native_mut(),
s.as_ptr() as *const c_char,
@@ -597,11 +618,11 @@
fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut vec: Option<Vec<u8>> = None;
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`.
+ // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>`
+ // for `allocate_vec`, so `vec` is safe to pass as the opaque data
+ // pointer on platforms where char is signed.
let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an `AParcel`.
- // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>`
- // for `allocate_vec`, so `vec` is safe to pass as the opaque data
- // pointer on platforms where char is signed.
sys::AParcel_readString(
parcel.as_native(),
&mut vec as *mut _ as *mut c_void,
@@ -751,11 +772,11 @@
impl Serialize for Status {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`
+ // and `Status` always contains a valid pointer to an `AStatus`, so
+ // both parameters are valid and safe. This call does not take
+ // ownership of either of its parameters.
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an `AParcel`
- // and `Status` always contains a valid pointer to an `AStatus`, so
- // both parameters are valid and safe. This call does not take
- // ownership of either of its parameters.
status_result(sys::AParcel_writeStatusHeader(parcel.as_native_mut(), self.as_native()))
}
}
@@ -772,21 +793,18 @@
fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut status_ptr = ptr::null_mut();
- let ret_status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. We pass a mutable out pointer which will be
- // assigned a valid `AStatus` pointer if the function returns
- // status OK. This function passes ownership of the status
- // pointer to the caller, if it was assigned.
- sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr)
- };
+ let ret_status =
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a mutable out pointer which will be
+ // assigned a valid `AStatus` pointer if the function returns
+ // status OK. This function passes ownership of the status
+ // pointer to the caller, if it was assigned.
+ unsafe { sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr) };
status_result(ret_status)?;
- Ok(unsafe {
- // Safety: At this point, the return status of the read call was ok,
- // so we know that `status_ptr` is a valid, owned pointer to an
- // `AStatus`, from which we can safely construct a `Status` object.
- Status::from_ptr(status_ptr)
- })
+ // Safety: At this point, the return status of the read call was ok,
+ // so we know that `status_ptr` is a valid, owned pointer to an
+ // `AStatus`, from which we can safely construct a `Status` object.
+ Ok(unsafe { Status::from_ptr(status_ptr) })
}
}
@@ -880,7 +898,6 @@
/// `Serialize`, `SerializeArray` and `SerializeOption` for
/// structured parcelables. The target type must implement the
/// `Parcelable` trait.
-/// ```
#[macro_export]
macro_rules! impl_serialize_for_parcelable {
($parcelable:ident) => {
@@ -1070,6 +1087,8 @@
assert!(custom.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1092,6 +1111,8 @@
assert!(bools.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1101,6 +1122,8 @@
assert_eq!(parcel.read::<u32>().unwrap(), 0);
assert_eq!(parcel.read::<u32>().unwrap(), 0);
assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1116,12 +1139,17 @@
assert!(parcel.write(&u8s[..]).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes
+
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1131,18 +1159,25 @@
let i8s = [-128i8, 127, 42, -117];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(parcel.write(&i8s[..]).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes
+
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1152,10 +1187,14 @@
let u16s = [u16::max_value(), 12_345, 42, 117];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(u16s.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1165,6 +1204,9 @@
assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1175,10 +1217,14 @@
let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(i16s.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1188,6 +1234,9 @@
assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
+
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1198,10 +1247,14 @@
let u32s = [u32::max_value(), 12_345, 42, 117];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(u32s.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1211,6 +1264,9 @@
assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1221,10 +1277,14 @@
let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(i32s.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1234,6 +1294,9 @@
assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
+
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1244,10 +1307,14 @@
let u64s = [u64::max_value(), 12_345, 42, 117];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(u64s.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1258,10 +1325,14 @@
let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(i64s.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1272,10 +1343,14 @@
let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(f32s.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1288,10 +1363,14 @@
let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(f64s.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
@@ -1309,10 +1388,14 @@
let strs = [s1, s2, s3, s4];
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
assert!(strs.serialize(&mut parcel.borrowed()).is_ok());
+ // SAFETY: start is less than the current size of the parcel data buffer, because we haven't
+ // made it any shorter since we got the position.
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index 383cc83..f906113 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -133,8 +133,8 @@
}
}
ParcelableHolderData::Parcel(ref mut parcel) => {
+ // Safety: 0 should always be a valid position.
unsafe {
- // Safety: 0 should always be a valid position.
parcel.set_data_position(0)?;
}
@@ -161,6 +161,15 @@
}
}
+impl Clone for ParcelableHolder {
+ fn clone(&self) -> ParcelableHolder {
+ ParcelableHolder {
+ data: Mutex::new(self.data.lock().unwrap().clone()),
+ stability: self.stability,
+ }
+ }
+}
+
impl Serialize for ParcelableHolder {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
@@ -205,15 +214,15 @@
parcelable.write_to_parcel(parcel)?;
let end = parcel.get_data_position();
+ // Safety: we got the position from `get_data_position`.
unsafe {
- // Safety: we got the position from `get_data_position`.
parcel.set_data_position(length_start)?;
}
assert!(end >= data_start);
parcel.write(&(end - data_start))?;
+ // Safety: we got the position from `get_data_position`.
unsafe {
- // Safety: we got the position from `get_data_position`.
parcel.set_data_position(end)?;
}
@@ -251,11 +260,11 @@
new_parcel.append_from(parcel, data_start, data_size)?;
*self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel);
+ // Safety: `append_from` checks if `data_size` overflows
+ // `parcel` and returns `BAD_VALUE` if that happens. We also
+ // explicitly check for negative and zero `data_size` above,
+ // so `data_end` is guaranteed to be greater than `data_start`.
unsafe {
- // Safety: `append_from` checks if `data_size` overflows
- // `parcel` and returns `BAD_VALUE` if that happens. We also
- // explicitly check for negative and zero `data_size` above,
- // so `data_end` is guaranteed to be greater than `data_start`.
parcel.set_data_position(data_end)?;
}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 036f6b4..dad3379 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -49,14 +49,12 @@
}
}
-/// # Safety
-///
-/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe
+/// Safety: An `SpIBinder` is an immutable handle to a C++ IBinder, which is
+/// thread-safe.
unsafe impl Send for SpIBinder {}
-/// # Safety
-///
-/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe
+/// Safety: An `SpIBinder` is an immutable handle to a C++ IBinder, which is
+/// thread-safe.
unsafe impl Sync for SpIBinder {}
impl SpIBinder {
@@ -97,11 +95,9 @@
/// Return true if this binder object is hosted in a different process than
/// the current one.
pub fn is_remote(&self) -> bool {
- unsafe {
- // Safety: `SpIBinder` guarantees that it always contains a valid
- // `AIBinder` pointer.
- sys::AIBinder_isRemote(self.as_native())
- }
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer.
+ unsafe { sys::AIBinder_isRemote(self.as_native()) }
}
/// Try to convert this Binder object into a trait object for the given
@@ -116,12 +112,12 @@
/// Return the interface class of this binder object, if associated with
/// one.
pub fn get_class(&mut self) -> Option<InterfaceClass> {
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer. `AIBinder_getClass` returns either a null
+ // pointer or a valid pointer to an `AIBinder_Class`. After mapping
+ // null to None, we can safely construct an `InterfaceClass` if the
+ // pointer was non-null.
unsafe {
- // Safety: `SpIBinder` guarantees that it always contains a valid
- // `AIBinder` pointer. `AIBinder_getClass` returns either a null
- // pointer or a valid pointer to an `AIBinder_Class`. After mapping
- // null to None, we can safely construct an `InterfaceClass` if the
- // pointer was non-null.
let class = sys::AIBinder_getClass(self.as_native_mut());
class.as_ref().map(|p| InterfaceClass::from_ptr(p))
}
@@ -152,7 +148,8 @@
///
/// See `SpIBinder::from_raw`.
pub unsafe fn new_spibinder(ptr: *mut sys::AIBinder) -> Option<SpIBinder> {
- SpIBinder::from_raw(ptr)
+ // Safety: The caller makes the same guarantees as this requires.
+ unsafe { SpIBinder::from_raw(ptr) }
}
}
@@ -171,30 +168,24 @@
impl AssociateClass for SpIBinder {
fn associate_class(&mut self, class: InterfaceClass) -> bool {
- unsafe {
- // Safety: `SpIBinder` guarantees that it always contains a valid
- // `AIBinder` pointer. An `InterfaceClass` can always be converted
- // into a valid `AIBinder_Class` pointer, so these parameters are
- // always safe.
- sys::AIBinder_associateClass(self.as_native_mut(), class.into())
- }
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer. An `InterfaceClass` can always be converted
+ // into a valid `AIBinder_Class` pointer, so these parameters are
+ // always safe.
+ unsafe { sys::AIBinder_associateClass(self.as_native_mut(), class.into()) }
}
}
impl Ord for SpIBinder {
fn cmp(&self, other: &Self) -> Ordering {
- let less_than = unsafe {
- // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
- // this pointer is always safe to pass to `AIBinder_lt` (null is
- // also safe to pass to this function, but we should never do that).
- sys::AIBinder_lt(self.0.as_ptr(), other.0.as_ptr())
- };
- let greater_than = unsafe {
- // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
- // this pointer is always safe to pass to `AIBinder_lt` (null is
- // also safe to pass to this function, but we should never do that).
- sys::AIBinder_lt(other.0.as_ptr(), self.0.as_ptr())
- };
+ // Safety: SpIBinder always holds a valid `AIBinder` pointer, so this
+ // pointer is always safe to pass to `AIBinder_lt` (null is also safe to
+ // pass to this function, but we should never do that).
+ let less_than = unsafe { sys::AIBinder_lt(self.0.as_ptr(), other.0.as_ptr()) };
+ // Safety: SpIBinder always holds a valid `AIBinder` pointer, so this
+ // pointer is always safe to pass to `AIBinder_lt` (null is also safe to
+ // pass to this function, but we should never do that).
+ let greater_than = unsafe { sys::AIBinder_lt(other.0.as_ptr(), self.0.as_ptr()) };
if !less_than && !greater_than {
Ordering::Equal
} else if less_than {
@@ -221,10 +212,10 @@
impl Clone for SpIBinder {
fn clone(&self) -> Self {
+ // Safety: Cloning a strong reference must increment the reference
+ // count. We are guaranteed by the `SpIBinder` constructor
+ // invariants that `self.0` is always a valid `AIBinder` pointer.
unsafe {
- // Safety: Cloning a strong reference must increment the reference
- // count. We are guaranteed by the `SpIBinder` constructor
- // invariants that `self.0` is always a valid `AIBinder` pointer.
sys::AIBinder_incStrong(self.0.as_ptr());
}
Self(self.0)
@@ -235,9 +226,9 @@
// We hold a strong reference to the IBinder in SpIBinder and need to give up
// this reference on drop.
fn drop(&mut self) {
+ // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we
+ // know this pointer is safe to pass to `AIBinder_decStrong` here.
unsafe {
- // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we
- // know this pointer is safe to pass to `AIBinder_decStrong` here.
sys::AIBinder_decStrong(self.as_native_mut());
}
}
@@ -246,26 +237,24 @@
impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
fn prepare_transact(&self) -> Result<Parcel> {
let mut input = ptr::null_mut();
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. It is safe to cast from an
+ // immutable pointer to a mutable pointer here, because
+ // `AIBinder_prepareTransaction` only calls immutable `AIBinder`
+ // methods but the parameter is unfortunately not marked as const.
+ //
+ // After the call, input will be either a valid, owned `AParcel`
+ // pointer, or null.
let status = unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`. It is safe to cast from an
- // immutable pointer to a mutable pointer here, because
- // `AIBinder_prepareTransaction` only calls immutable `AIBinder`
- // methods but the parameter is unfortunately not marked as const.
- //
- // After the call, input will be either a valid, owned `AParcel`
- // pointer, or null.
sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input)
};
status_result(status)?;
- unsafe {
- // Safety: At this point, `input` is either a valid, owned `AParcel`
- // pointer, or null. `OwnedParcel::from_raw` safely handles both cases,
- // taking ownership of the parcel.
- Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL)
- }
+ // Safety: At this point, `input` is either a valid, owned `AParcel`
+ // pointer, or null. `OwnedParcel::from_raw` safely handles both cases,
+ // taking ownership of the parcel.
+ unsafe { Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) }
}
fn submit_transact(
@@ -275,23 +264,23 @@
flags: TransactionFlags,
) -> Result<Parcel> {
let mut reply = ptr::null_mut();
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. Although `IBinder::transact` is
+ // not a const method, it is still safe to cast our immutable
+ // pointer to mutable for the call. First, `IBinder::transact` is
+ // thread-safe, so concurrency is not an issue. The only way that
+ // `transact` can affect any visible, mutable state in the current
+ // process is by calling `onTransact` for a local service. However,
+ // in order for transactions to be thread-safe, this method must
+ // dynamically lock its data before modifying it. We enforce this
+ // property in Rust by requiring `Sync` for remotable objects and
+ // only providing `on_transact` with an immutable reference to
+ // `self`.
+ //
+ // This call takes ownership of the `data` parcel pointer, and
+ // passes ownership of the `reply` out parameter to its caller. It
+ // does not affect ownership of the `binder` parameter.
let status = unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`. Although `IBinder::transact` is
- // not a const method, it is still safe to cast our immutable
- // pointer to mutable for the call. First, `IBinder::transact` is
- // thread-safe, so concurrency is not an issue. The only way that
- // `transact` can affect any visible, mutable state in the current
- // process is by calling `onTransact` for a local service. However,
- // in order for transactions to be thread-safe, this method must
- // dynamically lock its data before modifying it. We enforce this
- // property in Rust by requiring `Sync` for remotable objects and
- // only providing `on_transact` with an immutable reference to
- // `self`.
- //
- // This call takes ownership of the `data` parcel pointer, and
- // passes ownership of the `reply` out parameter to its caller. It
- // does not affect ownership of the `binder` parameter.
sys::AIBinder_transact(
self.as_native() as *mut sys::AIBinder,
code,
@@ -302,45 +291,45 @@
};
status_result(status)?;
- unsafe {
- // Safety: `reply` is either a valid `AParcel` pointer or null
- // after the call to `AIBinder_transact` above, so we can
- // construct a `Parcel` out of it. `AIBinder_transact` passes
- // ownership of the `reply` parcel to Rust, so we need to
- // construct an owned variant.
- Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL)
- }
+ // Safety: `reply` is either a valid `AParcel` pointer or null
+ // after the call to `AIBinder_transact` above, so we can
+ // construct a `Parcel` out of it. `AIBinder_transact` passes
+ // ownership of the `reply` parcel to Rust, so we need to
+ // construct an owned variant.
+ unsafe { Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) }
}
fn is_binder_alive(&self) -> bool {
- unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`.
- //
- // This call does not affect ownership of its pointer parameter.
- sys::AIBinder_isAlive(self.as_native())
- }
+ // Safety: `SpIBinder` guarantees that `self` always contains a valid
+ // pointer to an `AIBinder`.
+ //
+ // This call does not affect ownership of its pointer parameter.
+ unsafe { sys::AIBinder_isAlive(self.as_native()) }
}
#[cfg(not(android_vndk))]
fn set_requesting_sid(&mut self, enable: bool) {
+ // Safety: `SpIBinder` guarantees that `self` always contains a valid
+ // pointer to an `AIBinder`.
+ //
+ // This call does not affect ownership of its pointer parameter.
unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
}
fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> {
let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect();
let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect();
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the
+ // file descriptor parameter is always be a valid open file. The
+ // `args` pointer parameter is a valid pointer to an array of C
+ // strings that will outlive the call since `args` lives for the
+ // whole function scope.
+ //
+ // This call does not affect ownership of its binder pointer
+ // parameter and does not take ownership of the file or args array
+ // parameters.
let status = unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the
- // file descriptor parameter is always be a valid open file. The
- // `args` pointer parameter is a valid pointer to an array of C
- // strings that will outlive the call since `args` lives for the
- // whole function scope.
- //
- // This call does not affect ownership of its binder pointer
- // parameter and does not take ownership of the file or args array
- // parameters.
sys::AIBinder_dump(
self.as_native_mut(),
fp.as_raw_fd(),
@@ -353,22 +342,18 @@
fn get_extension(&mut self) -> Result<Option<SpIBinder>> {
let mut out = ptr::null_mut();
- let status = unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`. After this call, the `out`
- // parameter will be either null, or a valid pointer to an
- // `AIBinder`.
- //
- // This call passes ownership of the out pointer to its caller
- // (assuming it is set to a non-null value).
- sys::AIBinder_getExtension(self.as_native_mut(), &mut out)
- };
- let ibinder = unsafe {
- // Safety: The call above guarantees that `out` is either null or a
- // valid, owned pointer to an `AIBinder`, both of which are safe to
- // pass to `SpIBinder::from_raw`.
- SpIBinder::from_raw(out)
- };
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. After this call, the `out`
+ // parameter will be either null, or a valid pointer to an
+ // `AIBinder`.
+ //
+ // This call passes ownership of the out pointer to its caller
+ // (assuming it is set to a non-null value).
+ let status = unsafe { sys::AIBinder_getExtension(self.as_native_mut(), &mut out) };
+ // Safety: The call above guarantees that `out` is either null or a
+ // valid, owned pointer to an `AIBinder`, both of which are safe to
+ // pass to `SpIBinder::from_raw`.
+ let ibinder = unsafe { SpIBinder::from_raw(out) };
status_result(status)?;
Ok(ibinder)
@@ -377,17 +362,17 @@
impl<T: AsNative<sys::AIBinder>> IBinder for T {
fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `recipient` can always be
+ // converted into a valid pointer to an
+ // `AIBinder_DeathRecipient`.
+ //
+ // The cookie is also the correct pointer, and by calling new_cookie,
+ // we have created a new ref-count to the cookie, which linkToDeath
+ // takes ownership of. Once the DeathRecipient is unlinked for any
+ // reason (including if this call fails), the onUnlinked callback
+ // will consume that ref-count.
status_result(unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`. `recipient` can always be
- // converted into a valid pointer to an
- // `AIBinder_DeathRecipient`.
- //
- // The cookie is also the correct pointer, and by calling new_cookie,
- // we have created a new ref-count to the cookie, which linkToDeath
- // takes ownership of. Once the DeathRecipient is unlinked for any
- // reason (including if this call fails), the onUnlinked callback
- // will consume that ref-count.
sys::AIBinder_linkToDeath(
self.as_native_mut(),
recipient.as_native_mut(),
@@ -397,13 +382,13 @@
}
fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `recipient` can always be
+ // converted into a valid pointer to an
+ // `AIBinder_DeathRecipient`. Any value is safe to pass as the
+ // cookie, although we depend on this value being set by
+ // `get_cookie` when the death recipient callback is called.
status_result(unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`. `recipient` can always be
- // converted into a valid pointer to an
- // `AIBinder_DeathRecipient`. Any value is safe to pass as the
- // cookie, although we depend on this value being set by
- // `get_cookie` when the death recipient callback is called.
sys::AIBinder_unlinkToDeath(
self.as_native_mut(),
recipient.as_native_mut(),
@@ -413,13 +398,11 @@
}
fn ping_binder(&mut self) -> Result<()> {
- let status = unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`.
- //
- // This call does not affect ownership of its pointer parameter.
- sys::AIBinder_ping(self.as_native_mut())
- };
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`.
+ //
+ // This call does not affect ownership of its pointer parameter.
+ let status = unsafe { sys::AIBinder_ping(self.as_native_mut()) };
status_result(status)
}
}
@@ -472,35 +455,31 @@
}
}
-/// # Safety
-///
-/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe.
+/// Safety: A `WpIBinder` is an immutable handle to a C++ IBinder, which is
+/// thread-safe.
unsafe impl Send for WpIBinder {}
-/// # Safety
-///
-/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe.
+/// Safety: A `WpIBinder` is an immutable handle to a C++ IBinder, which is
+/// thread-safe.
unsafe impl Sync for WpIBinder {}
impl WpIBinder {
/// Create a new weak reference from an object that can be converted into a
/// raw `AIBinder` pointer.
fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder {
- let ptr = unsafe {
- // Safety: `SpIBinder` guarantees that `binder` always contains a
- // valid pointer to an `AIBinder`.
- sys::AIBinder_Weak_new(binder.as_native_mut())
- };
+ // Safety: `SpIBinder` guarantees that `binder` always contains a valid
+ // pointer to an `AIBinder`.
+ let ptr = unsafe { sys::AIBinder_Weak_new(binder.as_native_mut()) };
Self(ptr::NonNull::new(ptr).expect("Unexpected null pointer from AIBinder_Weak_new"))
}
/// Promote this weak reference to a strong reference to the binder object.
pub fn promote(&self) -> Option<SpIBinder> {
+ // Safety: `WpIBinder` always contains a valid weak reference, so we can
+ // pass this pointer to `AIBinder_Weak_promote`. Returns either null or
+ // an AIBinder owned by the caller, both of which are valid to pass to
+ // `SpIBinder::from_raw`.
unsafe {
- // Safety: `WpIBinder` always contains a valid weak reference, so we
- // can pass this pointer to `AIBinder_Weak_promote`. Returns either
- // null or an AIBinder owned by the caller, both of which are valid
- // to pass to `SpIBinder::from_raw`.
let ptr = sys::AIBinder_Weak_promote(self.0.as_ptr());
SpIBinder::from_raw(ptr)
}
@@ -509,35 +488,27 @@
impl Clone for WpIBinder {
fn clone(&self) -> Self {
- let ptr = unsafe {
- // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
- // so this pointer is always safe to pass to `AIBinder_Weak_clone`
- // (although null is also a safe value to pass to this API).
- //
- // We get ownership of the returned pointer, so can construct a new
- // WpIBinder object from it.
- sys::AIBinder_Weak_clone(self.0.as_ptr())
- };
+ // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so
+ // this pointer is always safe to pass to `AIBinder_Weak_clone`
+ // (although null is also a safe value to pass to this API).
+ //
+ // We get ownership of the returned pointer, so can construct a new
+ // WpIBinder object from it.
+ let ptr = unsafe { sys::AIBinder_Weak_clone(self.0.as_ptr()) };
Self(ptr::NonNull::new(ptr).expect("Unexpected null pointer from AIBinder_Weak_clone"))
}
}
impl Ord for WpIBinder {
fn cmp(&self, other: &Self) -> Ordering {
- let less_than = unsafe {
- // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
- // so this pointer is always safe to pass to `AIBinder_Weak_lt`
- // (null is also safe to pass to this function, but we should never
- // do that).
- sys::AIBinder_Weak_lt(self.0.as_ptr(), other.0.as_ptr())
- };
- let greater_than = unsafe {
- // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
- // so this pointer is always safe to pass to `AIBinder_Weak_lt`
- // (null is also safe to pass to this function, but we should never
- // do that).
- sys::AIBinder_Weak_lt(other.0.as_ptr(), self.0.as_ptr())
- };
+ // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so
+ // this pointer is always safe to pass to `AIBinder_Weak_lt` (null is
+ // also safe to pass to this function, but we should never do that).
+ let less_than = unsafe { sys::AIBinder_Weak_lt(self.0.as_ptr(), other.0.as_ptr()) };
+ // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so
+ // this pointer is always safe to pass to `AIBinder_Weak_lt` (null is
+ // also safe to pass to this function, but we should never do that).
+ let greater_than = unsafe { sys::AIBinder_Weak_lt(other.0.as_ptr(), self.0.as_ptr()) };
if !less_than && !greater_than {
Ordering::Equal
} else if less_than {
@@ -564,9 +535,9 @@
impl Drop for WpIBinder {
fn drop(&mut self) {
+ // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we
+ // know this pointer is safe to pass to `AIBinder_Weak_delete` here.
unsafe {
- // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we
- // know this pointer is safe to pass to `AIBinder_Weak_delete` here.
sys::AIBinder_Weak_delete(self.0.as_ptr());
}
}
@@ -574,7 +545,7 @@
/// Rust wrapper around DeathRecipient objects.
///
-/// The cookie in this struct represents an Arc<F> for the owned callback.
+/// The cookie in this struct represents an `Arc<F>` for the owned callback.
/// This struct owns a ref-count of it, and so does every binder that we
/// have been linked with.
///
@@ -592,17 +563,13 @@
cookie_decr_refcount: unsafe extern "C" fn(*mut c_void),
}
-/// # Safety
-///
-/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer
-/// to a `Fn` which is `Sync` and `Send` (the cookie field). As
+/// Safety: A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and
+/// a pointer to a `Fn` which is `Sync` and `Send` (the cookie field). As
/// `AIBinder_DeathRecipient` is threadsafe, this structure is too.
unsafe impl Send for DeathRecipient {}
-/// # Safety
-///
-/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer
-/// to a `Fn` which is `Sync` and `Send` (the cookie field). As
+/// Safety: A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and
+/// a pointer to a `Fn` which is `Sync` and `Send` (the cookie field). As
/// `AIBinder_DeathRecipient` is threadsafe, this structure is too.
unsafe impl Sync for DeathRecipient {}
@@ -614,19 +581,17 @@
F: Fn() + Send + Sync + 'static,
{
let callback: *const F = Arc::into_raw(Arc::new(callback));
- let recipient = unsafe {
- // Safety: The function pointer is a valid death recipient callback.
- //
- // This call returns an owned `AIBinder_DeathRecipient` pointer
- // which must be destroyed via `AIBinder_DeathRecipient_delete` when
- // no longer needed.
- sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
- };
+ // Safety: The function pointer is a valid death recipient callback.
+ //
+ // This call returns an owned `AIBinder_DeathRecipient` pointer which
+ // must be destroyed via `AIBinder_DeathRecipient_delete` when no longer
+ // needed.
+ let recipient = unsafe { sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>)) };
+ // Safety: The function pointer is a valid onUnlinked callback.
+ //
+ // All uses of linkToDeath in this file correctly increment the
+ // ref-count that this onUnlinked callback will decrement.
unsafe {
- // Safety: The function pointer is a valid onUnlinked callback.
- //
- // All uses of linkToDeath in this file correctly increment the
- // ref-count that this onUnlinked callback will decrement.
sys::AIBinder_DeathRecipient_setOnUnlinked(
recipient,
Some(Self::cookie_decr_refcount::<F>),
@@ -648,7 +613,12 @@
///
/// The caller must handle the returned ref-count correctly.
unsafe fn new_cookie(&self) -> *mut c_void {
- (self.vtable.cookie_incr_refcount)(self.cookie);
+ // Safety: `cookie_incr_refcount` points to
+ // `Self::cookie_incr_refcount`, and `self.cookie` is the cookie for an
+ // Arc<F>.
+ unsafe {
+ (self.vtable.cookie_incr_refcount)(self.cookie);
+ }
// Return a raw pointer with ownership of a ref-count
self.cookie
@@ -667,13 +637,14 @@
///
/// # Safety
///
- /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
/// the caller must hold a ref-count to it.
unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
where
F: Fn() + Send + Sync + 'static,
{
- let callback = (cookie as *const F).as_ref().unwrap();
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
callback();
}
@@ -682,34 +653,34 @@
///
/// # Safety
///
- /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
/// the owner must give up a ref-count to it.
unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn() + Send + Sync + 'static,
{
- drop(Arc::from_raw(cookie as *const F));
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ drop(unsafe { Arc::from_raw(cookie as *const F) });
}
/// Callback that increments the ref-count.
///
/// # Safety
///
- /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
/// the owner must handle the created ref-count properly.
unsafe extern "C" fn cookie_incr_refcount<F>(cookie: *mut c_void)
where
F: Fn() + Send + Sync + 'static,
{
- let arc = mem::ManuallyDrop::new(Arc::from_raw(cookie as *const F));
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ let arc = mem::ManuallyDrop::new(unsafe { Arc::from_raw(cookie as *const F) });
mem::forget(Arc::clone(&arc));
}
}
-/// # Safety
-///
-/// A `DeathRecipient` is always constructed with a valid raw pointer to an
-/// `AIBinder_DeathRecipient`, so it is always type-safe to extract this
+/// Safety: A `DeathRecipient` is always constructed with a valid raw pointer to
+/// an `AIBinder_DeathRecipient`, so it is always type-safe to extract this
/// pointer.
unsafe impl AsNative<sys::AIBinder_DeathRecipient> for DeathRecipient {
fn as_native(&self) -> *const sys::AIBinder_DeathRecipient {
@@ -723,18 +694,19 @@
impl Drop for DeathRecipient {
fn drop(&mut self) {
+ // Safety: `self.recipient` is always a valid, owned
+ // `AIBinder_DeathRecipient` pointer returned by
+ // `AIBinder_DeathRecipient_new` when `self` was created. This delete
+ // method can only be called once when `self` is dropped.
unsafe {
- // Safety: `self.recipient` is always a valid, owned
- // `AIBinder_DeathRecipient` pointer returned by
- // `AIBinder_DeathRecipient_new` when `self` was created. This
- // delete method can only be called once when `self` is dropped.
sys::AIBinder_DeathRecipient_delete(self.recipient);
+ }
- // Safety: We own a ref-count to the cookie, and so does every
- // linked binder. This call gives up our ref-count. The linked
- // binders should already have given up their ref-count, or should
- // do so shortly.
- (self.vtable.cookie_decr_refcount)(self.cookie)
+ // Safety: We own a ref-count to the cookie, and so does every linked
+ // binder. This call gives up our ref-count. The linked binders should
+ // already have given up their ref-count, or should do so shortly.
+ unsafe {
+ (self.vtable.cookie_decr_refcount)(self.cookie);
}
}
}
@@ -754,11 +726,9 @@
fn from_binder(binder: SpIBinder) -> Result<Self>;
}
-/// # Safety
-///
-/// This is a convenience method that wraps `AsNative` for `SpIBinder` to allow
-/// invocation of `IBinder` methods directly from `Interface` objects. It shares
-/// the same safety as the implementation for `SpIBinder`.
+/// Safety: This is a convenience method that wraps `AsNative` for `SpIBinder`
+/// to allow invocation of `IBinder` methods directly from `Interface` objects.
+/// It shares the same safety as the implementation for `SpIBinder`.
unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T {
fn as_native(&self) -> *const sys::AIBinder {
self.as_binder().as_native()
@@ -773,24 +743,20 @@
/// exist.
pub fn get_service(name: &str) -> Option<SpIBinder> {
let name = CString::new(name).ok()?;
- unsafe {
- // Safety: `AServiceManager_getService` returns either a null pointer or
- // a valid pointer to an owned `AIBinder`. Either of these values is
- // safe to pass to `SpIBinder::from_raw`.
- SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr()))
- }
+ // Safety: `AServiceManager_getService` returns either a null pointer or a
+ // valid pointer to an owned `AIBinder`. Either of these values is safe to
+ // pass to `SpIBinder::from_raw`.
+ unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) }
}
/// Retrieve an existing service, or start it if it is configured as a dynamic
/// service and isn't yet started.
pub fn wait_for_service(name: &str) -> Option<SpIBinder> {
let name = CString::new(name).ok()?;
- unsafe {
- // Safety: `AServiceManager_waitforService` returns either a null
- // pointer or a valid pointer to an owned `AIBinder`. Either of these
- // values is safe to pass to `SpIBinder::from_raw`.
- SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr()))
- }
+ // Safety: `AServiceManager_waitforService` returns either a null pointer or
+ // a valid pointer to an owned `AIBinder`. Either of these values is safe to
+ // pass to `SpIBinder::from_raw`.
+ unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) }
}
/// Retrieve an existing service for a particular interface, blocking for a few
@@ -809,12 +775,10 @@
pub fn is_declared(interface: &str) -> Result<bool> {
let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
- unsafe {
- // Safety: `interface` is a valid null-terminated C-style string and is
- // only borrowed for the lifetime of the call. The `interface` local
- // outlives this call as it lives for the function scope.
- Ok(sys::AServiceManager_isDeclared(interface.as_ptr()))
- }
+ // Safety: `interface` is a valid null-terminated C-style string and is only
+ // borrowed for the lifetime of the call. The `interface` local outlives
+ // this call as it lives for the function scope.
+ unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) }
}
/// Retrieve all declared instances for a particular interface
@@ -827,11 +791,13 @@
// CString, and outlives this callback. The null handling here is just
// to avoid the possibility of unwinding across C code if this crate is
// ever compiled with panic=unwind.
- if let Some(instances) = opaque.cast::<Vec<CString>>().as_mut() {
+ if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } {
// Safety: instance is a valid null-terminated C string with a
// lifetime at least as long as this function, and we immediately
// copy it into an owned CString.
- instances.push(CStr::from_ptr(instance).to_owned());
+ unsafe {
+ instances.push(CStr::from_ptr(instance).to_owned());
+ }
} else {
eprintln!("Opaque pointer was null in get_declared_instances callback!");
}
@@ -839,10 +805,10 @@
let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
let mut instances: Vec<CString> = vec![];
+ // Safety: `interface` and `instances` are borrowed for the length of this
+ // call and both outlive the call. `interface` is guaranteed to be a valid
+ // null-terminated C-style string.
unsafe {
- // Safety: `interface` and `instances` are borrowed for the length of
- // this call and both outlive the call. `interface` is guaranteed to be
- // a valid null-terminated C-style string.
sys::AServiceManager_forEachDeclaredInstance(
interface.as_ptr(),
&mut instances as *mut _ as *mut c_void,
@@ -860,10 +826,8 @@
})
}
-/// # Safety
-///
-/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an
-/// `AIBinder`, so we can trivially extract this pointer here.
+/// Safety: `SpIBinder` guarantees that `binder` always contains a valid pointer
+/// to an `AIBinder`, so we can trivially extract this pointer here.
unsafe impl AsNative<sys::AIBinder> for SpIBinder {
fn as_native(&self) -> *const sys::AIBinder {
self.0.as_ptr()
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index cc18741..a3a2562 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -22,30 +22,48 @@
pub struct ProcessState;
impl ProcessState {
- /// Start the Binder IPC thread pool
+ /// Starts the Binder IPC thread pool.
+ ///
+ /// Starts 1 thread, plus allows the kernel to lazily start up to
+ /// `num_threads` additional threads as specified by
+ /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count).
+ ///
+ /// This should be done before creating any Binder client or server. If
+ /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are
+ /// called, then some things (such as callbacks and
+ /// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently
+ /// not work: the callbacks will be queued but never called as there is no
+ /// thread to call them on.
pub fn start_thread_pool() {
+ // Safety: Safe FFI
unsafe {
- // Safety: Safe FFI
sys::ABinderProcess_startThreadPool();
}
}
- /// Set the maximum number of threads that can be started in the threadpool.
+ /// Sets the maximum number of threads that can be started in the
+ /// threadpool.
///
- /// By default, after startThreadPool is called, this is 15. If it is called
- /// additional times, it will only prevent the kernel from starting new
- /// threads and will not delete already existing threads.
+ /// By default, after [`start_thread_pool`](Self::start_thread_pool) is
+ /// called, this is 15. If it is called additional times, the thread pool
+ /// size can only be increased.
pub fn set_thread_pool_max_thread_count(num_threads: u32) {
+ // Safety: Safe FFI
unsafe {
- // Safety: Safe FFI
sys::ABinderProcess_setThreadPoolMaxThreadCount(num_threads);
}
}
- /// Block on the Binder IPC thread pool
+ /// Blocks on the Binder IPC thread pool by adding the current thread to the
+ /// pool.
+ ///
+ /// Note that this adds the current thread in addition to those that are
+ /// created by
+ /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count)
+ /// and [`start_thread_pool`](Self::start_thread_pool).
pub fn join_thread_pool() {
+ // Safety: Safe FFI
unsafe {
- // Safety: Safe FFI
sys::ABinderProcess_joinThreadPool();
}
}
@@ -68,10 +86,8 @@
/// \return calling uid or the current process's UID if this thread isn't
/// processing a transaction.
pub fn get_calling_uid() -> uid_t {
- unsafe {
- // Safety: Safe FFI
- sys::AIBinder_getCallingUid()
- }
+ // Safety: Safe FFI
+ unsafe { sys::AIBinder_getCallingUid() }
}
/// This returns the calling PID assuming that this thread is called from a
@@ -93,10 +109,8 @@
/// If the transaction being processed is a oneway transaction, then this
/// method will return 0.
pub fn get_calling_pid() -> pid_t {
- unsafe {
- // Safety: Safe FFI
- sys::AIBinder_getCallingPid()
- }
+ // Safety: Safe FFI
+ unsafe { sys::AIBinder_getCallingPid() }
}
/// Determine whether the current thread is currently executing an incoming transaction.
@@ -104,10 +118,8 @@
/// \return true if the current thread is currently executing an incoming transaction, and false
/// otherwise.
pub fn is_handling_transaction() -> bool {
- unsafe {
- // Safety: Safe FFI
- sys::AIBinder_isHandlingTransaction()
- }
+ // Safety: Safe FFI
+ unsafe { sys::AIBinder_isHandlingTransaction() }
}
/// This function makes the client's security context available to the
diff --git a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp
index 59ca6ed..663b9bb 100644
--- a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp
+++ b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp
@@ -54,14 +54,12 @@
EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
auto interface = aidl::IBinderRustNdkInteropTest::fromBinder(binder);
- // TODO(b/167723746): this test requires that fromBinder allow association
- // with an already associated local binder by treating it as remote.
- EXPECT_EQ(interface, nullptr);
+ EXPECT_NE(interface, nullptr);
- // std::string in("testing");
- // std::string out;
- // EXPECT_TRUE(interface->echo(in, &out).isOk());
- // EXPECT_EQ(in, out);
+ std::string in("testing");
+ std::string out;
+ EXPECT_TRUE(interface->echo(in, &out).isOk());
+ EXPECT_EQ(in, out);
}
int main(int argc, char** argv) {
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index ca2cedc..c049b80 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -545,6 +545,11 @@
}
fn get_expected_selinux_context() -> &'static str {
+ // SAFETY: The pointer we pass to `getcon` is valid because it comes from a reference, and
+ // `getcon` doesn't retain it after it returns. If `getcon` succeeds then `out_ptr` will
+ // point to a valid C string, otherwise it will remain null. We check for null, so the
+ // pointer we pass to `CStr::from_ptr` must be a valid pointer to a C string. There is a
+ // memory leak as we don't call `freecon`, but that's fine because this is just a test.
unsafe {
let mut out_ptr = ptr::null_mut();
assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index 415ede1..37f182e 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -28,10 +28,11 @@
///
/// # Safety
///
-/// service_name must be a valid, non-null C-style string (null-terminated).
+/// service_name must be a valid, non-null C-style string (nul-terminated).
#[no_mangle]
pub unsafe extern "C" fn rust_call_ndk(service_name: *const c_char) -> c_int {
- let service_name = CStr::from_ptr(service_name).to_str().unwrap();
+ // SAFETY: Our caller promises that service_name is a valid C string.
+ let service_name = unsafe { CStr::from_ptr(service_name) }.to_str().unwrap();
// The Rust class descriptor pointer will not match the NDK one, but the
// descriptor strings match so this needs to still associate.
@@ -85,10 +86,11 @@
///
/// # Safety
///
-/// service_name must be a valid, non-null C-style string (null-terminated).
+/// service_name must be a valid, non-null C-style string (nul-terminated).
#[no_mangle]
pub unsafe extern "C" fn rust_start_service(service_name: *const c_char) -> c_int {
- let service_name = CStr::from_ptr(service_name).to_str().unwrap();
+ // SAFETY: Our caller promises that service_name is a valid C string.
+ let service_name = unsafe { CStr::from_ptr(service_name) }.to_str().unwrap();
let service = BnBinderRustNdkInteropTest::new_binder(Service, BinderFeatures::default());
match binder::add_service(service_name, service.as_binder()) {
Ok(_) => StatusCode::OK as c_int,
diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
index 29bf92c..ce0f742 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
@@ -105,9 +105,9 @@
for operation in read_operations {
match operation {
ReadOperation::SetDataPosition { pos } => {
+ // Safety: Safe if pos is less than current size of the parcel.
+ // It relies on C++ code for bound checks
unsafe {
- // Safety: Safe if pos is less than current size of the parcel.
- // It relies on C++ code for bound checks
match parcel.set_data_position(pos) {
Ok(result) => result,
Err(e) => println!("error occurred while setting data position: {:?}", e),
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
index 43a3094..5cac647 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
@@ -11,7 +11,6 @@
source_stem: "bindings",
visibility: [":__subpackages__"],
bindgen_flags: [
- "--size_t-is-usize",
"--allowlist-function",
"createRandomParcel",
"--allowlist-function",
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
index 89126ca..2537ce0 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -19,11 +19,6 @@
srcs: [
"service_fuzzer.rs",
],
- shared_libs: [
- "libbinder",
- "libbinder_ndk",
- "libutils",
- ],
rustlibs: [
"libbinder_rs",
"libbinder_random_parcel_rs",
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 6220db4..2b6c282 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -26,7 +26,7 @@
use binder::binder_impl::{Binder, BorrowedParcel, TransactionCode};
use std::ffi::{c_void, CStr, CString};
-use std::sync::Once;
+use std::sync::OnceLock;
#[allow(
non_camel_case_types,
@@ -70,20 +70,18 @@
};
}
-static SERVICE_ONCE: Once = Once::new();
-static mut SERVICE: Option<SpIBinder> = None;
+static SERVICE: OnceLock<SpIBinder> = OnceLock::new();
/// Start binder service and return a raw AIBinder pointer to it.
///
/// Safe to call multiple times, only creates the service once.
#[no_mangle]
pub extern "C" fn rust_service() -> *mut c_void {
- unsafe {
- SERVICE_ONCE.call_once(|| {
- SERVICE = Some(BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder());
- });
- SERVICE.as_ref().unwrap().as_raw().cast()
- }
+ let service = SERVICE
+ .get_or_init(|| BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder());
+ // SAFETY: The SpIBinder will remain alive as long as the program is running because it is in
+ // the static SERVICE, so the pointer is valid forever.
+ unsafe { service.as_raw().cast() }
}
/// Empty interface just to use the declare_binder_interface macro
@@ -113,11 +111,13 @@
bindings::Transaction_TEST_BOOL => {
assert!(parcel.read::<bool>()?);
assert!(!parcel.read::<bool>()?);
+ // SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { bindings::TESTDATA_BOOL });
assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None);
reply.write(&true)?;
reply.write(&false)?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_BOOL }[..])?;
reply.write(&(None as Option<Vec<bool>>))?;
}
@@ -125,14 +125,18 @@
assert_eq!(parcel.read::<i8>()?, 0);
assert_eq!(parcel.read::<i8>()?, 1);
assert_eq!(parcel.read::<i8>()?, i8::max_value());
+ // SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 });
+ // SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u8>>()?, unsafe { bindings::TESTDATA_U8 });
assert_eq!(parcel.read::<Option<Vec<i8>>>()?, None);
reply.write(&0i8)?;
reply.write(&1i8)?;
reply.write(&i8::max_value())?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_U8 }[..])?;
reply.write(&(None as Option<Vec<i8>>))?;
}
@@ -140,12 +144,14 @@
assert_eq!(parcel.read::<u16>()?, 0);
assert_eq!(parcel.read::<u16>()?, 1);
assert_eq!(parcel.read::<u16>()?, u16::max_value());
+ // SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { bindings::TESTDATA_CHARS });
assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None);
reply.write(&0u16)?;
reply.write(&1u16)?;
reply.write(&u16::max_value())?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?;
reply.write(&(None as Option<Vec<u16>>))?;
}
@@ -153,12 +159,14 @@
assert_eq!(parcel.read::<i32>()?, 0);
assert_eq!(parcel.read::<i32>()?, 1);
assert_eq!(parcel.read::<i32>()?, i32::max_value());
+ // SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { bindings::TESTDATA_I32 });
assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None);
reply.write(&0i32)?;
reply.write(&1i32)?;
reply.write(&i32::max_value())?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?;
reply.write(&(None as Option<Vec<i32>>))?;
}
@@ -166,12 +174,14 @@
assert_eq!(parcel.read::<i64>()?, 0);
assert_eq!(parcel.read::<i64>()?, 1);
assert_eq!(parcel.read::<i64>()?, i64::max_value());
+ // SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { bindings::TESTDATA_I64 });
assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None);
reply.write(&0i64)?;
reply.write(&1i64)?;
reply.write(&i64::max_value())?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?;
reply.write(&(None as Option<Vec<i64>>))?;
}
@@ -179,12 +189,14 @@
assert_eq!(parcel.read::<u64>()?, 0);
assert_eq!(parcel.read::<u64>()?, 1);
assert_eq!(parcel.read::<u64>()?, u64::max_value());
+ // SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { bindings::TESTDATA_U64 });
assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None);
reply.write(&0u64)?;
reply.write(&1u64)?;
reply.write(&u64::max_value())?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?;
reply.write(&(None as Option<Vec<u64>>))?;
}
@@ -192,10 +204,12 @@
assert_eq!(parcel.read::<f32>()?, 0f32);
let floats = parcel.read::<Vec<f32>>()?;
assert!(floats[0].is_nan());
+ // SAFETY: Just reading an extern constant.
assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]);
assert_eq!(parcel.read::<Option<Vec<f32>>>()?, None);
reply.write(&0f32)?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_FLOAT }[..])?;
reply.write(&(None as Option<Vec<f32>>))?;
}
@@ -203,10 +217,12 @@
assert_eq!(parcel.read::<f64>()?, 0f64);
let doubles = parcel.read::<Vec<f64>>()?;
assert!(doubles[0].is_nan());
+ // SAFETY: Just reading an extern constant.
assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]);
assert_eq!(parcel.read::<Option<Vec<f64>>>()?, None);
reply.write(&0f64)?;
+ // SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_DOUBLE }[..])?;
reply.write(&(None as Option<Vec<f64>>))?;
}
@@ -216,14 +232,17 @@
let s: Option<String> = parcel.read()?;
assert_eq!(s, None);
let s: Option<Vec<Option<String>>> = parcel.read()?;
+ // SAFETY: Just reading an extern constant.
for (s, expected) in s.unwrap().iter().zip(unsafe { bindings::TESTDATA_STRS }.iter()) {
let expected =
+ // SAFETY: Just reading an extern constant.
unsafe { expected.as_ref().and_then(|e| CStr::from_ptr(e).to_str().ok()) };
assert_eq!(s.as_deref(), expected);
}
let s: Option<Vec<Option<String>>> = parcel.read()?;
assert_eq!(s, None);
+ // SAFETY: Just reading an extern constant.
let strings: Vec<Option<String>> = unsafe {
bindings::TESTDATA_STRS
.iter()
@@ -258,8 +277,7 @@
assert!(ibinders[1].is_none());
assert!(parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.is_none());
- let service =
- unsafe { SERVICE.as_ref().expect("Global binder service not initialized").clone() };
+ let service = SERVICE.get().expect("Global binder service not initialized").clone();
reply.write(&service)?;
reply.write(&(None as Option<&SpIBinder>))?;
reply.write(&[Some(&service), None][..])?;
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index bc40864..6712c9c 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -216,16 +216,16 @@
auto server = RpcServer::make();
server->setRootObject(sp<BBinder>::make());
- CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
+ ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
std::thread([server]() { server->join(); }).detach();
- status_t status;
auto session = RpcSession::make();
- status = session->setupUnixDomainClient(addr.c_str());
- CHECK_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str();
+ status_t status = session->setupUnixDomainClient(addr.c_str());
+ ASSERT_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str();
auto remoteBinder = session->getRootObject();
+ ASSERT_NE(remoteBinder, nullptr);
size_t mallocs = 0, totalBytes = 0;
{
@@ -233,7 +233,7 @@
mallocs++;
totalBytes += bytes;
});
- CHECK_EQ(OK, remoteBinder->pingBinder());
+ ASSERT_EQ(OK, remoteBinder->pingBinder());
}
EXPECT_EQ(mallocs, 1);
EXPECT_EQ(totalBytes, 40);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index abc423b..e021af0 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -82,7 +82,7 @@
static constexpr int kSchedPolicy = SCHED_RR;
static constexpr int kSchedPriority = 7;
static constexpr int kSchedPriorityMore = 8;
-static constexpr int kKernelThreads = 15;
+static constexpr int kKernelThreads = 17; // anything different than the default
static String16 binderLibTestServiceName = String16("test.binderLib");
@@ -1357,17 +1357,20 @@
EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply),
StatusEq(NO_ERROR));
int32_t replyi = reply.readInt32();
- // Expect 16 threads: kKernelThreads = 15 + Pool thread == 16
- EXPECT_TRUE(replyi == kKernelThreads || replyi == kKernelThreads + 1);
+ // see getThreadPoolMaxTotalThreadCount for why there is a race
+ EXPECT_TRUE(replyi == kKernelThreads + 1 || replyi == kKernelThreads + 2) << replyi;
+
EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_LOCK, data, &reply), NO_ERROR);
/*
- * This will use all threads in the pool expect the main pool thread.
- * The service should run fine without locking, and the thread count should
- * not exceed 16 (15 Max + pool thread).
+ * This will use all threads in the pool but one. There are actually kKernelThreads+2
+ * available in the other process (startThreadPool, joinThreadPool, + the kernel-
+ * started threads from setThreadPoolMaxThreadCount
+ *
+ * Adding one more will cause it to deadlock.
*/
std::vector<std::thread> ts;
- for (size_t i = 0; i < kKernelThreads; i++) {
+ for (size_t i = 0; i < kKernelThreads + 1; i++) {
ts.push_back(std::thread([&] {
Parcel local_reply;
EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply),
@@ -1375,8 +1378,13 @@
}));
}
- data.writeInt32(500);
- // Give a chance for all threads to be used
+ // make sure all of the above calls will be queued in parallel. Otherwise, most of
+ // the time, the below call will pre-empt them (presumably because we have the
+ // scheduler timeslice already + scheduler hint).
+ sleep(1);
+
+ data.writeInt32(1000);
+ // Give a chance for all threads to be used (kKernelThreads + 1 thread in use)
EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR);
for (auto &t : ts) {
@@ -1386,7 +1394,7 @@
EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply),
StatusEq(NO_ERROR));
replyi = reply.readInt32();
- EXPECT_EQ(replyi, kKernelThreads + 1);
+ EXPECT_EQ(replyi, kKernelThreads + 2);
}
TEST_F(BinderLibTest, ThreadPoolStarted) {
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 359c783..0a0dae0 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -29,6 +29,7 @@
using android::status_t;
using android::String16;
using android::String8;
+using android::base::unique_fd;
using android::binder::Status;
TEST(Parcel, NonNullTerminatedString8) {
@@ -112,6 +113,166 @@
EXPECT_EQ(ret[1], STDIN_FILENO);
}
+TEST(Parcel, AppendFromEmpty) {
+ Parcel p1;
+ Parcel p2;
+ p2.writeInt32(2);
+
+ ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize()));
+
+ p1.setDataPosition(0);
+ ASSERT_EQ(2, p1.readInt32());
+
+ p2.setDataPosition(0);
+ ASSERT_EQ(2, p2.readInt32());
+}
+
+TEST(Parcel, AppendPlainData) {
+ Parcel p1;
+ p1.writeInt32(1);
+ Parcel p2;
+ p2.writeInt32(2);
+
+ ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize()));
+
+ p1.setDataPosition(0);
+ ASSERT_EQ(1, p1.readInt32());
+ ASSERT_EQ(2, p1.readInt32());
+
+ p2.setDataPosition(0);
+ ASSERT_EQ(2, p2.readInt32());
+}
+
+TEST(Parcel, AppendPlainDataPartial) {
+ Parcel p1;
+ p1.writeInt32(1);
+ Parcel p2;
+ p2.writeInt32(2);
+ p2.writeInt32(3);
+ p2.writeInt32(4);
+
+ // only copy 8 bytes (two int32's worth)
+ ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8));
+
+ p1.setDataPosition(0);
+ ASSERT_EQ(1, p1.readInt32());
+ ASSERT_EQ(2, p1.readInt32());
+ ASSERT_EQ(3, p1.readInt32());
+ ASSERT_EQ(0, p1.readInt32()); // not 4, end of Parcel
+
+ p2.setDataPosition(0);
+ ASSERT_EQ(2, p2.readInt32());
+}
+
+TEST(Parcel, AppendWithBinder) {
+ sp<IBinder> b1 = sp<BBinder>::make();
+ sp<IBinder> b2 = sp<BBinder>::make();
+
+ Parcel p1;
+ p1.writeInt32(1);
+ p1.writeStrongBinder(b1);
+ Parcel p2;
+ p2.writeInt32(2);
+ p2.writeStrongBinder(b2);
+
+ ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize()));
+
+ p1.setDataPosition(0);
+ ASSERT_EQ(1, p1.readInt32());
+ ASSERT_EQ(b1, p1.readStrongBinder());
+ ASSERT_EQ(2, p1.readInt32());
+ ASSERT_EQ(b2, p1.readStrongBinder());
+ ASSERT_EQ(2, p1.objectsCount());
+
+ p2.setDataPosition(0);
+ ASSERT_EQ(2, p2.readInt32());
+ ASSERT_EQ(b2, p2.readStrongBinder());
+}
+
+TEST(Parcel, AppendWithBinderPartial) {
+ sp<IBinder> b1 = sp<BBinder>::make();
+ sp<IBinder> b2 = sp<BBinder>::make();
+
+ Parcel p1;
+ p1.writeInt32(1);
+ p1.writeStrongBinder(b1);
+ Parcel p2;
+ p2.writeInt32(2);
+ p2.writeStrongBinder(b2);
+
+ ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); // BAD: 4 bytes into strong binder
+
+ p1.setDataPosition(0);
+ ASSERT_EQ(1, p1.readInt32());
+ ASSERT_EQ(b1, p1.readStrongBinder());
+ ASSERT_EQ(2, p1.readInt32());
+ ASSERT_EQ(1935813253, p1.readInt32()); // whatever garbage that is there (ABI)
+ ASSERT_EQ(1, p1.objectsCount());
+
+ p2.setDataPosition(0);
+ ASSERT_EQ(2, p2.readInt32());
+ ASSERT_EQ(b2, p2.readStrongBinder());
+}
+
+TEST(Parcel, AppendWithFd) {
+ unique_fd fd1 = unique_fd(dup(0));
+ unique_fd fd2 = unique_fd(dup(0));
+
+ Parcel p1;
+ p1.writeInt32(1);
+ p1.writeDupFileDescriptor(0); // with ownership
+ p1.writeFileDescriptor(fd1.get()); // without ownership
+ Parcel p2;
+ p2.writeInt32(2);
+ p2.writeDupFileDescriptor(0); // with ownership
+ p2.writeFileDescriptor(fd2.get()); // without ownership
+
+ ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize()));
+
+ p1.setDataPosition(0);
+ ASSERT_EQ(1, p1.readInt32());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+ ASSERT_EQ(2, p1.readInt32());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+ ASSERT_EQ(4, p1.objectsCount());
+
+ p2.setDataPosition(0);
+ ASSERT_EQ(2, p2.readInt32());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+}
+
+TEST(Parcel, AppendWithFdPartial) {
+ unique_fd fd1 = unique_fd(dup(0));
+ unique_fd fd2 = unique_fd(dup(0));
+
+ Parcel p1;
+ p1.writeInt32(1);
+ p1.writeDupFileDescriptor(0); // with ownership
+ p1.writeFileDescriptor(fd1.get()); // without ownership
+ Parcel p2;
+ p2.writeInt32(2);
+ p2.writeDupFileDescriptor(0); // with ownership
+ p2.writeFileDescriptor(fd2.get()); // without ownership
+
+ ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); // BAD: 4 bytes into binder
+
+ p1.setDataPosition(0);
+ ASSERT_EQ(1, p1.readInt32());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+ ASSERT_EQ(2, p1.readInt32());
+ ASSERT_EQ(1717840517, p1.readInt32()); // whatever garbage that is there (ABI)
+ ASSERT_EQ(2, p1.objectsCount());
+
+ p2.setDataPosition(0);
+ ASSERT_EQ(2, p2.readInt32());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+ ASSERT_NE(-1, p1.readFileDescriptor());
+}
+
// Tests a second operation results in a parcel at the same location as it
// started.
void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 505f30f..4c3c68e 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -249,12 +249,12 @@
CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions);
}
- SocketType socketType = std::get<0>(GetParam());
- RpcSecurity rpcSecurity = std::get<1>(GetParam());
- uint32_t clientVersion = std::get<2>(GetParam());
- uint32_t serverVersion = std::get<3>(GetParam());
- bool singleThreaded = std::get<4>(GetParam());
- bool noKernel = std::get<5>(GetParam());
+ SocketType socketType = GetParam().type;
+ RpcSecurity rpcSecurity = GetParam().security;
+ uint32_t clientVersion = GetParam().clientVersion;
+ uint32_t serverVersion = GetParam().serverVersion;
+ bool singleThreaded = GetParam().singleThreaded;
+ bool noKernel = GetParam().noKernel;
std::string path = android::base::GetExecutableDirectory();
auto servicePath = android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(),
@@ -461,8 +461,11 @@
EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs);
- // Potential flake, but make sure calls are handled in parallel.
- EXPECT_LE(epochMsAfter, epochMsBefore + 4 * sleepMs);
+ // Potential flake, but make sure calls are handled in parallel. Due
+ // to past flakes, this only checks that the amount of time taken has
+ // some parallelism. Other tests such as ThreadPoolGreaterThanEqualRequested
+ // check this more exactly.
+ EXPECT_LE(epochMsAfter, epochMsBefore + (numCalls - 1) * sleepMs);
}
TEST_P(BinderRpc, ThreadPoolOverSaturated) {
@@ -671,7 +674,7 @@
// session 0 - will check for leaks in destrutor of proc
// session 1 - we want to make sure it gets deleted when we drop all references to it
auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1, .numIncomingConnectionsBySession = {0, 1}, .numSessions = 2});
+ {.numThreads = 1, .numSessions = 2, .numIncomingConnectionsBySession = {0, 1}});
wp<RpcSession> session = proc.proc->sessions.at(1).session;
@@ -688,7 +691,11 @@
EXPECT_EQ(nullptr, session.promote());
- sleep(1); // give time for remote session to shutdown
+ // now that it has died, wait for the remote session to shutdown
+ std::vector<int32_t> remoteCounts;
+ do {
+ EXPECT_OK(proc.rootIface->countBinders(&remoteCounts));
+ } while (remoteCounts.size() > 1);
}
TEST_P(BinderRpc, SingleDeathRecipient) {
@@ -1114,15 +1121,30 @@
}
#ifdef BINDER_RPC_TO_TRUSTY_TEST
-INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
- ::testing::Combine(::testing::Values(SocketType::TIPC),
- ::testing::Values(RpcSecurity::RAW),
- ::testing::ValuesIn(testVersions()),
- ::testing::ValuesIn(testVersions()),
- ::testing::Values(true), ::testing::Values(true)),
+
+static std::vector<BinderRpc::ParamType> getTrustyBinderRpcParams() {
+ std::vector<BinderRpc::ParamType> ret;
+
+ for (const auto& clientVersion : testVersions()) {
+ for (const auto& serverVersion : testVersions()) {
+ ret.push_back(BinderRpc::ParamType{
+ .type = SocketType::TIPC,
+ .security = RpcSecurity::RAW,
+ .clientVersion = clientVersion,
+ .serverVersion = serverVersion,
+ .singleThreaded = true,
+ .noKernel = true,
+ });
+ }
+ }
+
+ return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, ::testing::ValuesIn(getTrustyBinderRpcParams()),
BinderRpc::PrintParamInfo);
#else // BINDER_RPC_TO_TRUSTY_TEST
-static bool testSupportVsockLoopback() {
+bool testSupportVsockLoopback() {
// We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
@@ -1222,7 +1244,15 @@
if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED);
+#ifdef __BIONIC__
+ // Devices may not have vsock support. AVF tests will verify whether they do, but
+ // we can't require it due to old kernels for the time being.
static bool hasVsockLoopback = testSupportVsockLoopback();
+#else
+ // On host machines, we always assume we have vsock loopback. If we don't, the
+ // subsequent failures will be more clear than showing one now.
+ static bool hasVsockLoopback = true;
+#endif
if (hasVsockLoopback) {
ret.push_back(SocketType::VSOCK);
@@ -1231,13 +1261,47 @@
return ret;
}
-INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
- ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
- ::testing::ValuesIn(RpcSecurityValues()),
- ::testing::ValuesIn(testVersions()),
- ::testing::ValuesIn(testVersions()),
- ::testing::Values(false, true),
- ::testing::Values(false, true)),
+static std::vector<BinderRpc::ParamType> getBinderRpcParams() {
+ std::vector<BinderRpc::ParamType> ret;
+
+ constexpr bool full = false;
+
+ for (const auto& type : testSocketTypes()) {
+ if (full || type == SocketType::UNIX) {
+ for (const auto& security : RpcSecurityValues()) {
+ for (const auto& clientVersion : testVersions()) {
+ for (const auto& serverVersion : testVersions()) {
+ for (bool singleThreaded : {false, true}) {
+ for (bool noKernel : {false, true}) {
+ ret.push_back(BinderRpc::ParamType{
+ .type = type,
+ .security = security,
+ .clientVersion = clientVersion,
+ .serverVersion = serverVersion,
+ .singleThreaded = singleThreaded,
+ .noKernel = noKernel,
+ });
+ }
+ }
+ }
+ }
+ }
+ } else {
+ ret.push_back(BinderRpc::ParamType{
+ .type = type,
+ .security = RpcSecurity::RAW,
+ .clientVersion = RPC_WIRE_PROTOCOL_VERSION,
+ .serverVersion = RPC_WIRE_PROTOCOL_VERSION,
+ .singleThreaded = false,
+ .noKernel = false,
+ });
+ }
+ }
+
+ return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::ValuesIn(getBinderRpcParams()),
BinderRpc::PrintParamInfo);
class BinderRpcServerRootObject
diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
index 6cde9f7..2c9646b 100644
--- a/libs/binder/tests/binderRpcTestFixture.h
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -79,6 +79,7 @@
expectAlreadyShutdown = true;
}
+ BinderRpcTestProcessSession(std::unique_ptr<ProcessSession> proc) : proc(std::move(proc)){};
BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
~BinderRpcTestProcessSession() {
if (!expectAlreadyShutdown) {
@@ -105,15 +106,23 @@
}
};
-class BinderRpc : public ::testing::TestWithParam<
- std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> {
+struct BinderRpcParam {
+ SocketType type;
+ RpcSecurity security;
+ uint32_t clientVersion;
+ uint32_t serverVersion;
+ bool singleThreaded;
+ bool noKernel;
+};
+class BinderRpc : public ::testing::TestWithParam<BinderRpcParam> {
public:
- SocketType socketType() const { return std::get<0>(GetParam()); }
- RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
- uint32_t clientVersion() const { return std::get<2>(GetParam()); }
- uint32_t serverVersion() const { return std::get<3>(GetParam()); }
- bool serverSingleThreaded() const { return std::get<4>(GetParam()); }
- bool noKernel() const { return std::get<5>(GetParam()); }
+ // TODO: avoid unnecessary layer of indirection
+ SocketType socketType() const { return GetParam().type; }
+ RpcSecurity rpcSecurity() const { return GetParam().security; }
+ uint32_t clientVersion() const { return GetParam().clientVersion; }
+ uint32_t serverVersion() const { return GetParam().serverVersion; }
+ bool serverSingleThreaded() const { return GetParam().singleThreaded; }
+ bool noKernel() const { return GetParam().noKernel; }
bool clientOrServerSingleThreaded() const {
return !kEnableRpcThreads || serverSingleThreaded();
@@ -138,9 +147,7 @@
}
BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
- BinderRpcTestProcessSession ret{
- .proc = createRpcTestSocketServerProcessEtc(options),
- };
+ BinderRpcTestProcessSession ret(createRpcTestSocketServerProcessEtc(options));
ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root;
ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
@@ -149,15 +156,16 @@
}
static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
- auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
- std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
- if (singleThreaded) {
+ auto ret = PrintToString(info.param.type) + "_" +
+ newFactory(info.param.security)->toCString() + "_clientV" +
+ std::to_string(info.param.clientVersion) + "_serverV" +
+ std::to_string(info.param.serverVersion);
+ if (info.param.singleThreaded) {
ret += "_single_threaded";
} else {
ret += "_multi_threaded";
}
- if (noKernel) {
+ if (info.param.noKernel) {
ret += "_no_kernel";
} else {
ret += "_with_kernel";
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index cb09a7f..7435f30 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -139,7 +139,8 @@
CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd)));
break;
case SocketType::VSOCK:
- CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort));
+ CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort))
+ << "Need `sudo modprobe vsock_loopback`?";
break;
case SocketType::INET: {
CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index 28be10d..fcb83bd 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -57,9 +57,9 @@
[](size_t n) { return n != 0; }),
"Non-zero incoming connections on Trusty");
- RpcSecurity rpcSecurity = std::get<1>(GetParam());
- uint32_t clientVersion = std::get<2>(GetParam());
- uint32_t serverVersion = std::get<3>(GetParam());
+ RpcSecurity rpcSecurity = GetParam().security;
+ uint32_t clientVersion = GetParam().clientVersion;
+ uint32_t serverVersion = GetParam().serverVersion;
auto ret = std::make_unique<TrustyProcessSession>();
@@ -89,12 +89,27 @@
return ret;
}
-INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
- ::testing::Combine(::testing::Values(SocketType::TIPC),
- ::testing::Values(RpcSecurity::RAW),
- ::testing::ValuesIn(testVersions()),
- ::testing::ValuesIn(testVersions()),
- ::testing::Values(false), ::testing::Values(true)),
+static std::vector<BinderRpc::ParamType> getTrustyBinderRpcParams() {
+ std::vector<BinderRpc::ParamType> ret;
+
+ for (const auto& clientVersion : testVersions()) {
+ for (const auto& serverVersion : testVersions()) {
+ ret.push_back(BinderRpc::ParamType{
+ .type = SocketType::TIPC,
+ .security = RpcSecurity::RAW,
+ .clientVersion = clientVersion,
+ .serverVersion = serverVersion,
+ // TODO: should we test both versions here?
+ .singleThreaded = false,
+ .noKernel = true,
+ });
+ }
+ }
+
+ return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, ::testing::ValuesIn(getTrustyBinderRpcParams()),
BinderRpc::PrintParamInfo);
} // namespace android
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 1f46010..e43508e 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -84,7 +84,7 @@
GTEST_SKIP() << "This test requires a multi-threaded service";
}
- SocketType type = std::get<0>(GetParam());
+ SocketType type = GetParam().type;
if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) {
// we can't get port numbers for unix sockets
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 69f7147..b268c5d 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -21,6 +21,8 @@
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
+#include <private/android_filesystem_config.h>
+
namespace android {
void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) {
@@ -33,76 +35,64 @@
.extraFds = {},
};
+ // always refresh the calling identity, because we sometimes set it below, but also,
+ // the code we're fuzzing might reset it
+ IPCThreadState::self()->clearCallingIdentity();
+
+ // Always take so that a perturbation of just the one ConsumeBool byte will always
+ // take the same path, but with a different UID. Without this, the fuzzer needs to
+ // guess both the change in value and the shift at the same time.
+ int64_t maybeSetUid = provider.PickValueInArray<int64_t>(
+ {static_cast<int64_t>(AID_ROOT) << 32, static_cast<int64_t>(AID_SYSTEM) << 32,
+ provider.ConsumeIntegralInRange<int64_t>(static_cast<int64_t>(AID_ROOT) << 32,
+ static_cast<int64_t>(AID_USER) << 32),
+ provider.ConsumeIntegral<int64_t>()});
+
if (provider.ConsumeBool()) {
// set calling uid
- IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral<int64_t>());
+ IPCThreadState::self()->restoreCallingIdentity(maybeSetUid);
}
while (provider.remaining_bytes() > 0) {
- provider.PickValueInArray<std::function<void()>>({
- [&]() {
- // Most of the AIDL services will have small set of transaction codes.
- uint32_t code = provider.ConsumeBool()
- ? provider.ConsumeIntegral<uint32_t>()
- : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
- uint32_t flags = provider.ConsumeIntegral<uint32_t>();
- Parcel data;
- // for increased fuzz coverage
- data.setEnforceNoDataAvail(provider.ConsumeBool());
+ // Most of the AIDL services will have small set of transaction codes.
+ uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
+ : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+ uint32_t flags = provider.ConsumeIntegral<uint32_t>();
+ Parcel data;
+ // for increased fuzz coverage
+ data.setEnforceNoDataAvail(false);
+ data.setServiceFuzzing();
- sp<IBinder> target = options.extraBinders.at(
- provider.ConsumeIntegralInRange<size_t>(0,
- options.extraBinders.size() -
- 1));
- options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) {
- // most code will be behind checks that the head of the Parcel
- // is exactly this, so make it easier for fuzzers to reach this
- if (provider.ConsumeBool()) {
- p->writeInterfaceToken(target->getInterfaceDescriptor());
- }
- };
+ sp<IBinder> target = options.extraBinders.at(
+ provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1));
+ options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) {
+ // most code will be behind checks that the head of the Parcel
+ // is exactly this, so make it easier for fuzzers to reach this
+ if (provider.ConsumeBool()) {
+ p->writeInterfaceToken(target->getInterfaceDescriptor());
+ }
+ };
- std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
- provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
- fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()),
- &options);
+ std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
+ provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+ fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options);
- Parcel reply;
- // for increased fuzz coverage
- reply.setEnforceNoDataAvail(provider.ConsumeBool());
- (void)target->transact(code, data, &reply, flags);
+ Parcel reply;
+ // for increased fuzz coverage
+ reply.setEnforceNoDataAvail(false);
+ reply.setServiceFuzzing();
+ (void)target->transact(code, data, &reply, flags);
- // feed back in binders and fds that are returned from the service, so that
- // we can fuzz those binders, and use the fds and binders to feed back into
- // the binders
- auto retBinders = reply.debugReadAllStrongBinders();
- options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(),
- retBinders.end());
- auto retFds = reply.debugReadAllFileDescriptors();
- for (size_t i = 0; i < retFds.size(); i++) {
- options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
- }
- },
- [&]() {
- if (options.extraFds.size() == 0) {
- return;
- }
- uint32_t toDelete =
- provider.ConsumeIntegralInRange<uint32_t>(0,
- options.extraFds.size() - 1);
- options.extraFds.erase(options.extraFds.begin() + toDelete);
- },
- [&]() {
- if (options.extraBinders.size() <= 1) {
- return;
- }
- uint32_t toDelete =
- provider.ConsumeIntegralInRange<uint32_t>(0,
- options.extraBinders.size() -
- 1);
- options.extraBinders.erase(options.extraBinders.begin() + toDelete);
- },
- })();
+ // feed back in binders and fds that are returned from the service, so that
+ // we can fuzz those binders, and use the fds and binders to feed back into
+ // the binders
+ auto retBinders = reply.debugReadAllStrongBinders();
+ options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(),
+ retBinders.end());
+ auto retFds = reply.debugReadAllFileDescriptors();
+ for (size_t i = 0; i < retFds.size(); i++) {
+ options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
+ }
}
// invariants
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
index 28da285..96092b1 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
@@ -36,7 +36,29 @@
triage_assignee: "waghpawan@google.com",
// This fuzzer should be used only test fuzzService locally
- fuzz_on_haiku_host: false,
- fuzz_on_haiku_device: false,
+ fuzz_on_haiku_host: true,
+ fuzz_on_haiku_device: true,
},
}
+
+sh_test_host {
+ name: "fuzz_service_test",
+ src: "run_fuzz_service_test.sh",
+ filename: "run_fuzz_service_test.sh",
+ test_config: "fuzz_service_test_config.xml",
+ data_bins: [
+ "test_service_fuzzer_should_crash",
+ ],
+ required: [
+ "test_service_fuzzer_should_crash",
+ ],
+ target: {
+ linux_bionic: {
+ enabled: false,
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+ test_suites: ["general-tests"],
+}
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl
index 3eadc02..5089ae5 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl
@@ -21,4 +21,6 @@
void setCharData(char input);
void setBooleanData(boolean input);
-}
\ No newline at end of file
+
+ void setService(ITestService service);
+}
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
index 8907ea0..46205d7 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
@@ -17,35 +17,122 @@
#include <BnTestService.h>
#include <fuzzbinder/libbinder_driver.h>
+#include <binder/IPCThreadState.h>
#include <log/log.h>
-using android::fuzzService;
-using android::sp;
+#include <private/android_filesystem_config.h>
+
using android::binder::Status;
namespace android {
+
+enum class CrashType {
+ NONE,
+ ON_PLAIN,
+ ON_BINDER,
+ ON_KNOWN_UID,
+ ON_SYSTEM_AID,
+ ON_ROOT_AID,
+};
+
// This service is to verify that fuzzService is functioning properly
class TestService : public BnTestService {
public:
- Status setIntData(int /*input*/) {
- LOG_ALWAYS_FATAL("Expected crash in setIntData");
+ TestService(CrashType crash) : mCrash(crash) {}
+
+ void onData() {
+ switch (mCrash) {
+ case CrashType::ON_PLAIN: {
+ LOG_ALWAYS_FATAL("Expected crash, PLAIN.");
+ break;
+ }
+ case CrashType::ON_KNOWN_UID: {
+ if (IPCThreadState::self()->getCallingUid() == getuid()) {
+ LOG_ALWAYS_FATAL("Expected crash, KNOWN_UID.");
+ }
+ break;
+ }
+ case CrashType::ON_SYSTEM_AID: {
+ if (IPCThreadState::self()->getCallingUid() == AID_SYSTEM) {
+ LOG_ALWAYS_FATAL("Expected crash, AID_SYSTEM.");
+ }
+ break;
+ }
+ case CrashType::ON_ROOT_AID: {
+ if (IPCThreadState::self()->getCallingUid() == AID_ROOT) {
+ LOG_ALWAYS_FATAL("Expected crash, AID_ROOT.");
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ Status setIntData(int /*input*/) override {
+ onData();
return Status::ok();
}
- Status setCharData(char16_t /*input*/) {
- LOG_ALWAYS_FATAL("Expected crash in setCharData");
+ Status setCharData(char16_t /*input*/) override {
+ onData();
return Status::ok();
}
- Status setBooleanData(bool /*input*/) {
- LOG_ALWAYS_FATAL("Expected crash in setBooleanData");
+ Status setBooleanData(bool /*input*/) override {
+ onData();
return Status::ok();
}
+
+ Status setService(const sp<ITestService>& service) override {
+ onData();
+ if (mCrash == CrashType::ON_BINDER && service != nullptr) {
+ LOG_ALWAYS_FATAL("Expected crash, BINDER.");
+ }
+ return Status::ok();
+ }
+
+private:
+ CrashType mCrash;
};
-} // namespace android
+
+CrashType gCrashType = CrashType::NONE;
+
+extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
+ if (*argc < 2) {
+ printf("You must specify at least one argument\n");
+ exit(0); // success because this is a crash test
+ }
+
+ std::string arg = std::string((*argv)[1]);
+
+ // ignore first argument, because we consume it
+ (*argv)[1] = (*argv[0]);
+ (*argc)--;
+ (*argv)++;
+
+ if (arg == "PLAIN") {
+ gCrashType = CrashType::ON_PLAIN;
+ } else if (arg == "KNOWN_UID") {
+ gCrashType = CrashType::ON_KNOWN_UID;
+ } else if (arg == "AID_SYSTEM") {
+ gCrashType = CrashType::ON_SYSTEM_AID;
+ } else if (arg == "AID_ROOT") {
+ gCrashType = CrashType::ON_ROOT_AID;
+ } else if (arg == "BINDER") {
+ gCrashType = CrashType::ON_BINDER;
+ } else {
+ printf("INVALID ARG\n");
+ exit(0); // success because this is a crash test
+ }
+
+ return 0;
+}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- auto service = sp<android::TestService>::make();
+ auto service = sp<TestService>::make(gCrashType);
fuzzService(service, FuzzedDataProvider(data, size));
return 0;
}
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml
new file mode 100644
index 0000000..19eb33a
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs fuzzService test">
+ <option name="null-device" value="true" />
+ <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+ <option name="binary" value="run_fuzz_service_test.sh"/>
+ <option name="relative-path-execution" value="true" />
+ </test>
+</configuration>
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
new file mode 100755
index 0000000..25906d8
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+color_success=$'\E'"[0;32m"
+color_failed=$'\E'"[0;31m"
+color_reset=$'\E'"[00m"
+
+FUZZER_NAME=test_service_fuzzer_should_crash
+FUZZER_OUT=fuzzer-output
+
+if [ ! -f "$FUZZER_NAME" ]
+then
+ echo -e "${color_failed}Binary $FUZZER_NAME does not exist"
+ echo "${color_reset}"
+ exit 1
+fi
+
+for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER; do
+ echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE"
+
+ ./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=30 &>"$FUZZER_OUT"
+
+ echo "INFO: Searching fuzzer output for expected crashes"
+ if grep -q "Expected crash, $CRASH_TYPE." "$FUZZER_OUT"
+ then
+ echo -e "${color_success}Success: Found expected crash. fuzzService test successful!"
+ else
+ echo -e "${color_failed}Failed: Unable to find successful fuzzing output from test_service_fuzzer_should_crash"
+ echo "${color_reset}"
+ exit 1
+ fi
+done
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index b8ae84d..dcc8b8e 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -135,7 +135,7 @@
// b/260736889 - limit arbitrarily, due to thread resource exhaustion, which currently
// aborts. Servers should consider RpcServer::setConnectionFilter instead.
- constexpr size_t kMaxConnections = 1000;
+ constexpr size_t kMaxConnections = 10;
while (provider.remaining_bytes() > 0) {
if (connections.empty() ||
diff --git a/libs/binder/trusty/fuzzer/Android.bp b/libs/binder/trusty/fuzzer/Android.bp
new file mode 100644
index 0000000..2f1f54b
--- /dev/null
+++ b/libs/binder/trusty/fuzzer/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_fuzz {
+ name: "trusty_binder_fuzzer",
+ defaults: ["trusty_fuzzer_defaults"],
+ srcs: [":trusty_tipc_fuzzer"],
+ cflags: [
+ "-DTRUSTY_APP_PORT=\"com.android.trusty.binder.test.service\"",
+ "-DTRUSTY_APP_UUID=\"d42f06c5-9dc5-455b-9914-cf094116cfa8\"",
+ "-DTRUSTY_APP_FILENAME=\"binder-test-service.syms.elf\"",
+ ],
+}
+
+cc_fuzz {
+ name: "trusty_binder_rpc_fuzzer",
+ defaults: ["trusty_fuzzer_defaults"],
+ srcs: [":trusty_tipc_fuzzer"],
+ cflags: [
+ "-DTRUSTY_APP_PORT=\"com.android.trusty.binderRpcTestService.V0\"",
+ "-DTRUSTY_APP_UUID=\"87e424e5-69d7-4bbd-8b7c-7e24812cbc94\"",
+ "-DTRUSTY_APP_FILENAME=\"binderRpcTestService.syms.elf\"",
+ ],
+}
diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp
new file mode 100644
index 0000000..e1dc9ba
--- /dev/null
+++ b/libs/bufferstreams/Android.bp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_native_license"],
+}
diff --git a/libs/bufferstreams/OWNERS b/libs/bufferstreams/OWNERS
new file mode 100644
index 0000000..32b72b8
--- /dev/null
+++ b/libs/bufferstreams/OWNERS
@@ -0,0 +1,7 @@
+carlosmr@google.com
+hibrian@google.com
+jreck@google.com
+jshargo@google.com
+
+file:/services/surfaceflinger/OWNERS
+
diff --git a/libs/bufferstreams/README.md b/libs/bufferstreams/README.md
new file mode 100644
index 0000000..860adef
--- /dev/null
+++ b/libs/bufferstreams/README.md
@@ -0,0 +1,13 @@
+# libbufferstreams: Reactive Streams for Graphics Buffers
+
+This library is currently **experimental** and **under active development**.
+It is not production ready yet.
+
+For more information on reactive streams, please see <https://www.reactive-streams.org/>
+
+## Contributing
+
+This library is natively written in Rust and exposes a C API. If you make changes to the Rust API,
+you **must** update the C API in turn. To do so, with cbindgen installed, run:
+
+```$ ./update_include.sh```
diff --git a/libs/bufferstreams/include/bufferstreams.h b/libs/bufferstreams/include/bufferstreams.h
new file mode 100644
index 0000000..5308de2
--- /dev/null
+++ b/libs/bufferstreams/include/bufferstreams.h
@@ -0,0 +1,13 @@
+/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+
+/**
+ * This function will print Hello World.
+ */
+bool hello(void);
diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp
new file mode 100644
index 0000000..95a85b5
--- /dev/null
+++ b/libs/bufferstreams/rust/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+rust_library {
+ name: "libbufferstreams",
+ crate_name: "bufferstreams",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ vendor_available: true,
+ host_supported: true,
+ min_sdk_version: "30",
+}
diff --git a/libs/bufferstreams/rust/Cargo.lock b/libs/bufferstreams/rust/Cargo.lock
new file mode 100644
index 0000000..4482dba
--- /dev/null
+++ b/libs/bufferstreams/rust/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bufferstreams"
+version = "0.1.0"
diff --git a/libs/bufferstreams/rust/Cargo.toml b/libs/bufferstreams/rust/Cargo.toml
new file mode 100644
index 0000000..d30c55c
--- /dev/null
+++ b/libs/bufferstreams/rust/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "bufferstreams"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/libs/bufferstreams/rust/cbindgen.toml b/libs/bufferstreams/rust/cbindgen.toml
new file mode 100644
index 0000000..eda837f
--- /dev/null
+++ b/libs/bufferstreams/rust/cbindgen.toml
@@ -0,0 +1,149 @@
+# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
+# for detailed documentation of every option here.
+
+
+
+language = "C"
+
+
+
+############## Options for Wrapping the Contents of the Header #################
+
+# header = "/* Text to put at the beginning of the generated file. Probably a license. */"
+# trailer = "/* Text to put at the end of the generated file */"
+# include_guard = "my_bindings_h"
+# pragma_once = true
+autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
+include_version = false
+# namespace = "my_namespace"
+namespaces = []
+using_namespaces = []
+sys_includes = []
+includes = []
+no_includes = false
+after_includes = ""
+
+
+
+
+############################ Code Style Options ################################
+
+braces = "SameLine"
+line_length = 100
+tab_width = 2
+documentation = true
+documentation_style = "auto"
+documentation_length = "full"
+line_endings = "LF" # also "CR", "CRLF", "Native"
+
+
+
+
+############################# Codegen Options ##################################
+
+style = "both"
+sort_by = "Name" # default for `fn.sort_by` and `const.sort_by`
+usize_is_size_t = true
+
+
+
+[defines]
+# "target_os = freebsd" = "DEFINE_FREEBSD"
+# "feature = serde" = "DEFINE_SERDE"
+
+
+
+[export]
+include = []
+exclude = []
+# prefix = "CAPI_"
+item_types = []
+renaming_overrides_prefixing = false
+
+
+
+[export.rename]
+
+
+
+[export.body]
+
+
+[export.mangle]
+
+
+[fn]
+rename_args = "None"
+# must_use = "MUST_USE_FUNC"
+# no_return = "NO_RETURN"
+# prefix = "START_FUNC"
+# postfix = "END_FUNC"
+args = "auto"
+sort_by = "Name"
+
+
+
+
+[struct]
+rename_fields = "None"
+# must_use = "MUST_USE_STRUCT"
+derive_constructor = false
+derive_eq = false
+derive_neq = false
+derive_lt = false
+derive_lte = false
+derive_gt = false
+derive_gte = false
+
+
+
+
+[enum]
+rename_variants = "None"
+# must_use = "MUST_USE_ENUM"
+add_sentinel = false
+prefix_with_name = false
+derive_helper_methods = false
+derive_const_casts = false
+derive_mut_casts = false
+# cast_assert_name = "ASSERT"
+derive_tagged_enum_destructor = false
+derive_tagged_enum_copy_constructor = false
+enum_class = true
+private_default_tagged_enum_constructor = false
+
+
+
+
+[const]
+allow_static_const = true
+allow_constexpr = false
+sort_by = "Name"
+
+
+
+
+[macro_expansion]
+bitflags = false
+
+
+
+
+
+
+############## Options for How Your Rust library Should Be Parsed ##############
+
+[parse]
+parse_deps = false
+# include = []
+exclude = []
+clean = false
+extra_bindings = []
+
+
+
+[parse.expand]
+crates = []
+all_features = false
+default_features = true
+features = []
\ No newline at end of file
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
new file mode 100644
index 0000000..51f1c73
--- /dev/null
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! libbufferstreams: Reactive Streams for Graphics Buffers
+
+/// This function will print Hello World.
+#[no_mangle]
+pub extern "C" fn hello() -> bool {
+ println!("Hello world.");
+ true
+}
diff --git a/libs/bufferstreams/update_include.sh b/libs/bufferstreams/update_include.sh
new file mode 100755
index 0000000..e986e9f
--- /dev/null
+++ b/libs/bufferstreams/update_include.sh
@@ -0,0 +1,2 @@
+cd rust
+cbindgen --config cbindgen.toml --crate bufferstreams --output ../include/bufferstreams.h
diff --git a/libs/fakeservicemanager/FakeServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
index 3272bbc..80661c1 100644
--- a/libs/fakeservicemanager/FakeServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -26,6 +26,8 @@
}
sp<IBinder> FakeServiceManager::checkService( const String16& name) const {
+ std::lock_guard<std::mutex> l(mMutex);
+
auto it = mNameToService.find(name);
if (it == mNameToService.end()) {
return nullptr;
@@ -36,6 +38,8 @@
status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service,
bool /*allowIsolated*/,
int /*dumpsysFlags*/) {
+ std::lock_guard<std::mutex> l(mMutex);
+
if (service == nullptr) {
return UNEXPECTED_NULL;
}
@@ -44,6 +48,8 @@
}
Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) {
+ std::lock_guard<std::mutex> l(mMutex);
+
Vector<String16> services;
for (auto const& [name, service] : mNameToService) {
(void) service;
@@ -61,10 +67,14 @@
}
bool FakeServiceManager::isDeclared(const String16& name) {
+ std::lock_guard<std::mutex> l(mMutex);
+
return mNameToService.find(name) != mNameToService.end();
}
Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) {
+ std::lock_guard<std::mutex> l(mMutex);
+
Vector<String16> out;
const String16 prefix = name + String16("/");
for (const auto& [registeredName, service] : mNameToService) {
@@ -108,6 +118,8 @@
}
void FakeServiceManager::clear() {
+ std::lock_guard<std::mutex> l(mMutex);
+
mNameToService.clear();
}
} // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index 97add24..f62241d 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -19,6 +19,7 @@
#include <binder/IServiceManager.h>
#include <map>
+#include <mutex>
#include <optional>
#include <vector>
@@ -68,6 +69,7 @@
void clear();
private:
+ mutable std::mutex mMutex;
std::map<String16, sp<IBinder>> mNameToService;
};
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 5fbae3c..715822b 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -120,6 +120,16 @@
return base::Join(soNames, ':');
}
+static sp<IGpuService> getGpuService() {
+ static const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
+ if (!binder) {
+ ALOGE("Failed to get gpu service");
+ return nullptr;
+ }
+
+ return interface_cast<IGpuService>(binder);
+}
+
/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
static GraphicsEnv env;
return env;
@@ -142,8 +152,12 @@
return appDebuggable || platformDebuggable;
}
-void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
- const std::string sphalLibraries) {
+/**
+ * APIs for updatable graphics drivers
+ */
+
+void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string& path,
+ const std::string& sphalLibraries) {
if (!mDriverPath.empty() || !mSphalLibraries.empty()) {
ALOGV("ignoring attempt to change driver path from '%s' to '%s' or change sphal libraries "
"from '%s' to '%s'",
@@ -156,6 +170,108 @@
mSphalLibraries = sphalLibraries;
}
+// Return true if all the required libraries from vndk and sphal namespace are
+// linked to the driver namespace correctly.
+bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* destNamespace,
+ android_namespace_t* vndkNamespace,
+ const std::string& sharedSphalLibraries) {
+ const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
+ if (llndkLibraries.empty()) {
+ return false;
+ }
+ if (!android_link_namespaces(destNamespace, nullptr, llndkLibraries.c_str())) {
+ ALOGE("Failed to link default namespace[%s]", dlerror());
+ return false;
+ }
+
+ const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP);
+ if (vndkspLibraries.empty()) {
+ return false;
+ }
+ if (!android_link_namespaces(destNamespace, vndkNamespace, vndkspLibraries.c_str())) {
+ ALOGE("Failed to link vndk namespace[%s]", dlerror());
+ return false;
+ }
+
+ if (sharedSphalLibraries.empty()) {
+ return true;
+ }
+
+ // Make additional libraries in sphal to be accessible
+ auto sphalNamespace = android_get_exported_namespace("sphal");
+ if (!sphalNamespace) {
+ ALOGE("Depend on these libraries[%s] in sphal, but failed to get sphal namespace",
+ sharedSphalLibraries.c_str());
+ return false;
+ }
+
+ if (!android_link_namespaces(destNamespace, sphalNamespace, sharedSphalLibraries.c_str())) {
+ ALOGE("Failed to link sphal namespace[%s]", dlerror());
+ return false;
+ }
+
+ return true;
+}
+
+android_namespace_t* GraphicsEnv::getDriverNamespace() {
+ std::lock_guard<std::mutex> lock(mNamespaceMutex);
+
+ if (mDriverNamespace) {
+ return mDriverNamespace;
+ }
+
+ if (mDriverPath.empty()) {
+ // For an application process, driver path is empty means this application is not opted in
+ // to use updatable driver. Application process doesn't have the ability to set up
+ // environment variables and hence before `getenv` call will return.
+ // For a process that is not an application process, if it's run from an environment,
+ // for example shell, where environment variables can be set, then it can opt into using
+ // udpatable driver by setting UPDATABLE_GFX_DRIVER to 1. By setting to 1 the developer
+ // driver will be used currently.
+ // TODO(b/159240322) Support the production updatable driver.
+ const char* id = getenv("UPDATABLE_GFX_DRIVER");
+ if (id == nullptr || std::strcmp(id, "1") != 0) {
+ return nullptr;
+ }
+ const sp<IGpuService> gpuService = getGpuService();
+ if (!gpuService) {
+ return nullptr;
+ }
+ mDriverPath = gpuService->getUpdatableDriverPath();
+ if (mDriverPath.empty()) {
+ return nullptr;
+ }
+ mDriverPath.append(UPDATABLE_DRIVER_ABI);
+ ALOGI("Driver path is setup via UPDATABLE_GFX_DRIVER: %s", mDriverPath.c_str());
+ }
+
+ auto vndkNamespace = android_get_exported_namespace("vndk");
+ if (!vndkNamespace) {
+ return nullptr;
+ }
+
+ mDriverNamespace = android_create_namespace("updatable gfx driver",
+ mDriverPath.c_str(), // ld_library_path
+ mDriverPath.c_str(), // default_library_path
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr, // permitted_when_isolated_path
+ nullptr);
+
+ if (!linkDriverNamespaceLocked(mDriverNamespace, vndkNamespace, mSphalLibraries)) {
+ mDriverNamespace = nullptr;
+ }
+
+ return mDriverNamespace;
+}
+
+std::string GraphicsEnv::getDriverPath() const {
+ return mDriverPath;
+}
+
+/**
+ * APIs for GpuStats
+ */
+
void GraphicsEnv::hintActivityLaunch() {
ATRACE_CALL();
@@ -310,16 +426,6 @@
extensionHashes, numStats);
}
-static sp<IGpuService> getGpuService() {
- static const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
- if (!binder) {
- ALOGE("Failed to get gpu service");
- return nullptr;
- }
-
- return interface_cast<IGpuService>(binder);
-}
-
bool GraphicsEnv::readyToSendGpuStatsLocked() {
// Only send stats for processes having at least one activity launched and that process doesn't
// skip the GraphicsEnvironment setup.
@@ -392,86 +498,121 @@
return true;
}
-void* GraphicsEnv::loadLibrary(std::string name) {
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = getAngleNamespace(),
- };
-
- std::string libName = std::string("lib") + name + "_angle.so";
-
- void* so = android_dlopen_ext(libName.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
-
- if (so) {
- ALOGD("dlopen_ext from APK (%s) success at %p", libName.c_str(), so);
- return so;
- } else {
- ALOGE("dlopen_ext(\"%s\") failed: %s", libName.c_str(), dlerror());
- }
-
- return nullptr;
-}
-
-bool GraphicsEnv::shouldUseAngle(std::string appName) {
- if (appName != mAngleAppName) {
- // Make sure we are checking the app we were init'ed for
- ALOGE("App name does not match: expected '%s', got '%s'", mAngleAppName.c_str(),
- appName.c_str());
- return false;
- }
-
- return shouldUseAngle();
-}
+/**
+ * APIs for ANGLE
+ */
bool GraphicsEnv::shouldUseAngle() {
// Make sure we are init'ed
- if (mAngleAppName.empty()) {
- ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
+ if (mPackageName.empty()) {
+ ALOGV("Package name is empty. setAngleInfo() has not been called to enable ANGLE.");
return false;
}
- return (mUseAngle == YES) ? true : false;
+ return mShouldUseAngle;
}
-void GraphicsEnv::updateUseAngle() {
- const char* ANGLE_PREFER_ANGLE = "angle";
- const char* ANGLE_PREFER_NATIVE = "native";
-
- mUseAngle = NO;
- if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
- ALOGV("User set \"Developer Options\" to force the use of ANGLE");
- mUseAngle = YES;
- } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
- ALOGV("User set \"Developer Options\" to force the use of Native");
- } else {
- ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());
- }
-}
-
-void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
- const std::string developerOptIn,
+void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseSystemAngle,
+ const std::string& packageName,
const std::vector<std::string> eglFeatures) {
- if (mUseAngle != UNKNOWN) {
- // We've already figured out an answer for this app, so just return.
- ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
- (mUseAngle == YES) ? "true" : "false");
+ if (mShouldUseAngle) {
+ // ANGLE is already set up for this application process, even if the application
+ // needs to switch from apk to system or vice versa, the application process must
+ // be killed and relaunch so that the loader can properly load ANGLE again.
+ // The architecture does not support runtime switch between drivers, so just return.
+ ALOGE("ANGLE is already set for %s", packageName.c_str());
return;
}
mAngleEglFeatures = std::move(eglFeatures);
-
ALOGV("setting ANGLE path to '%s'", path.c_str());
- mAnglePath = path;
- ALOGV("setting ANGLE app name to '%s'", appName.c_str());
- mAngleAppName = appName;
- ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
- mAngleDeveloperOptIn = developerOptIn;
-
- // Update the current status of whether we should use ANGLE or not
- updateUseAngle();
+ mAnglePath = std::move(path);
+ ALOGV("setting app package name to '%s'", packageName.c_str());
+ mPackageName = std::move(packageName);
+ mShouldUseAngle = true;
+ mShouldUseSystemAngle = shouldUseSystemAngle;
}
-void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) {
+std::string& GraphicsEnv::getPackageName() {
+ return mPackageName;
+}
+
+const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
+ return mAngleEglFeatures;
+}
+
+android_namespace_t* GraphicsEnv::getAngleNamespace() {
+ std::lock_guard<std::mutex> lock(mNamespaceMutex);
+
+ if (mAngleNamespace) {
+ return mAngleNamespace;
+ }
+
+ if (mAnglePath.empty() && !mShouldUseSystemAngle) {
+ ALOGV("mAnglePath is empty and not using system ANGLE, abort creating ANGLE namespace");
+ return nullptr;
+ }
+
+ // Construct the search paths for system ANGLE.
+ const char* const defaultLibraryPaths =
+#if defined(__LP64__)
+ "/vendor/lib64/egl:/system/lib64/egl";
+#else
+ "/vendor/lib/egl:/system/lib/egl";
+#endif
+
+ // If the application process will run on top of system ANGLE, construct the namespace
+ // with sphal namespace being the parent namespace so that search paths and libraries
+ // are properly inherited.
+ mAngleNamespace =
+ android_create_namespace("ANGLE",
+ mShouldUseSystemAngle ? defaultLibraryPaths
+ : mAnglePath.c_str(), // ld_library_path
+ mShouldUseSystemAngle
+ ? defaultLibraryPaths
+ : mAnglePath.c_str(), // default_library_path
+ ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED,
+ nullptr, // permitted_when_isolated_path
+ mShouldUseSystemAngle ? android_get_exported_namespace("sphal")
+ : nullptr); // parent
+
+ ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default");
+
+ if (!mShouldUseSystemAngle) {
+ return mAngleNamespace;
+ }
+
+ auto vndkNamespace = android_get_exported_namespace("vndk");
+ if (!vndkNamespace) {
+ return nullptr;
+ }
+
+ if (!linkDriverNamespaceLocked(mAngleNamespace, vndkNamespace, "")) {
+ mAngleNamespace = nullptr;
+ }
+
+ return mAngleNamespace;
+}
+
+void GraphicsEnv::nativeToggleAngleAsSystemDriver(bool enabled) {
+ const sp<IGpuService> gpuService = getGpuService();
+ if (!gpuService) {
+ ALOGE("No GPU service");
+ return;
+ }
+ gpuService->toggleAngleAsSystemDriver(enabled);
+}
+
+bool GraphicsEnv::shouldUseSystemAngle() {
+ return mShouldUseSystemAngle;
+}
+
+/**
+ * APIs for debuggable layers
+ */
+
+void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace,
+ const std::string& layerPaths) {
if (mLayerPaths.empty()) {
mLayerPaths = layerPaths;
mAppNamespace = appNamespace;
@@ -485,14 +626,6 @@
return mAppNamespace;
}
-std::string& GraphicsEnv::getAngleAppName() {
- return mAngleAppName;
-}
-
-const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
- return mAngleEglFeatures;
-}
-
const std::string& GraphicsEnv::getLayerPaths() {
return mLayerPaths;
}
@@ -505,141 +638,12 @@
return mDebugLayersGLES;
}
-void GraphicsEnv::setDebugLayers(const std::string layers) {
+void GraphicsEnv::setDebugLayers(const std::string& layers) {
mDebugLayers = layers;
}
-void GraphicsEnv::setDebugLayersGLES(const std::string layers) {
+void GraphicsEnv::setDebugLayersGLES(const std::string& layers) {
mDebugLayersGLES = layers;
}
-// Return true if all the required libraries from vndk and sphal namespace are
-// linked to the updatable gfx driver namespace correctly.
-bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) {
- const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
- if (llndkLibraries.empty()) {
- return false;
- }
- if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) {
- ALOGE("Failed to link default namespace[%s]", dlerror());
- return false;
- }
-
- const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP);
- if (vndkspLibraries.empty()) {
- return false;
- }
- if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) {
- ALOGE("Failed to link vndk namespace[%s]", dlerror());
- return false;
- }
-
- if (mSphalLibraries.empty()) {
- return true;
- }
-
- // Make additional libraries in sphal to be accessible
- auto sphalNamespace = android_get_exported_namespace("sphal");
- if (!sphalNamespace) {
- ALOGE("Depend on these libraries[%s] in sphal, but failed to get sphal namespace",
- mSphalLibraries.c_str());
- return false;
- }
-
- if (!android_link_namespaces(mDriverNamespace, sphalNamespace, mSphalLibraries.c_str())) {
- ALOGE("Failed to link sphal namespace[%s]", dlerror());
- return false;
- }
-
- return true;
-}
-
-android_namespace_t* GraphicsEnv::getDriverNamespace() {
- std::lock_guard<std::mutex> lock(mNamespaceMutex);
-
- if (mDriverNamespace) {
- return mDriverNamespace;
- }
-
- if (mDriverPath.empty()) {
- // For an application process, driver path is empty means this application is not opted in
- // to use updatable driver. Application process doesn't have the ability to set up
- // environment variables and hence before `getenv` call will return.
- // For a process that is not an application process, if it's run from an environment,
- // for example shell, where environment variables can be set, then it can opt into using
- // udpatable driver by setting UPDATABLE_GFX_DRIVER to 1. By setting to 1 the developer
- // driver will be used currently.
- // TODO(b/159240322) Support the production updatable driver.
- const char* id = getenv("UPDATABLE_GFX_DRIVER");
- if (id == nullptr || std::strcmp(id, "1")) {
- return nullptr;
- }
- const sp<IGpuService> gpuService = getGpuService();
- if (!gpuService) {
- return nullptr;
- }
- mDriverPath = gpuService->getUpdatableDriverPath();
- if (mDriverPath.empty()) {
- return nullptr;
- }
- mDriverPath.append(UPDATABLE_DRIVER_ABI);
- ALOGI("Driver path is setup via UPDATABLE_GFX_DRIVER: %s", mDriverPath.c_str());
- }
-
- auto vndkNamespace = android_get_exported_namespace("vndk");
- if (!vndkNamespace) {
- return nullptr;
- }
-
- mDriverNamespace = android_create_namespace("gfx driver",
- mDriverPath.c_str(), // ld_library_path
- mDriverPath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_ISOLATED,
- nullptr, // permitted_when_isolated_path
- nullptr);
-
- if (!linkDriverNamespaceLocked(vndkNamespace)) {
- mDriverNamespace = nullptr;
- }
-
- return mDriverNamespace;
-}
-
-std::string GraphicsEnv::getDriverPath() const {
- return mDriverPath;
-}
-
-android_namespace_t* GraphicsEnv::getAngleNamespace() {
- std::lock_guard<std::mutex> lock(mNamespaceMutex);
-
- if (mAngleNamespace) {
- return mAngleNamespace;
- }
-
- if (mAnglePath.empty()) {
- ALOGV("mAnglePath is empty, not creating ANGLE namespace");
- return nullptr;
- }
-
- mAngleNamespace = android_create_namespace("ANGLE",
- nullptr, // ld_library_path
- mAnglePath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED,
- nullptr, // permitted_when_isolated_path
- nullptr);
-
- ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default");
-
- return mAngleNamespace;
-}
-
-void GraphicsEnv::nativeToggleAngleAsSystemDriver(bool enabled) {
- const sp<IGpuService> gpuService = getGpuService();
- if (!gpuService) {
- ALOGE("No GPU service");
- return;
- }
- gpuService->toggleAngleAsSystemDriver(enabled);
-}
-
} // namespace android
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 4c070ae..1c0439e 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -180,9 +180,9 @@
return reply->writeUtf8AsUtf16(driverPath);
}
case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
+ int in = dup(data.readFileDescriptor());
+ int out = dup(data.readFileDescriptor());
+ int err = dup(data.readFileDescriptor());
std::vector<String16> args;
data.readString16Vector(&args);
@@ -195,6 +195,9 @@
status = shellCommand(in, out, err, args);
if (resultReceiver != nullptr) resultReceiver->send(status);
+ ::close(in);
+ ::close(out);
+ ::close(err);
return OK;
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index f9b234a..fbf2902 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -29,6 +29,11 @@
struct NativeLoaderNamespace;
+// The GraphicsEnv is a singleton per application process and is used to properly set up the
+// graphics drivers for the application process during application starts. The architecture of
+// the graphics driver loader does not support runtime switch and only supports switch to different
+// graphics drivers when application process launches and hence the only way to switch to different
+// graphics drivers is to completely kill the application process and relaunch the application.
class GraphicsEnv {
public:
static GraphicsEnv& getInstance();
@@ -55,7 +60,7 @@
// Also set additional required sphal libraries to the linker for loading
// graphics drivers. The string is a list of libraries separated by ':',
// which is required by android_link_namespaces.
- void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries);
+ void setDriverPathAndSphalLibraries(const std::string& path, const std::string& sphalLibraries);
// Get the updatable driver namespace.
android_namespace_t* getDriverNamespace();
std::string getDriverPath() const;
@@ -96,8 +101,6 @@
/*
* Apis for ANGLE
*/
- // Check if the requested app should use ANGLE.
- bool shouldUseAngle(std::string appName);
// Check if this app process should use ANGLE.
bool shouldUseAngle();
// Set a search path for loading ANGLE libraries. The path is a list of
@@ -105,83 +108,96 @@
// (libraries must be stored uncompressed and page aligned); such elements
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
- void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
- const std::vector<std::string> eglFeatures);
+ void setAngleInfo(const std::string& path, const bool useSystemAngle,
+ const std::string& packageName, const std::vector<std::string> eglFeatures);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
- // Get the app name for ANGLE debug message.
- std::string& getAngleAppName();
-
+ // Get the app package name.
+ std::string& getPackageName();
const std::vector<std::string>& getAngleEglFeatures();
+ // Set the persist.graphics.egl system property value.
+ void nativeToggleAngleAsSystemDriver(bool enabled);
+ bool shouldUseSystemAngle();
/*
* Apis for debug layer
*/
// Set additional layer search paths.
- void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths);
+ void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string& layerPaths);
// Get the app namespace for loading layers.
NativeLoaderNamespace* getAppNamespace();
// Get additional layer search paths.
const std::string& getLayerPaths();
// Set the Vulkan debug layers.
- void setDebugLayers(const std::string layers);
+ void setDebugLayers(const std::string& layers);
// Set the GL debug layers.
- void setDebugLayersGLES(const std::string layers);
+ void setDebugLayersGLES(const std::string& layers);
// Get the debug layers to load.
const std::string& getDebugLayers();
// Get the debug layers to load.
const std::string& getDebugLayersGLES();
- // Set the persist.graphics.egl system property value.
- void nativeToggleAngleAsSystemDriver(bool enabled);
private:
- enum UseAngle { UNKNOWN, YES, NO };
-
- // Load requested ANGLE library.
- void* loadLibrary(std::string name);
- // Update whether ANGLE should be used.
- void updateUseAngle();
// Link updatable driver namespace with llndk and vndk-sp libs.
- bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace);
+ bool linkDriverNamespaceLocked(android_namespace_t* destNamespace,
+ android_namespace_t* vndkNamespace,
+ const std::string& sharedSphalLibraries);
// Check whether this process is ready to send stats.
bool readyToSendGpuStatsLocked();
// Send the initial complete GpuStats to GpuService.
void sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
GraphicsEnv() = default;
+
+ // This mutex protects the namespace creation.
+ std::mutex mNamespaceMutex;
+
+ /**
+ * Updatable driver variables.
+ */
// Path to updatable driver libs.
std::string mDriverPath;
// Path to additional sphal libs linked to updatable driver namespace.
std::string mSphalLibraries;
+ // Updatable driver namespace.
+ android_namespace_t* mDriverNamespace = nullptr;
+
+ /**
+ * ANGLE variables.
+ */
+ // Path to ANGLE libs.
+ std::string mAnglePath;
+ // App's package name.
+ std::string mPackageName;
+ // ANGLE EGL features;
+ std::vector<std::string> mAngleEglFeatures;
+ // Whether ANGLE should be used.
+ bool mShouldUseAngle = false;
+ // Whether loader should load system ANGLE.
+ bool mShouldUseSystemAngle = false;
+ // ANGLE namespace.
+ android_namespace_t* mAngleNamespace = nullptr;
+
+ /**
+ * GPU metrics.
+ */
// This mutex protects mGpuStats and get gpuservice call.
std::mutex mStatsLock;
// Cache the activity launch info
bool mActivityLaunched = false;
// Information bookkept for GpuStats.
GpuStatsInfo mGpuStats;
- // Path to ANGLE libs.
- std::string mAnglePath;
- // This App's name.
- std::string mAngleAppName;
- // ANGLE developer opt in status.
- std::string mAngleDeveloperOptIn;
- // ANGLE EGL features;
- std::vector<std::string> mAngleEglFeatures;
- // Use ANGLE flag.
- UseAngle mUseAngle = UNKNOWN;
+
+ /**
+ * Debug layers.
+ */
// Vulkan debug layers libs.
std::string mDebugLayers;
// GL debug layers libs.
std::string mDebugLayersGLES;
// Additional debug layers search path.
std::string mLayerPaths;
- // This mutex protects the namespace creation.
- std::mutex mNamespaceMutex;
- // Updatable driver namespace.
- android_namespace_t* mDriverNamespace = nullptr;
- // ANGLE namespace.
- android_namespace_t* mAngleNamespace = nullptr;
- // This App's namespace.
+ // This App's namespace to open native libraries.
NativeLoaderNamespace* mAppNamespace = nullptr;
};
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index bf2d7b6..d7e7eb8 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -73,6 +73,7 @@
"android/gui/FocusRequest.aidl",
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
+ "android/gui/IWindowInfosPublisher.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
"android/gui/WindowInfo.aidl",
"android/gui/WindowInfosUpdate.aidl",
@@ -90,6 +91,7 @@
"android/gui/FocusRequest.aidl",
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
+ "android/gui/IWindowInfosPublisher.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
"android/gui/WindowInfosUpdate.aidl",
"android/gui/WindowInfo.aidl",
@@ -129,13 +131,25 @@
},
}
-filegroup {
+aidl_library {
+ name: "libgui_aidl_hdrs",
+ hdrs: [
+ "android/gui/DisplayInfo.aidl",
+ "android/gui/FocusRequest.aidl",
+ "android/gui/InputApplicationInfo.aidl",
+ "android/gui/IWindowInfosListener.aidl",
+ "android/gui/IWindowInfosPublisher.aidl",
+ "android/gui/IWindowInfosReportedListener.aidl",
+ "android/gui/WindowInfo.aidl",
+ "android/gui/WindowInfosUpdate.aidl",
+ ],
+}
+
+aidl_library {
name: "libgui_aidl",
srcs: ["aidl/**/*.aidl"],
- path: "aidl/",
- aidl: {
- deps: [":android_gui_aidl"],
- },
+ strip_import_prefix: "aidl",
+ deps: ["libgui_aidl_hdrs"],
}
filegroup {
@@ -147,9 +161,6 @@
cc_library_static {
name: "libgui_aidl_static",
vendor_available: true,
- srcs: [
- ":libgui_aidl",
- ],
shared_libs: [
"libbinder",
@@ -175,9 +186,7 @@
aidl: {
export_aidl_headers: true,
- include_dirs: [
- "frameworks/native/libs/gui",
- ],
+ libs: ["libgui_aidl"],
},
}
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 808388f..b872541 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -418,6 +418,9 @@
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false;
+ sp<IConsumerListener> listener;
+ bool callOnFrameDequeued = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameDequeued == true
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
@@ -561,10 +564,11 @@
}
if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
- if (mCore->mConsumerListener != nullptr) {
- mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
- }
+ callOnFrameDequeued = true;
+ bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
}
+
+ listener = mCore->mConsumerListener;
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -581,10 +585,8 @@
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
- if (mCore->mConsumerListener != nullptr) {
- mCore->mConsumerListener->onFrameDequeued(
- mSlots[*outSlot].mGraphicBuffer->getId());
- }
+ callOnFrameDequeued = true;
+ bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
}
mCore->mIsAllocating = false;
@@ -608,6 +610,10 @@
} // Autolock scope
}
+ if (listener != nullptr && callOnFrameDequeued) {
+ listener->onFrameDequeued(bufferId);
+ }
+
if (attachedByConsumer) {
returnFlags |= BUFFER_NEEDS_REALLOCATION;
}
@@ -647,6 +653,8 @@
BQ_LOGV("detachBuffer: slot %d", slot);
sp<IConsumerListener> listener;
+ bool callOnFrameDetached = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameDetached is true
{
std::lock_guard<std::mutex> lock(mCore->mMutex);
@@ -684,8 +692,9 @@
listener = mCore->mConsumerListener;
auto gb = mSlots[slot].mGraphicBuffer;
- if (listener != nullptr && gb != nullptr) {
- listener->onFrameDetached(gb->getId());
+ if (gb != nullptr) {
+ callOnFrameDetached = true;
+ bufferId = gb->getId();
}
mSlots[slot].mBufferState.detachProducer();
mCore->mActiveBuffers.erase(slot);
@@ -695,6 +704,10 @@
VALIDATE_CONSISTENCY();
}
+ if (listener != nullptr && callOnFrameDetached) {
+ listener->onFrameDetached(bufferId);
+ }
+
if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -1105,58 +1118,71 @@
status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
ATRACE_CALL();
BQ_LOGV("cancelBuffer: slot %d", slot);
- std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
- return NO_INIT;
+ sp<IConsumerListener> listener;
+ bool callOnFrameCancelled = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameCancelled == true
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("cancelBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
+
+ if (mCore->mSharedBufferMode) {
+ BQ_LOGE("cancelBuffer: cannot cancel a buffer in shared buffer mode");
+ return BAD_VALUE;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
+ BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
+ "(state = %s)",
+ slot, mSlots[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if (fence == nullptr) {
+ BQ_LOGE("cancelBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
+
+ mSlots[slot].mBufferState.cancel();
+
+ // After leaving shared buffer mode, the shared buffer will still be around.
+ // Mark it as no longer shared if this operation causes it to be free.
+ if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
+ mSlots[slot].mBufferState.mShared = false;
+ }
+
+ // Don't put the shared buffer on the free list.
+ if (!mSlots[slot].mBufferState.isShared()) {
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeBuffers.push_back(slot);
+ }
+
+ auto gb = mSlots[slot].mGraphicBuffer;
+ if (gb != nullptr) {
+ callOnFrameCancelled = true;
+ bufferId = gb->getId();
+ }
+ mSlots[slot].mFence = fence;
+ mCore->mDequeueCondition.notify_all();
+ listener = mCore->mConsumerListener;
+ VALIDATE_CONSISTENCY();
}
- if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("cancelBuffer: BufferQueue has no connected producer");
- return NO_INIT;
+ if (listener != nullptr && callOnFrameCancelled) {
+ listener->onFrameCancelled(bufferId);
}
- if (mCore->mSharedBufferMode) {
- BQ_LOGE("cancelBuffer: cannot cancel a buffer in shared buffer mode");
- return BAD_VALUE;
- }
-
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
- } else if (!mSlots[slot].mBufferState.isDequeued()) {
- BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (fence == nullptr) {
- BQ_LOGE("cancelBuffer: fence is NULL");
- return BAD_VALUE;
- }
-
- mSlots[slot].mBufferState.cancel();
-
- // After leaving shared buffer mode, the shared buffer will still be around.
- // Mark it as no longer shared if this operation causes it to be free.
- if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
- mSlots[slot].mBufferState.mShared = false;
- }
-
- // Don't put the shared buffer on the free list.
- if (!mSlots[slot].mBufferState.isShared()) {
- mCore->mActiveBuffers.erase(slot);
- mCore->mFreeBuffers.push_back(slot);
- }
-
- auto gb = mSlots[slot].mGraphicBuffer;
- if (mCore->mConsumerListener != nullptr && gb != nullptr) {
- mCore->mConsumerListener->onFrameCancelled(gb->getId());
- }
- mSlots[slot].mFence = fence;
- mCore->mDequeueCondition.notify_all();
- VALIDATE_CONSISTENCY();
-
return NO_ERROR;
}
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 6849a95..67cbc7b 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -99,7 +99,7 @@
if (mEventConnection != nullptr) {
auto status = mEventConnection->getLatestVsyncEventData(outVsyncEventData);
if (!status.isOk()) {
- ALOGE("Failed to get latest vsync event data: %s", status.exceptionMessage().c_str());
+ ALOGE("Failed to get latest vsync event data: %s", status.toString8().c_str());
return status.transactionError();
}
return NO_ERROR;
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index f3eb4e8..afb09de 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -255,7 +255,6 @@
uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) {
FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
if (frame == nullptr) {
- ALOGE("updateAcquireFence: Did not find frame.");
return;
}
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 05b5533..826a418 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,12 +1,7 @@
-adyabr@google.com
-alecmouri@google.com
-chaviw@google.com
chrisforbes@google.com
jreck@google.com
-lpy@google.com
-pdwilliams@google.com
-racarr@google.com
-vishnun@google.com
+
+file:/services/surfaceflinger/OWNERS
per-file EndToEndNativeInputTest.cpp = svv@google.com
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index ed69100..53a2f64 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1792,19 +1792,20 @@
int Surface::dispatchSetFrameTimelineInfo(va_list args) {
ATRACE_CALL();
- auto frameNumber = static_cast<uint64_t>(va_arg(args, uint64_t));
- auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
- auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
- auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
- auto useForRefreshRateSelection = static_cast<bool>(va_arg(args, int32_t));
-
ALOGV("Surface::%s", __func__);
+
+ const auto nativeWindowFtlInfo = static_cast<ANativeWindowFrameTimelineInfo>(
+ va_arg(args, ANativeWindowFrameTimelineInfo));
+
FrameTimelineInfo ftlInfo;
- ftlInfo.vsyncId = frameTimelineVsyncId;
- ftlInfo.inputEventId = inputEventId;
- ftlInfo.startTimeNanos = startTimeNanos;
- ftlInfo.useForRefreshRateSelection = useForRefreshRateSelection;
- return setFrameTimelineInfo(frameNumber, ftlInfo);
+ ftlInfo.vsyncId = nativeWindowFtlInfo.frameTimelineVsyncId;
+ ftlInfo.inputEventId = nativeWindowFtlInfo.inputEventId;
+ ftlInfo.startTimeNanos = nativeWindowFtlInfo.startTimeNanos;
+ ftlInfo.useForRefreshRateSelection = nativeWindowFtlInfo.useForRefreshRateSelection;
+ ftlInfo.skippedFrameVsyncId = nativeWindowFtlInfo.skippedFrameVsyncId;
+ ftlInfo.skippedFrameStartTimeNanos = nativeWindowFtlInfo.skippedFrameStartTimeNanos;
+
+ return setFrameTimelineInfo(nativeWindowFtlInfo.frameNumber, ftlInfo);
}
bool Surface::transformToDisplayInverse() const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0fda358..db99726 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -377,7 +377,6 @@
}
auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
if (!callbackFunction) {
- ALOGE("cannot call null callback function, skipping");
continue;
}
std::vector<SurfaceControlStats> surfaceControlStats;
@@ -394,6 +393,11 @@
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
+
+ // More than one transaction may contain the same callback id. Erase the callback from
+ // the map to ensure that it is only called once. This can happen if transactions are
+ // parcelled out of process and applied in both processes.
+ callbacksMap.erase(callbackId);
}
// handle on complete callbacks
@@ -1027,7 +1031,7 @@
mEarlyWakeupEnd = false;
mDesiredPresentTime = 0;
mIsAutoTimestamp = true;
- clearFrameTimelineInfo(mFrameTimelineInfo);
+ mFrameTimelineInfo = {};
mApplyToken = nullptr;
mMergedTransactionIds.clear();
}
@@ -2279,27 +2283,13 @@
if (t.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
if (other.vsyncId > t.vsyncId) {
- t.vsyncId = other.vsyncId;
- t.inputEventId = other.inputEventId;
- t.startTimeNanos = other.startTimeNanos;
- t.useForRefreshRateSelection = other.useForRefreshRateSelection;
+ t = other;
}
} else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
- t.vsyncId = other.vsyncId;
- t.inputEventId = other.inputEventId;
- t.startTimeNanos = other.startTimeNanos;
- t.useForRefreshRateSelection = other.useForRefreshRateSelection;
+ t = other;
}
}
-// copied from FrameTimelineInfo::clear()
-void SurfaceComposerClient::Transaction::clearFrameTimelineInfo(FrameTimelineInfo& t) {
- t.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
- t.inputEventId = os::IInputConstants::INVALID_INPUT_EVENT_ID;
- t.startTimeNanos = 0;
- t.useForRefreshRateSelection = false;
-}
-
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::setTrustedPresentationCallback(
const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
index 9415035..a590c86 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -2,12 +2,63 @@
"imports": [
{
"path": "frameworks/native/libs/nativewindow"
+ },
+ {
+ "path": "frameworks/native/services/surfaceflinger"
}
],
- "postsubmit": [
+ "presubmit": [
{
- // TODO(257123981): move this to presubmit after dealing with existing breakages.
- "name": "libgui_test"
+ "name": "libgui_test",
+ "options": [
+ // TODO(b/277604286): Failing on Cuttlefish.
+ {
+ "exclude-filter": "MultiTextureConsumerTest#EGLImageTargetWorks"
+ },
+
+ // TODO(b/285011590): Failing on Cuttlefish.
+ {
+ "exclude-filter": "SurfaceTest#GetHdrSupport"
+ },
+ {
+ "exclude-filter": "SurfaceTest#GetWideColorSupport"
+ },
+
+ // TODO(b/285006554): Failing on Cuttlefish.
+ {
+ "exclude-filter": "SurfaceTextureGLTest#InvalidWidthOrHeightFails"
+ },
+
+ // TODO(b/277347351): Known test data issues, failing across devices.
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferNpot"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferPow2"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferWithCrop"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BuffersRepeatedly"
+ },
+
+ // TODO(b/285041169): Hanging on Cuttlefish.
+ {
+ "exclude-filter": "SurfaceTextureGLThreadToGLTest#UpdateTexImageBeforeFrameFinishedCompletes"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLThreadToGLTest#RepeatedUpdateTexImageBeforeFrameFinishedCompletes"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLThreadToGLTest#RepeatedUpdateTexImageAfterFrameFinishedCompletes"
+ },
+
+ // TODO(b/285041070): Failing on Cuttlefish.
+ {
+ "exclude-filter": "SurfaceTextureGLToGLTest#EglDestroySurfaceUnrefsBuffers"
+ }
+ ]
}
]
}
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 6df9ff1..52af9d5 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -90,8 +90,10 @@
}
parcel->writeInt32(1);
- // Ensure that the size of the flags that we use is 32 bits for writing into the parcel.
+ // Ensure that the size of custom types are what we expect for writing into the parcel.
static_assert(sizeof(inputConfig) == 4u);
+ static_assert(sizeof(ownerPid.val()) == 4u);
+ static_assert(sizeof(ownerUid.val()) == 4u);
// clang-format off
status_t status = parcel->writeStrongBinder(token) ?:
@@ -115,8 +117,8 @@
parcel->writeFloat(transform.dsdy()) ?:
parcel->writeFloat(transform.ty()) ?:
parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
- parcel->writeInt32(ownerPid) ?:
- parcel->writeInt32(ownerUid) ?:
+ parcel->writeInt32(ownerPid.val()) ?:
+ parcel->writeInt32(ownerUid.val()) ?:
parcel->writeUtf8AsUtf16(packageName) ?:
parcel->writeInt32(inputConfig.get()) ?:
parcel->writeInt32(displayId) ?:
@@ -147,7 +149,7 @@
}
float dsdx, dtdx, tx, dtdy, dsdy, ty;
- int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt;
+ int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt;
sp<IBinder> touchableRegionCropHandleSp;
// clang-format off
@@ -167,8 +169,8 @@
parcel->readFloat(&dsdy) ?:
parcel->readFloat(&ty) ?:
parcel->readInt32(&touchOcclusionModeInt) ?:
- parcel->readInt32(&ownerPid) ?:
- parcel->readInt32(&ownerUid) ?:
+ parcel->readInt32(&ownerPidInt) ?:
+ parcel->readInt32(&ownerUidInt) ?:
parcel->readUtf8FromUtf16(&packageName) ?:
parcel->readInt32(&inputConfigInt) ?:
parcel->readInt32(&displayId) ?:
@@ -190,6 +192,8 @@
transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
inputConfig = ftl::Flags<InputConfig>(inputConfigInt);
+ ownerPid = Pid{ownerPidInt};
+ ownerUid = Uid{static_cast<uid_t>(ownerUidInt)};
touchableRegionCropHandle = touchableRegionCropHandleSp;
return OK;
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 76e7b6e..0929b8e 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -22,7 +22,6 @@
namespace android {
using gui::DisplayInfo;
-using gui::IWindowInfosReportedListener;
using gui::WindowInfo;
using gui::WindowInfosListener;
using gui::aidl_utils::statusTFromBinderStatus;
@@ -40,8 +39,13 @@
{
std::scoped_lock lock(mListenersMutex);
if (mWindowInfosListeners.empty()) {
- binder::Status s = surfaceComposer->addWindowInfosListener(this);
+ gui::WindowInfosListenerInfo listenerInfo;
+ binder::Status s = surfaceComposer->addWindowInfosListener(this, &listenerInfo);
status = statusTFromBinderStatus(s);
+ if (status == OK) {
+ mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher);
+ mListenerId = listenerInfo.listenerId;
+ }
}
if (status == OK) {
@@ -85,8 +89,7 @@
}
binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
- const gui::WindowInfosUpdate& update,
- const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
+ const gui::WindowInfosUpdate& update) {
std::unordered_set<sp<WindowInfosListener>, gui::SpHash<WindowInfosListener>>
windowInfosListeners;
@@ -104,9 +107,7 @@
listener->onWindowInfosChanged(update);
}
- if (windowInfosReportedListener) {
- windowInfosReportedListener->onWindowInfosReported();
- }
+ mWindowInfosPublisher->ackWindowInfosReceived(update.vsyncId, mListenerId);
return binder::Status::ok();
}
@@ -114,7 +115,10 @@
void WindowInfosListenerReporter::reconnect(const sp<gui::ISurfaceComposer>& composerService) {
std::scoped_lock lock(mListenersMutex);
if (!mWindowInfosListeners.empty()) {
- composerService->addWindowInfosListener(this);
+ gui::WindowInfosListenerInfo listenerInfo;
+ composerService->addWindowInfosListener(this, &listenerInfo);
+ mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher);
+ mListenerId = listenerInfo.listenerId;
}
}
diff --git a/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
index 6a86c6a..4b647a4 100644
--- a/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
+++ b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
@@ -37,4 +37,10 @@
// Whether this vsyncId should be used to heuristically select the display refresh rate
// TODO(b/281695725): Clean this up once TextureView use setFrameRate API
boolean useForRefreshRateSelection = false;
+
+ // The VsyncId of a frame that was not drawn and squashed into this frame.
+ long skippedFrameVsyncId = INVALID_VSYNC_ID;
+
+ // The start time of a frame that was not drawn and squashed into this frame.
+ long skippedFrameStartTimeNanos = 0;
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index ec3266c..539a1c1 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -40,12 +40,14 @@
import android.gui.ISurfaceComposerClient;
import android.gui.ITunnelModeEnabledListener;
import android.gui.IWindowInfosListener;
+import android.gui.IWindowInfosPublisher;
import android.gui.LayerCaptureArgs;
import android.gui.LayerDebugInfo;
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ARect;
import android.gui.StaticDisplayInfo;
+import android.gui.WindowInfosListenerInfo;
/** @hide */
interface ISurfaceComposer {
@@ -500,7 +502,7 @@
*/
int getMaxAcquiredBufferCount();
- void addWindowInfosListener(IWindowInfosListener windowInfosListener);
+ WindowInfosListenerInfo addWindowInfosListener(IWindowInfosListener windowInfosListener);
void removeWindowInfosListener(IWindowInfosListener windowInfosListener);
diff --git a/libs/gui/aidl/android/gui/WindowInfosListenerInfo.aidl b/libs/gui/aidl/android/gui/WindowInfosListenerInfo.aidl
new file mode 100644
index 0000000..0ca13b7
--- /dev/null
+++ b/libs/gui/aidl/android/gui/WindowInfosListenerInfo.aidl
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.IWindowInfosPublisher;
+
+/** @hide */
+parcelable WindowInfosListenerInfo {
+ long listenerId;
+ IWindowInfosPublisher windowInfosPublisher;
+}
\ No newline at end of file
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
index 400229d..07cb5ed 100644
--- a/libs/gui/android/gui/IWindowInfosListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -16,11 +16,9 @@
package android.gui;
-import android.gui.IWindowInfosReportedListener;
import android.gui.WindowInfosUpdate;
/** @hide */
oneway interface IWindowInfosListener {
- void onWindowInfosChanged(
- in WindowInfosUpdate update, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+ void onWindowInfosChanged(in WindowInfosUpdate update);
}
diff --git a/libs/gui/android/gui/IWindowInfosPublisher.aidl b/libs/gui/android/gui/IWindowInfosPublisher.aidl
new file mode 100644
index 0000000..5a9c328
--- /dev/null
+++ b/libs/gui/android/gui/IWindowInfosPublisher.aidl
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IWindowInfosPublisher
+{
+ void ackWindowInfosReceived(long vsyncId, long listenerId);
+}
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 872b069..073cc08 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -46,7 +46,7 @@
"android.hardware.configstore-utils",
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
"android.hidl.token@1.0",
"libSurfaceFlingerProp",
"libgui",
@@ -90,6 +90,7 @@
],
defaults: [
"libgui_fuzzer_defaults",
+ "service_fuzzer_defaults",
],
}
@@ -100,6 +101,7 @@
],
defaults: [
"libgui_fuzzer_defaults",
+ "service_fuzzer_defaults",
],
}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 8c003d8..4c7d056 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -153,8 +153,8 @@
MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
- MOCK_METHOD(binder::Status, addWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
- (override));
+ MOCK_METHOD(binder::Status, addWindowInfosListener,
+ (const sp<gui::IWindowInfosListener>&, gui::WindowInfosListenerInfo*), (override));
MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
(override));
MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
index 57720dd..3e37e48 100644
--- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Boost.h>
#include <fuzzbinder/libbinder_driver.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -39,10 +39,13 @@
ui::ColorMode::BT2100_HLG,
ui::ColorMode::DISPLAY_BT2020};
-constexpr hardware::power::Boost kBoost[] = {
- hardware::power::Boost::INTERACTION, hardware::power::Boost::DISPLAY_UPDATE_IMMINENT,
- hardware::power::Boost::ML_ACC, hardware::power::Boost::AUDIO_LAUNCH,
- hardware::power::Boost::CAMERA_LAUNCH, hardware::power::Boost::CAMERA_SHOT,
+constexpr aidl::android::hardware::power::Boost kBoost[] = {
+ aidl::android::hardware::power::Boost::INTERACTION,
+ aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT,
+ aidl::android::hardware::power::Boost::ML_ACC,
+ aidl::android::hardware::power::Boost::AUDIO_LAUNCH,
+ aidl::android::hardware::power::Boost::CAMERA_LAUNCH,
+ aidl::android::hardware::power::Boost::CAMERA_SHOT,
};
constexpr gui::TouchOcclusionMode kMode[] = {
@@ -186,8 +189,8 @@
windowInfo->touchableRegion = Region(getRect(&mFdp));
windowInfo->replaceTouchableRegionWithCrop = mFdp.ConsumeBool();
windowInfo->touchOcclusionMode = mFdp.PickValueInArray(kMode);
- windowInfo->ownerPid = mFdp.ConsumeIntegral<int32_t>();
- windowInfo->ownerUid = mFdp.ConsumeIntegral<int32_t>();
+ windowInfo->ownerPid = gui::Pid{mFdp.ConsumeIntegral<pid_t>()};
+ windowInfo->ownerUid = gui::Uid{mFdp.ConsumeIntegral<uid_t>()};
windowInfo->packageName = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes);
windowInfo->inputConfig = mFdp.PickValueInArray(kFeatures);
}
@@ -284,7 +287,7 @@
SurfaceComposerClient::doUncacheBufferTransaction(mFdp.ConsumeIntegral<uint64_t>());
SurfaceComposerClient::setDisplayBrightness(displayToken, getBrightness(&mFdp));
- hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost);
+ aidl::android::hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost);
SurfaceComposerClient::notifyPowerBoost((int32_t)boostId);
String8 surfaceName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str());
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 7c150d5..3ff6735 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -26,6 +26,7 @@
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
#include <android/gui/IWindowInfosListener.h>
+#include <android/gui/IWindowInfosPublisher.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
#include <gui/ITransactionCompletedListener.h>
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index a6f503e..7aa7068 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -267,12 +267,13 @@
layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged |
- layer_state_t::eFrameRateChanged | layer_state_t::eFixedTransformHintChanged;
+ layer_state_t::eFrameRateChanged | layer_state_t::eFrameRateSelectionPriority |
+ layer_state_t::eFixedTransformHintChanged;
// Changes affecting data sent to input.
- static constexpr uint64_t INPUT_CHANGES = layer_state_t::GEOMETRY_CHANGES |
- layer_state_t::HIERARCHY_CHANGES | layer_state_t::eInputInfoChanged |
- layer_state_t::eDropInputModeChanged | layer_state_t::eTrustedOverlayChanged;
+ static constexpr uint64_t INPUT_CHANGES = 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 =
diff --git a/libs/gui/include/gui/PidUid.h b/libs/gui/include/gui/PidUid.h
new file mode 100644
index 0000000..7930942
--- /dev/null
+++ b/libs/gui/include/gui/PidUid.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/mixins.h>
+#include <sys/types.h>
+#include <string>
+
+namespace android::gui {
+
+// Type-safe wrapper for a PID.
+struct Pid : ftl::Constructible<Pid, pid_t>, ftl::Equatable<Pid>, ftl::Orderable<Pid> {
+ using Constructible::Constructible;
+
+ const static Pid INVALID;
+
+ constexpr auto val() const { return ftl::to_underlying(*this); }
+
+ constexpr bool isValid() const { return val() >= 0; }
+
+ std::string toString() const { return std::to_string(val()); }
+};
+
+const inline Pid Pid::INVALID{-1};
+
+// Type-safe wrapper for a UID.
+// We treat the unsigned equivalent of -1 as a singular invalid value.
+struct Uid : ftl::Constructible<Uid, uid_t>, ftl::Equatable<Uid>, ftl::Orderable<Uid> {
+ using Constructible::Constructible;
+
+ const static Uid INVALID;
+
+ constexpr auto val() const { return ftl::to_underlying(*this); }
+
+ constexpr bool isValid() const { return val() != static_cast<uid_t>(-1); }
+
+ std::string toString() const { return std::to_string(val()); }
+};
+
+const inline Uid Uid::INVALID{static_cast<uid_t>(-1)};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index fb57f63..3cf57b1 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -410,7 +410,6 @@
static sp<IBinder> sApplyToken;
void releaseBufferIfOverwriting(const layer_state_t& state);
static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
- static void clearFrameTimelineInfo(FrameTimelineInfo& t);
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 70b2ee8..7ff7387 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -21,6 +21,8 @@
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <ftl/flags.h>
+#include <ftl/mixins.h>
+#include <gui/PidUid.h>
#include <gui/constants.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -223,8 +225,8 @@
Region touchableRegion;
TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED;
- int32_t ownerPid = -1;
- int32_t ownerUid = -1;
+ Pid ownerPid = Pid::INVALID;
+ Uid ownerUid = Uid::INVALID;
std::string packageName;
ftl::Flags<InputConfig> inputConfig;
int32_t displayId = ADISPLAY_ID_NONE;
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 38cb108..684e21a 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -18,7 +18,7 @@
#include <android/gui/BnWindowInfosListener.h>
#include <android/gui/ISurfaceComposer.h>
-#include <android/gui/IWindowInfosReportedListener.h>
+#include <android/gui/IWindowInfosPublisher.h>
#include <binder/IBinder.h>
#include <gui/SpHash.h>
#include <gui/WindowInfosListener.h>
@@ -30,8 +30,7 @@
class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
public:
static sp<WindowInfosListenerReporter> getInstance();
- binder::Status onWindowInfosChanged(const gui::WindowInfosUpdate& update,
- const sp<gui::IWindowInfosReportedListener>&) override;
+ binder::Status onWindowInfosChanged(const gui::WindowInfosUpdate& update) override;
status_t addWindowInfosListener(
const sp<gui::WindowInfosListener>& windowInfosListener,
const sp<gui::ISurfaceComposer>&,
@@ -47,5 +46,8 @@
std::vector<gui::WindowInfo> mLastWindowInfos GUARDED_BY(mListenersMutex);
std::vector<gui::DisplayInfo> mLastDisplayInfos GUARDED_BY(mListenersMutex);
+
+ sp<gui::IWindowInfosPublisher> mWindowInfosPublisher;
+ int64_t mListenerId;
};
} // namespace android
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index cd35d2f..462ce6e 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -21,6 +21,7 @@
],
srcs: [
+ "LibGuiMain.cpp", // Custom gtest entrypoint
"BLASTBufferQueue_test.cpp",
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
diff --git a/libs/gui/tests/AndroidTest.xml b/libs/gui/tests/AndroidTest.xml
index 5e09fff..31b10d7 100644
--- a/libs/gui/tests/AndroidTest.xml
+++ b/libs/gui/tests/AndroidTest.xml
@@ -23,6 +23,7 @@
<option name="screen-always-on" value="on" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
+ <option name="not-shardable" value="true" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="libgui_test" />
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index a3ad680..cc6f3dd 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -176,18 +176,6 @@
class BLASTBufferQueueTest : public ::testing::Test {
public:
protected:
- BLASTBufferQueueTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
- }
-
- ~BLASTBufferQueueTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(), testInfo->name());
- }
-
void SetUp() {
mComposer = ComposerService::getComposerService();
mClient = new SurfaceComposerClient();
@@ -363,6 +351,24 @@
sp<CountProducerListener> mProducerListener;
};
+// Helper class to ensure the provided BBQ frame number has been committed in SurfaceFlinger.
+class BBQSyncHelper {
+public:
+ BBQSyncHelper(BLASTBufferQueueHelper& bbqHelper, uint64_t frameNumber) {
+ t.addTransactionCompletedCallback(callbackHelper.function, callbackHelper.getContext());
+ bbqHelper.mergeWithNextTransaction(&t, frameNumber);
+ }
+
+ void wait() {
+ CallbackData callbackData;
+ callbackHelper.getCallbackData(&callbackData);
+ }
+
+private:
+ Transaction t;
+ CallbackHelper callbackHelper;
+};
+
TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
// create BLASTBufferQueue adapter associated with this surface
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
@@ -434,6 +440,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
@@ -459,7 +466,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -519,6 +526,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -543,7 +551,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -574,6 +582,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -602,7 +611,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
@@ -633,6 +642,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -665,7 +675,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -700,6 +710,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -732,7 +743,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
// Verify cropped region is scaled correctly.
@@ -758,6 +769,7 @@
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
{
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -779,7 +791,7 @@
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
- adapter.waitForCallbacks();
+ syncHelper.wait();
}
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -792,6 +804,7 @@
adapter.update(mSurfaceControl, mDisplayWidth, mDisplayHeight / 2);
{
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -814,7 +827,7 @@
NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
0, Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
- adapter.waitForCallbacks();
+ syncHelper.wait();
}
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -1335,43 +1348,6 @@
ASSERT_EQ(queuesToNativeWindow, 1);
}
-// Test a slow producer doesn't hold up a faster producer from the same client. Essentially tests
-// BBQ uses separate transaction queues.
-TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {
- sp<SurfaceControl> bgSurface =
- mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState);
- ASSERT_NE(nullptr, bgSurface.get());
- Transaction t;
- t.setLayerStack(bgSurface, ui::DEFAULT_LAYER_STACK)
- .show(bgSurface)
- .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
- .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
- .apply();
-
- BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
- sp<IGraphicBufferProducer> slowIgbProducer;
- setUpProducer(slowAdapter, slowIgbProducer);
- nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
- queueBuffer(slowIgbProducer, 0 /* r */, 255 /* g */, 0 /* b */, presentTimeDelay);
-
- BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
- sp<IGraphicBufferProducer> fastIgbProducer;
- setUpProducer(fastAdapter, fastIgbProducer);
- uint8_t r = 255;
- uint8_t g = 0;
- uint8_t b = 0;
- queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */);
- fastAdapter.waitForCallbacks();
-
- // capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
-
- ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(r, g, b,
- {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
-}
-
TEST_F(BLASTBufferQueueTest, TransformHint) {
// Transform hint is provided to BBQ via the surface control passed by WM
mSurfaceControl->setTransformHint(ui::Transform::ROT_90);
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 2f1fd3e..0168877 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -17,11 +17,14 @@
#define LOG_TAG "BufferQueue_test"
//#define LOG_NDEBUG 0
+#include "Constants.h"
#include "MockConsumer.h"
#include <gui/BufferItem.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
#include <ui/GraphicBuffer.h>
@@ -36,6 +39,7 @@
#include <gtest/gtest.h>
+#include <future>
#include <thread>
using namespace std::chrono_literals;
@@ -46,20 +50,6 @@
public:
protected:
- BufferQueueTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-
- ~BufferQueueTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-
void GetMinUndequeuedBufferCount(int* bufferCount) {
ASSERT_TRUE(bufferCount != nullptr);
ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
@@ -535,7 +525,8 @@
int slot;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
@@ -578,7 +569,8 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -592,7 +584,9 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -629,7 +623,8 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -656,7 +651,9 @@
// always return the same one.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -695,7 +692,8 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Enable shared buffer mode
@@ -712,7 +710,9 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -747,7 +747,8 @@
for (int i = 0; i < 5; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr);
+ auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr);
if (i < 2) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
result);
@@ -774,7 +775,9 @@
for (int i = 0; i < 2; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0ull, true,
HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
@@ -785,7 +788,9 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
auto startTime = systemTime();
- ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(TIMED_OUT,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_GE(systemTime() - startTime, TIMEOUT);
// We're technically attaching the same buffer multiple times (since we
@@ -806,7 +811,8 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> sourceFence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -829,7 +835,8 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
sp<GraphicBuffer> firstBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
@@ -841,7 +848,8 @@
// Dequeue a second buffer
slot = BufferQueue::INVALID_BUFFER_SLOT;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
sp<GraphicBuffer> secondBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
@@ -892,8 +900,8 @@
int slots[3] = {};
mProducer->setMaxDequeuedBufferCount(3);
for (size_t i = 0; i < 3; ++i) {
- status_t result =
- mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0,
+ TEST_PRODUCER_USAGE_BITS, nullptr, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -906,7 +914,9 @@
// The first segment is a two-buffer segment, so we only put one buffer into
// the queue at a time
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -921,16 +931,22 @@
// two-buffer segment, but then at the end, we put two buffers in the queue
// at the same time before draining it.
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -945,10 +961,14 @@
// The third segment is a triple-buffer segment, so the queue is switching
// between one buffer and two buffers deep.
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -1047,8 +1067,8 @@
int slots[4] = {};
mProducer->setMaxDequeuedBufferCount(4);
for (size_t i = 0; i < 4; ++i) {
- status_t result =
- mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0,
+ TEST_PRODUCER_USAGE_BITS, nullptr, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -1059,14 +1079,22 @@
// Get buffers in all states: dequeued, filled, acquired, free
// Fill 3 buffers
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Dequeue 1 buffer
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
// Acquire and free 1 buffer
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -1132,8 +1160,8 @@
int slots[2] = {};
ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
for (size_t i = 0; i < 2; ++i) {
- status_t result =
- mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0,
+ TEST_PRODUCER_USAGE_BITS, nullptr, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -1143,10 +1171,14 @@
// Fill 2 buffers without consumer consuming them. Verify that all
// queued buffer returns proper bufferReplaced flag
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(false, output.bufferReplaced);
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(true, output.bufferReplaced);
}
@@ -1167,7 +1199,8 @@
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
// Dequeue, request, and queue one buffer
- status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr);
+ status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
@@ -1182,7 +1215,9 @@
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
// Dequeue and queue the buffer again
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Acquire and release the buffer again. Upon acquiring, the buffer handle
@@ -1194,7 +1229,9 @@
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
// Dequeue and queue the buffer again
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Disconnect the producer end. This should clear all of the slots and mark
@@ -1224,4 +1261,86 @@
ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
}
+class Latch {
+public:
+ explicit Latch(int expected) : mExpected(expected) {}
+ Latch(const Latch&) = delete;
+ Latch& operator=(const Latch&) = delete;
+
+ void CountDown() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mExpected--;
+ if (mExpected <= 0) {
+ mCV.notify_all();
+ }
+ }
+
+ void Wait() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mCV.wait(lock, [&] { return mExpected == 0; });
+ }
+
+private:
+ int mExpected;
+ std::mutex mLock;
+ std::condition_variable mCV;
+};
+
+struct OneshotOnDequeuedListener final : public BufferItemConsumer::FrameAvailableListener {
+ OneshotOnDequeuedListener(std::function<void()>&& oneshot)
+ : mOneshotRunnable(std::move(oneshot)) {}
+
+ std::function<void()> mOneshotRunnable;
+
+ void run() {
+ if (mOneshotRunnable) {
+ mOneshotRunnable();
+ mOneshotRunnable = nullptr;
+ }
+ }
+
+ void onFrameDequeued(const uint64_t) override { run(); }
+
+ void onFrameAvailable(const BufferItem&) override {}
+};
+
+// See b/270004534
+TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<BufferItemConsumer> bufferConsumer =
+ sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ ASSERT_NE(nullptr, bufferConsumer.get());
+ sp<Surface> surface = sp<Surface>::make(producer);
+ native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
+ native_window_set_buffers_dimensions(surface.get(), 100, 100);
+
+ Latch triggerDisconnect(1);
+ Latch resumeCallback(1);
+ auto luckyListener = sp<OneshotOnDequeuedListener>::make([&]() {
+ triggerDisconnect.CountDown();
+ resumeCallback.Wait();
+ });
+ bufferConsumer->setFrameAvailableListener(luckyListener);
+
+ std::future<void> disconnecter = std::async(std::launch::async, [&]() {
+ triggerDisconnect.Wait();
+ luckyListener = nullptr;
+ bufferConsumer = nullptr;
+ resumeCallback.CountDown();
+ });
+
+ std::future<void> render = std::async(std::launch::async, [=]() {
+ ANativeWindow_Buffer buffer;
+ surface->lock(&buffer, nullptr);
+ surface->unlockAndPost();
+ });
+
+ ASSERT_EQ(std::future_status::ready, render.wait_for(1s));
+ EXPECT_EQ(nullptr, luckyListener.get());
+ EXPECT_EQ(nullptr, bufferConsumer.get());
+}
+
} // namespace android
diff --git a/libs/gui/tests/Constants.h b/libs/gui/tests/Constants.h
new file mode 100644
index 0000000..85c0f9f
--- /dev/null
+++ b/libs/gui/tests/Constants.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hardware/gralloc.h>
+
+// Arbitrary non-zero usage flag.
+constexpr uint64_t TEST_PRODUCER_USAGE_BITS = GRALLOC_USAGE_SW_READ_RARELY;
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 0a14afa..d80bd9c 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -62,7 +62,7 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
CpuConsumerTestParams params = GetParam();
- ALOGD("** Starting test %s (%d x %d, %d, 0x%x)",
+ ALOGD("** Starting parameterized test %s (%d x %d, %d, 0x%x)",
test_info->name(),
params.width, params.height,
params.maxLockedBuffers, params.format);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 4ec7a06..662e9fe 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -24,6 +24,7 @@
#include <memory>
+#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/keycodes.h>
#include <android/native_window.h>
@@ -74,6 +75,26 @@
static const int LAYER_BASE = INT32_MAX - 10;
static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
+class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener {
+public:
+ binder::Status onWindowInfosReported() override {
+ std::lock_guard<std::mutex> lock{mMutex};
+ mWindowInfosReported = true;
+ mConditionVariable.notify_one();
+ return binder::Status::ok();
+ }
+
+ void wait() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ mConditionVariable.wait(lock, [&] { return mWindowInfosReported; });
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mConditionVariable;
+ bool mWindowInfosReported{false};
+};
+
class InputSurface {
public:
InputSurface(const sp<SurfaceControl> &sc, int width, int height, bool noInputChannel = false) {
@@ -264,7 +285,10 @@
t.setPosition(mSurfaceControl, x, y);
t.setCrop(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
- t.apply(true);
+ auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make();
+ t.addWindowInfosReportedListener(reportedListener);
+ t.apply();
+ reportedListener->wait();
}
void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
@@ -821,7 +845,7 @@
// with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
// Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
// the default obscured/untrusted touch filter introduced in S.
nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
@@ -842,8 +866,8 @@
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = 22222;
- parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(100, 100);
@@ -866,8 +890,8 @@
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = 22222;
- parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(50, 50);
@@ -886,7 +910,7 @@
std::unique_ptr<InputSurface> bufferSurface =
InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- bufferSurface->mInputInfo.ownerUid = 22222;
+ bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
surface->showAt(10, 10);
bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -901,7 +925,7 @@
std::unique_ptr<BlastInputSurface> bufferSurface =
BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- bufferSurface->mInputInfo.ownerUid = 22222;
+ bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
surface->showAt(10, 10);
bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -948,13 +972,13 @@
TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.ownerUid = 11111;
+ surface->mInputInfo.ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- obscuringSurface->mInputInfo.ownerUid = 22222;
+ obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
obscuringSurface->showAt(100, 100);
injectTap(101, 101);
EXPECT_EQ(surface->consumeEvent(100), nullptr);
@@ -967,13 +991,13 @@
TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.ownerUid = 11111;
+ surface->mInputInfo.ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- obscuringSurface->mInputInfo.ownerUid = 22222;
+ obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
obscuringSurface->showAt(190, 190);
injectTap(101, 101);
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index 3ae4b6d..afeea42 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -29,10 +29,6 @@
}
void GLTest::SetUp() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
-
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
@@ -132,10 +128,6 @@
eglTerminate(mEglDisplay);
}
ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(), testInfo->name());
}
EGLint const* GLTest::getConfigAttribs() {
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index e6cb89c..b1f5d08 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "IGraphicBufferProducer_test"
//#define LOG_NDEBUG 0
+#include "Constants.h"
#include "MockConsumer.h"
#include <gtest/gtest.h>
@@ -40,7 +41,6 @@
#define TEST_API NATIVE_WINDOW_API_CPU
#define TEST_API_OTHER NATIVE_WINDOW_API_EGL // valid API that's not TEST_API
#define TEST_CONTROLLED_BY_APP false
-#define TEST_PRODUCER_USAGE_BITS (0)
namespace android {
@@ -82,11 +82,6 @@
IGraphicBufferProducerTest() {}
virtual void SetUp() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
-
mMC = new MockConsumer;
switch (GetParam()) {
@@ -111,13 +106,6 @@
ASSERT_OK(mConsumer->consumerConnect(mMC, /*controlledByApp*/ false));
}
- virtual void TearDown() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-
status_t TryConnectProducer() {
IGraphicBufferProducer::QueueBufferOutput output;
return mProducer->connect(TEST_TOKEN,
diff --git a/libs/gui/tests/LibGuiMain.cpp b/libs/gui/tests/LibGuiMain.cpp
new file mode 100644
index 0000000..10f7207
--- /dev/null
+++ b/libs/gui/tests/LibGuiMain.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+#include "log/log.h"
+
+namespace {
+
+class TestCaseLogger : public ::testing::EmptyTestEventListener {
+ void OnTestStart(const ::testing::TestInfo& testInfo) override {
+ ALOGD("Begin test: %s#%s", testInfo.test_suite_name(), testInfo.name());
+ }
+
+ void OnTestEnd(const testing::TestInfo& testInfo) override {
+ ALOGD("End test: %s#%s", testInfo.test_suite_name(), testInfo.name());
+ }
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ testing::UnitTest::GetInstance()->listeners().Append(new TestCaseLogger());
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
index 58d7cc6..376420c 100644
--- a/libs/gui/tests/Malicious.cpp
+++ b/libs/gui/tests/Malicious.cpp
@@ -151,7 +151,6 @@
sp<MaliciousBQP> malicious = getMaliciousBQP();
sp<Surface> surface = new Surface(malicious);
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
ANativeWindow_Buffer buffer;
ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
@@ -165,7 +164,6 @@
sp<MaliciousBQP> malicious = getMaliciousBQP();
sp<Surface> surface = new Surface(malicious);
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
ANativeWindow_Buffer buffer;
ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
@@ -179,7 +177,6 @@
sp<MaliciousBQP> malicious = getMaliciousBQP();
sp<Surface> surface = new Surface(malicious);
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
ANativeWindow_Buffer buffer;
ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
@@ -193,7 +190,6 @@
sp<MaliciousBQP> malicious = getMaliciousBQP();
sp<Surface> surface = new Surface(malicious);
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
ANativeWindow_Buffer buffer;
ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
diff --git a/libs/gui/tests/OWNERS b/libs/gui/tests/OWNERS
new file mode 100644
index 0000000..156efdb
--- /dev/null
+++ b/libs/gui/tests/OWNERS
@@ -0,0 +1,3 @@
+# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > Surfaces
+# Bug component: 316245 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp
+# Buganizer template url: https://b.corp.google.com/issues/new?component=316245&template=1018194 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 2f14924..f34b03e 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -30,23 +30,7 @@
namespace android {
-class StreamSplitterTest : public ::testing::Test {
-
-protected:
- StreamSplitterTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-
- ~StreamSplitterTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-};
+class StreamSplitterTest : public ::testing::Test {};
struct FakeListener : public BnConsumerListener {
virtual void onFrameAvailable(const BufferItem& /* item */) {}
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 82b6697..b28dca8 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,11 +40,6 @@
}
virtual void SetUp() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
-
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -96,11 +91,6 @@
eglDestroyContext(mEglDisplay, mEglContext);
eglDestroySurface(mEglDisplay, mEglSurface);
eglTerminate(mEglDisplay);
-
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
}
virtual EGLint const* getConfigAttribs() {
diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h
index 53eb68c..9d8af5d 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SURFACE_TEXTURE_GL_H
#define ANDROID_SURFACE_TEXTURE_GL_H
+#include "Constants.h"
#include "GLTest.h"
#include "FrameWaiter.h"
@@ -43,6 +44,7 @@
true, false);
mSTC = new Surface(producer);
mANW = mSTC;
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS));
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp());
mFW = new FrameWaiter;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 096a43c..567604d 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "Constants.h"
#include "MockConsumer.h"
#include <gtest/gtest.h>
@@ -148,6 +149,7 @@
/*listener*/listener));
const int BUFFER_COUNT = 4 + extraDiscardedBuffers;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
ANativeWindowBuffer* buffers[BUFFER_COUNT];
// Dequeue first to allocate a number of buffers
@@ -530,7 +532,8 @@
ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(),
NATIVE_WINDOW_API_CPU));
- native_window_set_buffer_count(window.get(), 4);
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), 4));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
int fence;
ANativeWindowBuffer* buffer;
@@ -560,6 +563,7 @@
/*reportBufferRemoval*/true));
const int BUFFER_COUNT = 4;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
sp<GraphicBuffer> detachedBuffer;
sp<Fence> outFence;
@@ -998,7 +1002,8 @@
}
binder::Status addWindowInfosListener(
- const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) override {
+ const sp<gui::IWindowInfosListener>& /*windowInfosListener*/,
+ gui::WindowInfosListenerInfo* /*outInfo*/) override {
return binder::Status::ok();
}
@@ -1202,7 +1207,8 @@
ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(),
NATIVE_WINDOW_API_CPU));
- native_window_set_buffer_count(mWindow.get(), 4);
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(mWindow.get(), 4));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mWindow.get(), TEST_PRODUCER_USAGE_BITS));
}
void disableFrameTimestamps() {
@@ -2068,8 +2074,9 @@
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
- native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
- native_window_set_buffers_dimensions(window.get(), 0, 0);
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(window.get(), 0, 0));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
int fence;
ANativeWindowBuffer* buffer;
@@ -2121,6 +2128,7 @@
native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270);
native_window_set_buffers_dimensions(window.get(), 0, 0);
+ native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS);
ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
EXPECT_EQ(10, buffer->width);
EXPECT_EQ(20, buffer->height);
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index 11b87ef..461fe4a 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -61,8 +61,8 @@
i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
i.touchOcclusionMode = TouchOcclusionMode::ALLOW;
- i.ownerPid = 19;
- i.ownerUid = 24;
+ i.ownerPid = gui::Pid{19};
+ i.ownerUid = gui::Uid{24};
i.packageName = "com.example.package";
i.inputConfig = WindowInfo::InputConfig::NOT_FOCUSABLE;
i.displayId = 34;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 4be7328..757cde2 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -33,6 +33,86 @@
],
}
+aidl_interface {
+ name: "inputconstants",
+ host_supported: true,
+ vendor_available: true,
+ unstable: true,
+ srcs: [
+ ":inputconstants_aidl",
+ ],
+
+ backend: {
+ rust: {
+ enabled: true,
+ },
+ },
+}
+
+rust_bindgen {
+ name: "libinput_bindgen",
+ host_supported: true,
+ crate_name: "input_bindgen",
+ visibility: ["//frameworks/native/services/inputflinger"],
+ wrapper_src: "InputWrapper.hpp",
+
+ include_dirs: [
+ "frameworks/native/include",
+ ],
+
+ source_stem: "bindings",
+
+ bindgen_flags: [
+ "--verbose",
+ "--allowlist-var=AMOTION_EVENT_FLAG_CANCELED",
+ "--allowlist-var=AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED",
+ "--allowlist-var=AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED",
+ "--allowlist-var=AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT",
+ "--allowlist-var=AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE",
+ "--allowlist-var=AMOTION_EVENT_ACTION_CANCEL",
+ "--allowlist-var=AMOTION_EVENT_ACTION_UP",
+ "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN",
+ "--allowlist-var=AMOTION_EVENT_ACTION_DOWN",
+ "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT",
+ "--allowlist-var=MAX_POINTER_ID",
+ ],
+
+ static_libs: [
+ "inputconstants-cpp",
+ "libui-types",
+ ],
+ shared_libs: ["libc++"],
+ header_libs: [
+ "native_headers",
+ "jni_headers",
+ "flatbuffer_headers",
+ ],
+}
+
+// Contains methods to help access C++ code from rust
+cc_library_static {
+ name: "libinput_from_rust_to_cpp",
+ cpp_std: "c++20",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "FromRustToCpp.cpp",
+ ],
+
+ generated_headers: [
+ "cxx-bridge-header",
+ ],
+ generated_sources: ["libinput_cxx_bridge_code"],
+
+ shared_libs: [
+ "libbase",
+ ],
+}
+
cc_library {
name: "libinput",
cpp_std: "c++20",
@@ -42,11 +122,18 @@
"-Wextra",
"-Werror",
"-Wno-unused-parameter",
+ "-Wthread-safety",
+ "-Wshadow",
+ "-Wshadow-field-in-constructor-modified",
+ "-Wshadow-uncaptured-local",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
srcs: [
+ "android/os/IInputFlinger.aidl",
"Input.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
+ "InputTransport.cpp",
"InputVerifier.cpp",
"Keyboard.cpp",
"KeyCharacterMap.cpp",
@@ -70,14 +157,19 @@
export_header_lib_headers: ["jni_headers"],
generated_headers: [
+ "cxx-bridge-header",
+ "libinput_cxx_bridge_header",
"toolbox_input_labels",
],
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"liblog",
"libPlatformProperties",
+ "libtinyxml2",
+ "libutils",
"libvintf",
],
@@ -92,68 +184,37 @@
},
static_libs: [
+ "inputconstants-cpp",
+ "libgui_window_info_static",
"libui-types",
"libtflite_static",
],
+ whole_static_libs: [
+ "libinput_rust_ffi",
+ ],
+
export_static_lib_headers: [
+ "libgui_window_info_static",
"libui-types",
],
+ export_generated_headers: [
+ "cxx-bridge-header",
+ "libinput_cxx_bridge_header",
+ ],
+
target: {
android: {
- srcs: [
- "InputTransport.cpp",
- "android/os/IInputFlinger.aidl",
- ":inputconstants_aidl",
- ],
-
- export_shared_lib_headers: ["libbinder"],
-
- shared_libs: [
- "libutils",
- "libbinder",
- ],
-
- static_libs: [
- "libgui_window_info_static",
- ],
-
- export_static_lib_headers: [
- "libgui_window_info_static",
- ],
-
required: [
"motion_predictor_model_prebuilt",
+ "motion_predictor_model_config",
],
},
host: {
- shared: {
- enabled: false,
- },
include_dirs: [
"bionic/libc/kernel/android/uapi/",
"bionic/libc/kernel/uapi",
- "frameworks/native/libs/arect/include",
- ],
- },
- host_linux: {
- srcs: [
- "InputTransport.cpp",
- "android/os/IInputConstants.aidl",
- "android/os/IInputFlinger.aidl",
- "android/os/InputConfig.aidl",
- ],
- static_libs: [
- "libhostgraphics",
- "libgui_window_info_static",
- ],
- shared_libs: [
- "libbinder",
- ],
-
- export_static_lib_headers: [
- "libgui_window_info_static",
],
},
},
diff --git a/libs/input/FromRustToCpp.cpp b/libs/input/FromRustToCpp.cpp
new file mode 100644
index 0000000..e4ce62e
--- /dev/null
+++ b/libs/input/FromRustToCpp.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <ffi/FromRustToCpp.h>
+
+namespace android {
+
+bool shouldLog(rust::Str tag) {
+ return android::base::ShouldLog(android::base::LogSeverity::DEBUG, tag.data());
+}
+
+} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 00925ba..c127411 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -497,19 +497,6 @@
}
}
-// --- PointerProperties ---
-
-bool PointerProperties::operator==(const PointerProperties& other) const {
- return id == other.id
- && toolType == other.toolType;
-}
-
-void PointerProperties::copyFrom(const PointerProperties& other) {
- id = other.id;
- toolType = other.toolType;
-}
-
-
// --- MotionEvent ---
void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 1c7cc12..c218e1e 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -18,6 +18,7 @@
#include <linux/input-event-codes.h>
#include <linux/input.h>
+#include <strings.h>
#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
@@ -434,17 +435,14 @@
// clang-format on
// --- InputEventLookup ---
-const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE};
-const std::vector<InputEventLabel> InputEventLookup::KEY_NAMES = {KEYCODES_SEQUENCE};
-
-const std::unordered_map<std::string, int> InputEventLookup::AXES = {AXES_SEQUENCE};
-
-const std::vector<InputEventLabel> InputEventLookup::AXES_NAMES = {AXES_SEQUENCE};
-
-const std::unordered_map<std::string, int> InputEventLookup::LEDS = {LEDS_SEQUENCE};
-
-const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE};
+InputEventLookup::InputEventLookup()
+ : KEYCODES({KEYCODES_SEQUENCE}),
+ KEY_NAMES({KEYCODES_SEQUENCE}),
+ AXES({AXES_SEQUENCE}),
+ AXES_NAMES({AXES_SEQUENCE}),
+ LEDS({LEDS_SEQUENCE}),
+ FLAGS({FLAGS_SEQUENCE}) {}
std::optional<int> InputEventLookup::lookupValueByLabel(
const std::unordered_map<std::string, int>& map, const char* literal) {
@@ -462,30 +460,36 @@
}
std::optional<int> InputEventLookup::getKeyCodeByLabel(const char* label) {
- return lookupValueByLabel(KEYCODES, label);
+ const auto& self = get();
+ return self.lookupValueByLabel(self.KEYCODES, label);
}
const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) {
- if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) {
- return lookupLabelByValue(KEY_NAMES, keyCode);
+ const auto& self = get();
+ if (keyCode >= 0 && static_cast<size_t>(keyCode) < self.KEYCODES.size()) {
+ return get().lookupLabelByValue(self.KEY_NAMES, keyCode);
}
return nullptr;
}
std::optional<int> InputEventLookup::getKeyFlagByLabel(const char* label) {
- return lookupValueByLabel(FLAGS, label);
+ const auto& self = get();
+ return lookupValueByLabel(self.FLAGS, label);
}
std::optional<int> InputEventLookup::getAxisByLabel(const char* label) {
- return lookupValueByLabel(AXES, label);
+ const auto& self = get();
+ return lookupValueByLabel(self.AXES, label);
}
const char* InputEventLookup::getAxisLabel(int32_t axisId) {
- return lookupLabelByValue(AXES_NAMES, axisId);
+ const auto& self = get();
+ return lookupLabelByValue(self.AXES_NAMES, axisId);
}
std::optional<int> InputEventLookup::getLedByLabel(const char* label) {
- return lookupValueByLabel(LEDS, label);
+ const auto& self = get();
+ return lookupValueByLabel(self.LEDS, label);
}
namespace {
@@ -520,6 +524,14 @@
return labels->name != nullptr ? labels->name : std::to_string(value);
}
+std::optional<int> getValue(const label* labels, const char* searchLabel) {
+ if (labels == nullptr) return {};
+ while (labels->name != nullptr && ::strcasecmp(labels->name, searchLabel) != 0) {
+ labels++;
+ }
+ return labels->name != nullptr ? std::make_optional(labels->value) : std::nullopt;
+}
+
const label* getCodeLabelsForType(int32_t type) {
switch (type) {
case EV_SYN:
@@ -553,7 +565,7 @@
if (type == EV_KEY) {
return ev_key_value_labels;
}
- if (type == EV_MSC && code == ABS_MT_TOOL_TYPE) {
+ if (type == EV_ABS && code == ABS_MT_TOOL_TYPE) {
return mt_tool_labels;
}
return nullptr;
@@ -569,4 +581,17 @@
};
}
+std::optional<int> InputEventLookup::getLinuxEvdevEventTypeByLabel(const char* label) {
+ return getValue(ev_labels, label);
+}
+
+std::optional<int> InputEventLookup::getLinuxEvdevEventCodeByLabel(int32_t type,
+ const char* label) {
+ return getValue(getCodeLabelsForType(type), label);
+}
+
+std::optional<int> InputEventLookup::getLinuxEvdevInputPropByLabel(const char* label) {
+ return getValue(input_prop_labels, label);
+}
+
} // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index f6b4648..d9b7700 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -4,6 +4,7 @@
// Provides a shared memory transport for input events.
//
#define LOG_TAG "InputTransport"
+#define ATRACE_TAG ATRACE_TAG_INPUT
#include <errno.h>
#include <fcntl.h>
@@ -13,6 +14,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
@@ -80,6 +82,7 @@
} // namespace
+using android::base::Result;
using android::base::StringPrintf;
namespace android {
@@ -406,7 +409,7 @@
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
- sp<IBinder> token = new BBinder();
+ sp<IBinder> token = sp<BBinder>::make();
std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
@@ -449,6 +452,13 @@
ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(),
ftl::enum_string(msg->header.type).c_str());
+
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")",
+ mName.c_str(), msg->header.seq, msg->header.type);
+ ATRACE_NAME(message.c_str());
+ }
return OK;
}
@@ -484,6 +494,13 @@
ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(),
ftl::enum_string(msg->header.type).c_str());
+
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32
+ ", type=0x%" PRIx32 ")",
+ mName.c_str(), msg->header.seq, msg->header.type);
+ ATRACE_NAME(message.c_str());
+ }
return OK;
}
@@ -606,8 +623,12 @@
ATRACE_NAME(message.c_str());
}
if (verifyEvents()) {
- mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
- pointerCoords, flags);
+ Result<void> result =
+ mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
+ pointerCoords, flags);
+ if (!result.ok()) {
+ LOG(FATAL) << "Bad stream: " << result.error();
+ }
}
if (debugTransportPublisher()) {
std::string transformString;
@@ -671,8 +692,8 @@
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
for (uint32_t i = 0; i < pointerCount; i++) {
- msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
- msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
+ msg.body.motion.pointers[i].properties = pointerProperties[i];
+ msg.body.motion.pointers[i].coords = pointerCoords[i];
}
return mChannel->sendMessage(&msg);
@@ -1249,13 +1270,13 @@
// We know here that the coordinates for the pointer haven't changed because we
// would've cleared the resampled bit in rewriteMessage if they had. We can't modify
// lastResample in place becasue the mapping from pointer ID to index may have changed.
- touchState.lastResample.pointers[i].copyFrom(oldLastResample.getPointerById(id));
+ touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id);
continue;
}
PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
const PointerCoords& currentCoords = current->getPointerById(id);
- resampledCoords.copyFrom(currentCoords);
+ resampledCoords = currentCoords;
if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
const PointerCoords& otherCoords = other->getPointerById(id);
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
@@ -1433,8 +1454,8 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
- pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
+ pointerProperties[i] = msg->body.motion.pointers[i].properties;
+ pointerCoords[i] = msg->body.motion.pointers[i].coords;
}
ui::Transform transform;
@@ -1463,7 +1484,7 @@
uint32_t pointerCount = msg->body.motion.pointerCount;
PointerCoords pointerCoords[pointerCount];
for (uint32_t i = 0; i < pointerCount; i++) {
- pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
+ pointerCoords[i] = msg->body.motion.pointers[i].coords;
}
event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index eb75804..341eb6f 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -18,111 +18,42 @@
#include <android-base/logging.h>
#include <input/InputVerifier.h>
+#include "input_cxx_bridge.rs.h"
+
+using android::base::Error;
+using android::base::Result;
+using android::input::RustPointerProperties;
+
+using DeviceId = int32_t;
namespace android {
-/**
- * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead
- * to inconsistent events.
- * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG"
- */
-static bool logEvents() {
- return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LogEvents", ANDROID_LOG_INFO);
-}
-
// --- InputVerifier ---
-InputVerifier::InputVerifier(const std::string& name) : mName(name){};
+InputVerifier::InputVerifier(const std::string& name)
+ : mVerifier(android::input::verifier::create(name)){};
-void InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords, int32_t flags) {
- if (logEvents()) {
- LOG(ERROR) << "Processing " << MotionEvent::actionToString(action) << " for device "
- << deviceId << " (" << pointerCount << " pointer"
- << (pointerCount == 1 ? "" : "s") << ") on " << mName;
+Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, int32_t flags) {
+ std::vector<RustPointerProperties> rpp;
+ for (size_t i = 0; i < pointerCount; i++) {
+ rpp.emplace_back(RustPointerProperties{.id = pointerProperties[i].id});
}
-
- switch (MotionEvent::getActionMasked(action)) {
- case AMOTION_EVENT_ACTION_DOWN: {
- auto [it, inserted] = mTouchingPointerIdsByDevice.insert({deviceId, {}});
- if (!inserted) {
- LOG(FATAL) << "Got ACTION_DOWN, but already have touching pointers " << it->second
- << " for device " << deviceId << " on " << mName;
- }
- it->second.set(pointerProperties[0].id);
- break;
- }
- case AMOTION_EVENT_ACTION_POINTER_DOWN: {
- auto it = mTouchingPointerIdsByDevice.find(deviceId);
- if (it == mTouchingPointerIdsByDevice.end()) {
- LOG(FATAL) << "Got POINTER_DOWN, but no touching pointers for device " << deviceId
- << " on " << mName;
- }
- it->second.set(pointerProperties[MotionEvent::getActionIndex(action)].id);
- break;
- }
- case AMOTION_EVENT_ACTION_MOVE: {
- ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "MOVE");
- break;
- }
- case AMOTION_EVENT_ACTION_POINTER_UP: {
- auto it = mTouchingPointerIdsByDevice.find(deviceId);
- if (it == mTouchingPointerIdsByDevice.end()) {
- LOG(FATAL) << "Got POINTER_UP, but no touching pointers for device " << deviceId
- << " on " << mName;
- }
- it->second.reset(pointerProperties[MotionEvent::getActionIndex(action)].id);
- break;
- }
- case AMOTION_EVENT_ACTION_UP: {
- auto it = mTouchingPointerIdsByDevice.find(deviceId);
- if (it == mTouchingPointerIdsByDevice.end()) {
- LOG(FATAL) << "Got ACTION_UP, but no record for deviceId " << deviceId << " on "
- << mName;
- }
- const auto& [_, touchingPointerIds] = *it;
- if (touchingPointerIds.count() != 1) {
- LOG(FATAL) << "Got ACTION_UP, but we have pointers: " << touchingPointerIds
- << " for deviceId " << deviceId << " on " << mName;
- }
- const int32_t pointerId = pointerProperties[0].id;
- if (!touchingPointerIds.test(pointerId)) {
- LOG(FATAL) << "Got ACTION_UP, but pointerId " << pointerId
- << " is not touching. Touching pointers: " << touchingPointerIds
- << " for deviceId " << deviceId << " on " << mName;
- }
- mTouchingPointerIdsByDevice.erase(it);
- break;
- }
- case AMOTION_EVENT_ACTION_CANCEL: {
- if ((flags & AMOTION_EVENT_FLAG_CANCELED) != AMOTION_EVENT_FLAG_CANCELED) {
- LOG(FATAL) << "For ACTION_CANCEL, must set FLAG_CANCELED";
- }
- ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "CANCEL");
- mTouchingPointerIdsByDevice.erase(deviceId);
- break;
- }
+ rust::Slice<const RustPointerProperties> properties{rpp.data(), rpp.size()};
+ rust::String errorMessage =
+ android::input::verifier::process_movement(*mVerifier, deviceId, action, properties,
+ static_cast<uint32_t>(flags));
+ if (errorMessage.empty()) {
+ return {};
+ } else {
+ return Error() << errorMessage;
}
}
-void InputVerifier::ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const char* action) const {
- auto it = mTouchingPointerIdsByDevice.find(deviceId);
- if (it == mTouchingPointerIdsByDevice.end()) {
- LOG(FATAL) << "Got " << action << ", but no touching pointers for device " << deviceId
- << " on " << mName;
- }
- const auto& [_, touchingPointerIds] = *it;
- for (size_t i = 0; i < pointerCount; i++) {
- const int32_t pointerId = pointerProperties[i].id;
- if (!touchingPointerIds.test(pointerId)) {
- LOG(FATAL) << "Got " << action << " for pointerId " << pointerId
- << " but the touching pointers are " << touchingPointerIds << " on "
- << mName;
- }
- }
-};
+void InputVerifier::resetDevice(DeviceId deviceId) {
+ android::input::verifier::reset_device(*mVerifier, deviceId);
+}
} // namespace android
diff --git a/libs/input/InputWrapper.hpp b/libs/input/InputWrapper.hpp
new file mode 100644
index 0000000..a01080d
--- /dev/null
+++ b/libs/input/InputWrapper.hpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/input.h>
+#include "input/Input.h"
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index a425b93..0961a9d 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -36,9 +36,6 @@
namespace android {
namespace {
-const int64_t PREDICTION_INTERVAL_NANOS =
- 12500000 / 3; // TODO(b/266747937): Get this from the model.
-
/**
* Log debug messages about predictions.
* Enable this via "adb shell setprop log.tag.MotionPredictor DEBUG"
@@ -70,7 +67,7 @@
android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
// We still have an active gesture for another device. The provided MotionEvent is not
- // consistent the previous gesture.
+ // consistent with the previous gesture.
LOG(ERROR) << "Inconsistent event stream: last event is " << *mLastEvent << ", but "
<< __func__ << " is called with " << event;
return android::base::Error()
@@ -86,9 +83,10 @@
// Initialise the model now that it's likely to be used.
if (!mModel) {
mModel = TfLiteMotionPredictorModel::create();
+ LOG_ALWAYS_FATAL_IF(!mModel);
}
- if (mBuffers == nullptr) {
+ if (!mBuffers) {
mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
}
@@ -136,6 +134,16 @@
mLastEvent = MotionEvent();
}
mLastEvent->copyFrom(&event, /*keepHistory=*/false);
+
+ // Pass input event to the MetricsManager.
+ if (!mMetricsManager) {
+ mMetricsManager =
+ std::make_optional<MotionPredictorMetricsManager>(mModel->config()
+ .predictionInterval,
+ mModel->outputLength());
+ }
+ mMetricsManager->onRecord(event);
+
return {};
}
@@ -177,19 +185,30 @@
const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) {
- const TfLiteMotionPredictorSample::Point point =
- convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
+ if (predictedR[i] < mModel->config().distanceNoiseFloor) {
+ // Stop predicting when the predicted output is below the model's noise floor.
+ //
+ // We assume that all subsequent predictions in the batch are unreliable because later
+ // predictions are conditional on earlier predictions, and a state of noise is not a
+ // good basis for prediction.
+ //
+ // The UX trade-off is that this potentially sacrifices some predictions when the input
+ // device starts to speed up, but avoids producing noisy predictions as it slows down.
+ break;
+ }
// TODO(b/266747654): Stop predictions if confidence is < some threshold.
- ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.y);
+ const TfLiteMotionPredictorSample::Point predictedPoint =
+ convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
+
+ ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, predictedPoint.x, predictedPoint.y);
PointerCoords coords;
coords.clear();
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, point.x);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, point.y);
- // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold.
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, predictedPoint.x);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, predictedPoint.y);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
- predictionTime += PREDICTION_INTERVAL_NANOS;
+ predictionTime += mModel->config().predictionInterval;
if (i == 0) {
hasPredictions = true;
prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
@@ -206,12 +225,17 @@
}
axisFrom = axisTo;
- axisTo = point;
+ axisTo = predictedPoint;
}
- // TODO(b/266747511): Interpolate to futureTime?
+
if (!hasPredictions) {
return nullptr;
}
+
+ // Pass predictions to the MetricsManager.
+ LOG_ALWAYS_FATAL_IF(!mMetricsManager);
+ mMetricsManager->onPredict(*prediction);
+
return prediction;
}
diff --git a/libs/input/OWNERS b/libs/input/OWNERS
new file mode 100644
index 0000000..c88bfe9
--- /dev/null
+++ b/libs/input/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 85fa176..5984b4d3 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -36,6 +36,7 @@
#define ATRACE_TAG ATRACE_TAG_INPUT
#include <cutils/trace.h>
#include <log/log.h>
+#include <utils/Timers.h>
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/core/api/op_resolver.h"
@@ -44,6 +45,8 @@
#include "tensorflow/lite/model.h"
#include "tensorflow/lite/mutable_op_resolver.h"
+#include "tinyxml2.h"
+
namespace android {
namespace {
@@ -72,16 +75,41 @@
std::string getModelPath() {
#if defined(__ANDROID__)
- static const char* oemModel = "/vendor/etc/motion_predictor_model.fb";
+ static const char* oemModel = "/vendor/etc/motion_predictor_model.tflite";
if (fileExists(oemModel)) {
return oemModel;
}
- return "/system/etc/motion_predictor_model.fb";
+ return "/system/etc/motion_predictor_model.tflite";
#else
- return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
+ return base::GetExecutableDirectory() + "/motion_predictor_model.tflite";
#endif
}
+std::string getConfigPath() {
+ // The config file should be alongside the model file.
+ return base::Dirname(getModelPath()) + "/motion_predictor_config.xml";
+}
+
+int64_t parseXMLInt64(const tinyxml2::XMLElement& configRoot, const char* elementName) {
+ const tinyxml2::XMLElement* element = configRoot.FirstChildElement(elementName);
+ LOG_ALWAYS_FATAL_IF(!element, "Could not find '%s' element", elementName);
+
+ int64_t value = 0;
+ LOG_ALWAYS_FATAL_IF(element->QueryInt64Text(&value) != tinyxml2::XML_SUCCESS,
+ "Failed to parse %s: %s", elementName, element->GetText());
+ return value;
+}
+
+float parseXMLFloat(const tinyxml2::XMLElement& configRoot, const char* elementName) {
+ const tinyxml2::XMLElement* element = configRoot.FirstChildElement(elementName);
+ LOG_ALWAYS_FATAL_IF(!element, "Could not find '%s' element", elementName);
+
+ float value = 0;
+ LOG_ALWAYS_FATAL_IF(element->QueryFloatText(&value) != tinyxml2::XML_SUCCESS,
+ "Failed to parse %s: %s", elementName, element->GetText());
+ return value;
+}
+
// A TFLite ErrorReporter that logs to logcat.
class LoggingErrorReporter : public tflite::ErrorReporter {
public:
@@ -134,6 +162,7 @@
::tflite::ops::builtin::Register_CONCATENATION());
resolver->AddBuiltin(::tflite::BuiltinOperator_FULLY_CONNECTED,
::tflite::ops::builtin::Register_FULLY_CONNECTED());
+ resolver->AddBuiltin(::tflite::BuiltinOperator_GELU, ::tflite::ops::builtin::Register_GELU());
return resolver;
}
@@ -190,13 +219,7 @@
float phi = 0;
float orientation = 0;
- // Ignore the sample if there is no movement. These samples can occur when there's change to a
- // property other than the coordinates and pollute the input to the model.
- if (r == 0) {
- return;
- }
-
- if (!mAxisFrom) { // Second point.
+ if (!mAxisFrom && r > 0) { // Second point.
// We can only determine the distance from the first point, and not any
// angle. However, if the second point forms an axis, the orientation can
// be transformed relative to that axis.
@@ -217,8 +240,10 @@
}
// Update the axis for the next point.
- mAxisFrom = mAxisTo;
- mAxisTo = sample;
+ if (r > 0) {
+ mAxisFrom = mAxisTo;
+ mAxisTo = sample;
+ }
// Push the current sample onto the end of the input buffers.
mInputR.pushBack(r);
@@ -246,13 +271,26 @@
PLOG(FATAL) << "Failed to mmap model";
}
+ const std::string configPath = getConfigPath();
+ tinyxml2::XMLDocument configDocument;
+ LOG_ALWAYS_FATAL_IF(configDocument.LoadFile(configPath.c_str()) != tinyxml2::XML_SUCCESS,
+ "Failed to load config file from %s", configPath.c_str());
+
+ // Parse configuration file.
+ const tinyxml2::XMLElement* configRoot = configDocument.FirstChildElement("motion-predictor");
+ LOG_ALWAYS_FATAL_IF(!configRoot);
+ Config config{
+ .predictionInterval = parseXMLInt64(*configRoot, "prediction-interval"),
+ .distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"),
+ };
+
return std::unique_ptr<TfLiteMotionPredictorModel>(
- new TfLiteMotionPredictorModel(std::move(modelBuffer)));
+ new TfLiteMotionPredictorModel(std::move(modelBuffer), std::move(config)));
}
TfLiteMotionPredictorModel::TfLiteMotionPredictorModel(
- std::unique_ptr<android::base::MappedFile> model)
- : mFlatBuffer(std::move(model)) {
+ std::unique_ptr<android::base::MappedFile> model, Config config)
+ : mFlatBuffer(std::move(model)), mConfig(std::move(config)) {
CHECK(mFlatBuffer);
mErrorReporter = std::make_unique<LoggingErrorReporter>();
mModel = tflite::FlatBufferModel::VerifyAndBuildFromBuffer(mFlatBuffer->data(),
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 87c7768..8704eee 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -16,10 +16,11 @@
#define LOG_TAG "VelocityTracker"
-#include <array>
+#include <android-base/logging.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
+#include <array>
#include <optional>
#include <input/PrintTools.h>
@@ -243,6 +244,11 @@
void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis,
float position) {
+ if (pointerId < 0 || pointerId > MAX_POINTER_ID) {
+ LOG(FATAL) << "Invalid pointer ID " << pointerId << " for axis "
+ << MotionEvent::getLabel(axis);
+ }
+
if (mCurrentPointerIdBits.hasBit(pointerId) &&
std::chrono::nanoseconds(eventTime - mLastEventTime) > ASSUME_POINTER_STOPPED_TIME) {
ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state.",
diff --git a/libs/input/ffi/FromRustToCpp.h b/libs/input/ffi/FromRustToCpp.h
new file mode 100644
index 0000000..889945c
--- /dev/null
+++ b/libs/input/ffi/FromRustToCpp.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rust/cxx.h"
+
+namespace android {
+
+bool shouldLog(rust::Str tag);
+
+} // namespace android
diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp
new file mode 100644
index 0000000..018d199
--- /dev/null
+++ b/libs/input/rust/Android.bp
@@ -0,0 +1,72 @@
+// Copyright 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+rust_defaults {
+ name: "libinput_rust_defaults",
+ crate_name: "input",
+ srcs: ["lib.rs"],
+ host_supported: true,
+ rustlibs: [
+ "libbitflags",
+ "libcxx",
+ "libinput_bindgen",
+ "liblogger",
+ "liblog_rust",
+ "inputconstants-rust",
+ ],
+ whole_static_libs: [
+ "libinput_from_rust_to_cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+rust_library {
+ name: "libinput_rust",
+ defaults: ["libinput_rust_defaults"],
+}
+
+rust_ffi_static {
+ name: "libinput_rust_ffi",
+ defaults: ["libinput_rust_defaults"],
+}
+
+rust_test {
+ name: "libinput_rust_test",
+ defaults: ["libinput_rust_defaults"],
+ test_options: {
+ unit_test: true,
+ },
+ test_suites: ["device_tests"],
+ sanitize: {
+ hwaddress: true,
+ },
+}
+
+genrule {
+ name: "libinput_cxx_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["input_cxx_bridge_generated.cpp"],
+}
+
+genrule {
+ name: "libinput_cxx_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["input_cxx_bridge.rs.h"],
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
new file mode 100644
index 0000000..9d3b386
--- /dev/null
+++ b/libs/input/rust/input.rs
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Common definitions of the Android Input Framework in rust.
+
+use bitflags::bitflags;
+use std::fmt;
+
+/// The InputDevice ID.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct DeviceId(pub i32);
+
+/// A rust enum representation of a MotionEvent action.
+#[repr(u32)]
+pub enum MotionAction {
+ /// ACTION_DOWN
+ Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ /// ACTION_UP
+ Up = input_bindgen::AMOTION_EVENT_ACTION_UP,
+ /// ACTION_MOVE
+ Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ /// ACTION_CANCEL
+ Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+ /// ACTION_OUTSIDE
+ Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE,
+ /// ACTION_POINTER_DOWN
+ PointerDown {
+ /// The index of the affected pointer.
+ action_index: usize,
+ } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN,
+ /// ACTION_POINTER_UP
+ PointerUp {
+ /// The index of the affected pointer.
+ action_index: usize,
+ } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP,
+ /// ACTION_HOVER_ENTER
+ HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ /// ACTION_HOVER_MOVE
+ HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
+ /// ACTION_HOVER_EXIT
+ HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
+ /// ACTION_SCROLL
+ Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
+ /// ACTION_BUTTON_PRESS
+ ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ /// ACTION_BUTTON_RELEASE
+ ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+}
+
+impl fmt::Display for MotionAction {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ MotionAction::Down => write!(f, "DOWN"),
+ MotionAction::Up => write!(f, "UP"),
+ MotionAction::Move => write!(f, "MOVE"),
+ MotionAction::Cancel => write!(f, "CANCEL"),
+ MotionAction::Outside => write!(f, "OUTSIDE"),
+ MotionAction::PointerDown { action_index } => {
+ write!(f, "POINTER_DOWN({})", action_index)
+ }
+ MotionAction::PointerUp { action_index } => write!(f, "POINTER_UP({})", action_index),
+ MotionAction::HoverMove => write!(f, "HOVER_MOVE"),
+ MotionAction::Scroll => write!(f, "SCROLL"),
+ MotionAction::HoverEnter => write!(f, "HOVER_ENTER"),
+ MotionAction::HoverExit => write!(f, "HOVER_EXIT"),
+ MotionAction::ButtonPress => write!(f, "BUTTON_PRESS"),
+ MotionAction::ButtonRelease => write!(f, "BUTTON_RELEASE"),
+ }
+ }
+}
+
+impl From<u32> for MotionAction {
+ fn from(action: u32) -> Self {
+ let (action_masked, action_index) = MotionAction::breakdown_action(action);
+ match action_masked {
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
+ input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up,
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move,
+ input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel,
+ input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside,
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => {
+ MotionAction::PointerDown { action_index }
+ }
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => {
+ MotionAction::PointerUp { action_index }
+ }
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter,
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
+ input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
+ input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
+ input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
+ _ => panic!("Unknown action: {}", action),
+ }
+ }
+}
+
+impl MotionAction {
+ fn breakdown_action(action: u32) -> (u32, usize) {
+ let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
+ let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ (action_masked, index.try_into().unwrap())
+ }
+}
+
+bitflags! {
+ /// MotionEvent flags.
+ pub struct MotionFlags: u32 {
+ /// FLAG_CANCELED
+ const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED as u32;
+ /// FLAG_WINDOW_IS_OBSCURED
+ const WINDOW_IS_OBSCURED = input_bindgen::AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ /// FLAG_WINDOW_IS_PARTIALLY_OBSCURED
+ const WINDOW_IS_PARTIALLY_OBSCURED =
+ input_bindgen::AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ /// FLAG_IS_ACCESSIBILITY_EVENT
+ const IS_ACCESSIBILITY_EVENT =
+ input_bindgen::AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
+ /// FLAG_NO_FOCUS_CHANGE
+ const NO_FOCUS_CHANGE = input_bindgen::AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ }
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
new file mode 100644
index 0000000..64c0466
--- /dev/null
+++ b/libs/input/rust/input_verifier.rs
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Contains the InputVerifier, used to validate a stream of input events.
+
+use crate::ffi::RustPointerProperties;
+use crate::input::{DeviceId, MotionAction, MotionFlags};
+use log::info;
+use std::collections::HashMap;
+use std::collections::HashSet;
+
+/// The InputVerifier is used to validate a stream of input events.
+pub struct InputVerifier {
+ name: String,
+ should_log: bool,
+ touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
+ hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
+}
+
+impl InputVerifier {
+ /// Create a new InputVerifier.
+ pub fn new(name: &str, should_log: bool) -> Self {
+ logger::init(
+ logger::Config::default()
+ .with_tag_on_device("InputVerifier")
+ .with_min_level(log::Level::Trace),
+ );
+ Self {
+ name: name.to_owned(),
+ should_log,
+ touching_pointer_ids_by_device: HashMap::new(),
+ hovering_pointer_ids_by_device: HashMap::new(),
+ }
+ }
+
+ /// Process a pointer movement event from an InputDevice.
+ /// If the event is not valid, we return an error string that describes the issue.
+ pub fn process_movement(
+ &mut self,
+ device_id: DeviceId,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: MotionFlags,
+ ) -> Result<(), String> {
+ if self.should_log {
+ info!(
+ "Processing {} for device {:?} ({} pointer{}) on {}",
+ MotionAction::from(action).to_string(),
+ device_id,
+ pointer_properties.len(),
+ if pointer_properties.len() == 1 { "" } else { "s" },
+ self.name
+ );
+ }
+
+ match action.into() {
+ MotionAction::Down => {
+ let it = self
+ .touching_pointer_ids_by_device
+ .entry(device_id)
+ .or_insert_with(HashSet::new);
+ let pointer_id = pointer_properties[0].id;
+ if it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
+ self.name, device_id, it
+ ));
+ }
+ it.insert(pointer_id);
+ }
+ MotionAction::PointerDown { action_index } => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Received POINTER_DOWN but no pointers are currently down \
+ for device {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ let pointer_id = pointer_properties[action_index].id;
+ if it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Pointer with id={} not found in the properties",
+ self.name, pointer_id
+ ));
+ }
+ it.insert(pointer_id);
+ }
+ MotionAction::Move => {
+ if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ return Err(format!(
+ "{}: ACTION_MOVE touching pointers don't match",
+ self.name
+ ));
+ }
+ }
+ MotionAction::PointerUp { action_index } => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Received POINTER_UP but no pointers are currently down for device \
+ {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ let pointer_id = pointer_properties[action_index].id;
+ it.remove(&pointer_id);
+ }
+ MotionAction::Up => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{} Received ACTION_UP but no pointers are currently down for device {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ if it.len() != 1 {
+ return Err(format!(
+ "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
+ self.name, it, device_id
+ ));
+ }
+ let pointer_id = pointer_properties[0].id;
+ if !it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
+ {:?} for device {:?}",
+ self.name, pointer_id, it, device_id
+ ));
+ }
+ self.touching_pointer_ids_by_device.remove(&device_id);
+ }
+ MotionAction::Cancel => {
+ if !flags.contains(MotionFlags::CANCELED) {
+ return Err(format!(
+ "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
+ self.name
+ ));
+ }
+ if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ return Err(format!(
+ "{}: Got ACTION_CANCEL, but the pointers don't match. \
+ Existing pointers: {:?}",
+ self.name, self.touching_pointer_ids_by_device
+ ));
+ }
+ self.touching_pointer_ids_by_device.remove(&device_id);
+ }
+ /*
+ * The hovering protocol currently supports a single pointer only, because we do not
+ * have ACTION_HOVER_POINTER_ENTER or ACTION_HOVER_POINTER_EXIT.
+ * Still, we are keeping the infrastructure here pretty general in case that is
+ * eventually supported.
+ */
+ MotionAction::HoverEnter => {
+ if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
+ {:?}",
+ self.name, device_id, self.hovering_pointer_ids_by_device
+ ));
+ }
+ let it = self
+ .hovering_pointer_ids_by_device
+ .entry(device_id)
+ .or_insert_with(HashSet::new);
+ it.insert(pointer_properties[0].id);
+ }
+ MotionAction::HoverMove => {
+ // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
+ // If there was no prior HOVER_ENTER, just start a new hovering pointer.
+ let it = self
+ .hovering_pointer_ids_by_device
+ .entry(device_id)
+ .or_insert_with(HashSet::new);
+ it.insert(pointer_properties[0].id);
+ }
+ MotionAction::HoverExit => {
+ if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
+ self.name, device_id
+ ));
+ }
+ let pointer_id = pointer_properties[0].id;
+ let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ it.remove(&pointer_id);
+
+ if !it.is_empty() {
+ return Err(format!(
+ "{}: Removed hovering pointer {}, but pointers are still\
+ hovering for device {:?}: {:?}",
+ self.name, pointer_id, device_id, it
+ ));
+ }
+ self.hovering_pointer_ids_by_device.remove(&device_id);
+ }
+ _ => return Ok(()),
+ }
+ Ok(())
+ }
+
+ /// Notify the verifier that the device has been reset, which will cause the verifier to erase
+ /// the current internal state for this device. Subsequent events from this device are expected
+ //// to start a new gesture.
+ pub fn reset_device(&mut self, device_id: DeviceId) {
+ self.touching_pointer_ids_by_device.remove(&device_id);
+ self.hovering_pointer_ids_by_device.remove(&device_id);
+ }
+
+ fn ensure_touching_pointers_match(
+ &self,
+ device_id: DeviceId,
+ pointer_properties: &[RustPointerProperties],
+ ) -> bool {
+ let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
+ return false;
+ };
+
+ for pointer_property in pointer_properties.iter() {
+ let pointer_id = pointer_property.id;
+ if !pointers.contains(&pointer_id) {
+ return false;
+ }
+ }
+ true
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_verifier::InputVerifier;
+ use crate::DeviceId;
+ use crate::MotionFlags;
+ use crate::RustPointerProperties;
+ #[test]
+ fn single_pointer_stream() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn multi_device_stream() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(2),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(2),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn action_cancel() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+ &pointer_properties,
+ MotionFlags::CANCELED,
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn invalid_action_cancel() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+ &pointer_properties,
+ MotionFlags::empty(), // forgot to set FLAG_CANCELED
+ )
+ .is_err());
+ }
+
+ #[test]
+ fn invalid_up() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_err());
+ }
+
+ #[test]
+ fn correct_hover_sequence() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn double_hover_enter() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_err());
+ }
+}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
new file mode 100644
index 0000000..688d941
--- /dev/null
+++ b/libs/input/rust/lib.rs
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! The rust component of libinput.
+
+mod input;
+mod input_verifier;
+
+pub use input::{DeviceId, MotionAction, MotionFlags};
+pub use input_verifier::InputVerifier;
+
+#[cxx::bridge(namespace = "android::input")]
+mod ffi {
+ #[namespace = "android"]
+ unsafe extern "C++" {
+ include!("ffi/FromRustToCpp.h");
+ fn shouldLog(tag: &str) -> bool;
+ }
+
+ #[namespace = "android::input::verifier"]
+ extern "Rust" {
+ /// Used to validate the incoming motion stream.
+ /// This class is not thread-safe.
+ /// State is stored in the "InputVerifier" object
+ /// that can be created via the 'create' method.
+ /// Usage:
+ ///
+ /// ```ignore
+ /// Box<InputVerifier> verifier = create("inputChannel name");
+ /// result = process_movement(verifier, ...);
+ /// if (result) {
+ /// crash(result.error_message());
+ /// }
+ /// ```
+ type InputVerifier;
+ fn create(name: String) -> Box<InputVerifier>;
+ fn process_movement(
+ verifier: &mut InputVerifier,
+ device_id: i32,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: u32,
+ ) -> String;
+ fn reset_device(verifier: &mut InputVerifier, device_id: i32);
+ }
+
+ #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+ pub struct RustPointerProperties {
+ pub id: i32,
+ }
+}
+
+use crate::ffi::RustPointerProperties;
+
+fn create(name: String) -> Box<InputVerifier> {
+ Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents")))
+}
+
+fn process_movement(
+ verifier: &mut InputVerifier,
+ device_id: i32,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: u32,
+) -> String {
+ let result = verifier.process_movement(
+ DeviceId(device_id),
+ action,
+ pointer_properties,
+ MotionFlags::from_bits(flags).unwrap(),
+ );
+ match result {
+ Ok(()) => "".to_string(),
+ Err(e) => e,
+ }
+}
+
+fn reset_device(verifier: &mut InputVerifier, device_id: i32) {
+ verifier.reset_device(DeviceId(device_id));
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 6aae25d..cadac88 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -64,12 +64,13 @@
"libcutils",
"liblog",
"libPlatformProperties",
+ "libtinyxml2",
"libutils",
"libvintf",
],
data: [
"data/*",
- ":motion_predictor_model.fb",
+ ":motion_predictor_model",
],
test_options: {
unit_test: true,
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 7a62f5e..4ac7ae9 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -72,11 +72,20 @@
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
+TEST(MotionPredictorTest, StationaryNoiseFloor) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
+ []() { return true /*enable prediction*/; });
+ predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
+ predictor.record(getMotionEvent(MOVE, 0, 1, 35ms)); // No movement.
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+ ASSERT_EQ(nullptr, predicted);
+}
+
TEST(MotionPredictorTest, Offset) {
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
[]() { return true /*enable prediction*/; });
predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
- predictor.record(getMotionEvent(MOVE, 0, 2, 35ms));
+ predictor.record(getMotionEvent(MOVE, 0, 5, 35ms)); // Move enough to overcome the noise floor.
std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
ASSERT_NE(nullptr, predicted);
ASSERT_GE(predicted->getEventTime(), 41);
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 8060705..e7b2195 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -40,6 +40,80 @@
using namespace android;
// ----------------------------------------------------------------------------
+// Validate hardware_buffer.h and PixelFormat.aidl agree
+// ----------------------------------------------------------------------------
+
+static_assert(HAL_PIXEL_FORMAT_RGBA_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGBX_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGB_565 == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGB_888 == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGBA_FP16 == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGBA_1010102 == AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_BLOB == AHARDWAREBUFFER_FORMAT_BLOB,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_16 == AHARDWAREBUFFER_FORMAT_D16_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_24 == AHARDWAREBUFFER_FORMAT_D24_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_32F == AHARDWAREBUFFER_FORMAT_D32_FLOAT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_STENCIL_8 == AHARDWAREBUFFER_FORMAT_S8_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_BGRA_8888 == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YV12 == AHARDWAREBUFFER_FORMAT_YV12,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_Y8 == AHARDWAREBUFFER_FORMAT_Y8,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_Y16 == AHARDWAREBUFFER_FORMAT_Y16,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RAW16 == AHARDWAREBUFFER_FORMAT_RAW16,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RAW10 == AHARDWAREBUFFER_FORMAT_RAW10,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RAW12 == AHARDWAREBUFFER_FORMAT_RAW12,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RAW_OPAQUE == AHARDWAREBUFFER_FORMAT_RAW_OPAQUE,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED ==
+ AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCBCR_420_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCBCR_422_SP == AHARDWAREBUFFER_FORMAT_YCbCr_422_SP,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCRCB_420_SP == AHARDWAREBUFFER_FORMAT_YCrCb_420_SP,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCBCR_P010 == AHARDWAREBUFFER_FORMAT_YCbCr_P010,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
+ AHARDWAREBUFFER_FORMAT_R8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_16_UINT) ==
+ AHARDWAREBUFFER_FORMAT_R16_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(
+ static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RG_1616_UINT) ==
+ AHARDWAREBUFFER_FORMAT_R16G16_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(
+ static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) ==
+ AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+
+// ----------------------------------------------------------------------------
// Public functions
// ----------------------------------------------------------------------------
@@ -227,11 +301,14 @@
}
return result;
} else {
- const uint32_t pixelStride = AHardwareBuffer_bytesPerPixel(format);
+ int32_t bytesPerPixel;
+ int32_t bytesPerStride;
+ int result = gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence,
+ &bytesPerPixel, &bytesPerStride);
outPlanes->planeCount = 1;
- outPlanes->planes[0].pixelStride = pixelStride;
- outPlanes->planes[0].rowStride = gBuffer->getStride() * pixelStride;
- return gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence);
+ outPlanes->planes[0].pixelStride = bytesPerPixel;
+ outPlanes->planes[0].rowStride = bytesPerStride;
+ return result;
}
}
@@ -487,12 +564,6 @@
return false;
}
- if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
- ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))",
- desc->format, desc->format);
- return false;
- }
-
if (desc->rfu0 != 0 || desc->rfu1 != 0) {
ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0");
return false;
@@ -557,114 +628,6 @@
return true;
}
-bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
- static_assert(HAL_PIXEL_FORMAT_RGBA_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGBX_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGB_565 == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGB_888 == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGBA_FP16 == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGBA_1010102 == AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_BLOB == AHARDWAREBUFFER_FORMAT_BLOB,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_16 == AHARDWAREBUFFER_FORMAT_D16_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_24 == AHARDWAREBUFFER_FORMAT_D24_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_32F == AHARDWAREBUFFER_FORMAT_D32_FLOAT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_STENCIL_8 == AHARDWAREBUFFER_FORMAT_S8_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_BGRA_8888 == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YV12 == AHARDWAREBUFFER_FORMAT_YV12,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_Y8 == AHARDWAREBUFFER_FORMAT_Y8,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_Y16 == AHARDWAREBUFFER_FORMAT_Y16,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RAW16 == AHARDWAREBUFFER_FORMAT_RAW16,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RAW10 == AHARDWAREBUFFER_FORMAT_RAW10,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RAW12 == AHARDWAREBUFFER_FORMAT_RAW12,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RAW_OPAQUE == AHARDWAREBUFFER_FORMAT_RAW_OPAQUE,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED == AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCBCR_420_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCBCR_422_SP == AHARDWAREBUFFER_FORMAT_YCbCr_422_SP,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCRCB_420_SP == AHARDWAREBUFFER_FORMAT_YCrCb_420_SP,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCBCR_P010 == AHARDWAREBUFFER_FORMAT_YCbCr_P010,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
- AHARDWAREBUFFER_FORMAT_R8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_16_UINT) ==
- AHARDWAREBUFFER_FORMAT_R16_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RG_1616_UINT) ==
- AHARDWAREBUFFER_FORMAT_R16G16_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) ==
- AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
-
- switch (format) {
- case AHARDWAREBUFFER_FORMAT_R8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R16_UINT:
- case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
- case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
- case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
- case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
- case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
- case AHARDWAREBUFFER_FORMAT_BLOB:
- case AHARDWAREBUFFER_FORMAT_D16_UNORM:
- case AHARDWAREBUFFER_FORMAT_D24_UNORM:
- case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
- case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
- case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
- case AHARDWAREBUFFER_FORMAT_S8_UINT:
- case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
- // VNDK formats only -- unfortunately we can't differentiate from where we're called
- case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM:
- case AHARDWAREBUFFER_FORMAT_YV12:
- case AHARDWAREBUFFER_FORMAT_Y8:
- case AHARDWAREBUFFER_FORMAT_Y16:
- case AHARDWAREBUFFER_FORMAT_RAW16:
- case AHARDWAREBUFFER_FORMAT_RAW10:
- case AHARDWAREBUFFER_FORMAT_RAW12:
- case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE:
- case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED:
- case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
- case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
- case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
- case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
- return true;
-
- default:
- return false;
- }
-}
-
bool AHardwareBuffer_formatIsYuv(uint32_t format) {
switch (format) {
case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
@@ -681,32 +644,6 @@
}
}
-uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
- switch (format) {
- case AHARDWAREBUFFER_FORMAT_R8_UNORM:
- return 1;
- case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
- case AHARDWAREBUFFER_FORMAT_D16_UNORM:
- case AHARDWAREBUFFER_FORMAT_R16_UINT:
- return 2;
- case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
- case AHARDWAREBUFFER_FORMAT_D24_UNORM:
- return 3;
- case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
- case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
- case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
- case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
- case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
- return 4;
- case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
- case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
- return 8;
- default:
- return 0;
- }
-}
-
uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) {
return hal_format;
}
diff --git a/libs/nativewindow/TEST_MAPPING b/libs/nativewindow/TEST_MAPPING
index 3d7f3c2..9d6425b 100644
--- a/libs/nativewindow/TEST_MAPPING
+++ b/libs/nativewindow/TEST_MAPPING
@@ -1,7 +1,13 @@
{
"presubmit": [
{
+ "name": "libnativewindow_bindgen_test"
+ },
+ {
"name": "libnativewindow_test"
+ },
+ {
+ "name": "libnativewindow_rs-internal_test"
}
]
}
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 6d3d295..880c694 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -37,15 +37,9 @@
// parameters. Note: this does not verify any platform-specific contraints.
bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log);
-// whether this AHardwareBuffer format is valid
-bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
-
// whether this is a YUV type format
bool AHardwareBuffer_formatIsYuv(uint32_t format);
-// number of bytes per pixel or 0 if unknown or multi-planar
-uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format);
-
// convert AHardwareBuffer format to HAL format (note: this is a no-op)
uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index ad4cc4a..9fa5569 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -450,7 +450,7 @@
*
* Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
*/
- ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
/**
* Adobe RGB
@@ -471,21 +471,21 @@
ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
/**
+ * ITU-R Recommendation 601 (BT.601) - 625-line
+ *
+ * Standard-definition television, 625 Lines (PAL)
+ *
+ * Use limited range, SMPTE 170M transfer and BT.601_625 standard.
+ */
+ ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+ /**
* ITU-R Recommendation 601 (BT.601) - 525-line
*
* Standard-definition television, 525 Lines (NTSC)
*
* Use limited range, SMPTE 170M transfer and BT.601_525 standard.
*/
- ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
-
- /**
- * ITU-R Recommendation 709 (BT.709)
- *
- * High-definition television
- *
- * Use limited range, SMPTE 170M transfer and BT.709 standard.
- */
ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
/**
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 0fee3c1..edaa422 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1066,12 +1066,33 @@
(int)compatibility, (int)changeFrameRateStrategy);
}
+struct ANativeWindowFrameTimelineInfo {
+ // Frame Id received from ANativeWindow_getNextFrameId.
+ uint64_t frameNumber;
+
+ // VsyncId received from the Choreographer callback that started this frame.
+ int64_t frameTimelineVsyncId;
+
+ // Input Event ID received from the input event that started this frame.
+ int32_t inputEventId;
+
+ // The time which this frame rendering started (i.e. when Choreographer callback actually run)
+ int64_t startTimeNanos;
+
+ // Whether or not to use the vsyncId to determine the refresh rate. Used for TextureView only.
+ int32_t useForRefreshRateSelection;
+
+ // The VsyncId of a frame that was not drawn and squashed into this frame.
+ // Used for UI thread updates that were not picked up by RenderThread on time.
+ int64_t skippedFrameVsyncId;
+
+ // The start time of a frame that was not drawn and squashed into this frame.
+ int64_t skippedFrameStartTimeNanos;
+};
+
static inline int native_window_set_frame_timeline_info(
- struct ANativeWindow* window, uint64_t frameNumber, int64_t frameTimelineVsyncId,
- int32_t inputEventId, int64_t startTimeNanos, int32_t useForRefreshRateSelection) {
- return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
- frameTimelineVsyncId, inputEventId, startTimeNanos,
- useForRefreshRateSelection);
+ struct ANativeWindow* window, struct ANativeWindowFrameTimelineInfo frameTimelineInfo) {
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineInfo);
}
// ------------------------------------------------------------------------------------------------
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index c2fd6ef..dcb5068 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -65,7 +65,6 @@
LIBNATIVEWINDOW_PLATFORM {
global:
extern "C++" {
- android::AHardwareBuffer_isValidPixelFormat*;
android::AHardwareBuffer_convertFromPixelFormat*;
android::AHardwareBuffer_convertToPixelFormat*;
android::AHardwareBuffer_convertFromGrallocUsageBits*;
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
new file mode 100644
index 0000000..dc1575c
--- /dev/null
+++ b/libs/nativewindow/rust/Android.bp
@@ -0,0 +1,87 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: [
+ "frameworks_native_libs_nativewindow_license",
+ ],
+}
+
+rust_bindgen {
+ name: "libnativewindow_bindgen",
+ crate_name: "nativewindow_bindgen",
+ wrapper_src: "sys/nativewindow_bindings.h",
+ source_stem: "bindings",
+ bindgen_flags: [
+ "--constified-enum-module=AHardwareBuffer_Format",
+ "--bitfield-enum=AHardwareBuffer_UsageFlags",
+
+ "--allowlist-file=.*/nativewindow/include/.*\\.h",
+
+ "--with-derive-eq",
+ "--with-derive-partialeq",
+ ],
+ shared_libs: [
+ "libnativewindow",
+ ],
+
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ min_sdk_version: "VanillaIceCream",
+}
+
+rust_test {
+ name: "libnativewindow_bindgen_test",
+ srcs: [":libnativewindow_bindgen"],
+ crate_name: "nativewindow_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
+
+rust_defaults {
+ name: "libnativewindow_defaults",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "libnativewindow_bindgen",
+ ],
+}
+
+rust_library {
+ name: "libnativewindow_rs",
+ crate_name: "nativewindow",
+ defaults: ["libnativewindow_defaults"],
+
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ min_sdk_version: "VanillaIceCream",
+}
+
+rust_test {
+ name: "libnativewindow_rs-internal_test",
+ crate_name: "nativewindow",
+ defaults: ["libnativewindow_defaults"],
+ test_suites: ["general-tests"],
+}
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
new file mode 100644
index 0000000..a5bcc62
--- /dev/null
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -0,0 +1,260 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Pleasant Rust bindings for libnativewindow, including AHardwareBuffer
+
+extern crate nativewindow_bindgen as ffi;
+
+pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+
+use std::os::raw::c_void;
+use std::ptr;
+
+/// Wrapper around an opaque C AHardwareBuffer.
+pub struct AHardwareBuffer(*mut ffi::AHardwareBuffer);
+
+impl AHardwareBuffer {
+ /// Test whether the given format and usage flag combination is allocatable. If this function
+ /// returns true, it means that a buffer with the given description can be allocated on this
+ /// implementation, unless resource exhaustion occurs. If this function returns false, it means
+ /// that the allocation of the given description will never succeed.
+ ///
+ /// Available since API 29
+ pub fn is_supported(
+ width: u32,
+ height: u32,
+ layers: u32,
+ format: AHardwareBuffer_Format::Type,
+ usage: AHardwareBuffer_UsageFlags,
+ stride: u32,
+ ) -> bool {
+ let buffer_desc = ffi::AHardwareBuffer_Desc {
+ width,
+ height,
+ layers,
+ format,
+ usage: usage.0,
+ stride,
+ rfu0: 0,
+ rfu1: 0,
+ };
+ // SAFETY: *buffer_desc will never be null.
+ let status = unsafe { ffi::AHardwareBuffer_isSupported(&buffer_desc) };
+
+ status == 1
+ }
+
+ /// Allocates a buffer that matches the passed AHardwareBuffer_Desc. If allocation succeeds, the
+ /// buffer can be used according to the usage flags specified in its description. If a buffer is
+ /// used in ways not compatible with its usage flags, the results are undefined and may include
+ /// program termination.
+ ///
+ /// Available since API level 26.
+ pub fn new(
+ width: u32,
+ height: u32,
+ layers: u32,
+ format: AHardwareBuffer_Format::Type,
+ usage: AHardwareBuffer_UsageFlags,
+ ) -> Option<Self> {
+ let buffer_desc = ffi::AHardwareBuffer_Desc {
+ width,
+ height,
+ layers,
+ format,
+ usage: usage.0,
+ stride: 0,
+ rfu0: 0,
+ rfu1: 0,
+ };
+ let mut buffer = ptr::null_mut();
+ // SAFETY: The returned pointer is valid until we drop/deallocate it. The function may fail
+ // and return a status, but we check it later.
+ let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut buffer) };
+
+ if status == 0 {
+ Some(Self(buffer))
+ } else {
+ None
+ }
+ }
+
+ /// Adopts the raw pointer and wraps it in a Rust AHardwareBuffer.
+ ///
+ /// # Errors
+ ///
+ /// Will panic if buffer_ptr is null.
+ ///
+ /// # Safety
+ ///
+ /// This function adopts the pointer but does NOT increment the refcount on the buffer. If the
+ /// caller uses the pointer after the created object is dropped it will cause a memory leak.
+ pub unsafe fn take_from_raw(buffer_ptr: *mut c_void) -> Self {
+ assert!(!buffer_ptr.is_null());
+ Self(buffer_ptr as *mut ffi::AHardwareBuffer)
+ }
+
+ /// Get the system wide unique id for an AHardwareBuffer. This function may panic in extreme
+ /// and undocumented circumstances.
+ ///
+ /// Available since API level 31.
+ pub fn id(&self) -> u64 {
+ let mut out_id = 0;
+ // SAFETY: Neither pointers can be null.
+ let status = unsafe { ffi::AHardwareBuffer_getId(self.0, &mut out_id) };
+ assert_eq!(status, 0, "id() failed for AHardwareBuffer with error code: {status}");
+
+ out_id
+ }
+
+ /// Get the width of this buffer
+ pub fn width(&self) -> u32 {
+ self.description().width
+ }
+
+ /// Get the height of this buffer
+ pub fn height(&self) -> u32 {
+ self.description().height
+ }
+
+ /// Get the number of layers of this buffer
+ pub fn layers(&self) -> u32 {
+ self.description().layers
+ }
+
+ /// Get the format of this buffer
+ pub fn format(&self) -> AHardwareBuffer_Format::Type {
+ self.description().format
+ }
+
+ /// Get the usage bitvector of this buffer
+ pub fn usage(&self) -> AHardwareBuffer_UsageFlags {
+ AHardwareBuffer_UsageFlags(self.description().usage)
+ }
+
+ /// Get the stride of this buffer
+ pub fn stride(&self) -> u32 {
+ self.description().stride
+ }
+
+ fn description(&self) -> ffi::AHardwareBuffer_Desc {
+ let mut buffer_desc = ffi::AHardwareBuffer_Desc {
+ width: 0,
+ height: 0,
+ layers: 0,
+ format: 0,
+ usage: 0,
+ stride: 0,
+ rfu0: 0,
+ rfu1: 0,
+ };
+ // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null.
+ unsafe { ffi::AHardwareBuffer_describe(self.0, &mut buffer_desc) };
+ buffer_desc
+ }
+}
+
+impl Drop for AHardwareBuffer {
+ fn drop(&mut self) {
+ // SAFETY: self.0 will never be null. AHardwareBuffers allocated from within Rust will have
+ // a refcount of one, and there is a safety warning on taking an AHardwareBuffer from a raw
+ // pointer requiring callers to ensure the refcount is managed appropriately.
+ unsafe { ffi::AHardwareBuffer_release(self.0) }
+ }
+}
+
+#[cfg(test)]
+mod ahardwarebuffer_tests {
+ use super::*;
+
+ #[test]
+ fn create_valid_buffer_returns_ok() {
+ let buffer = AHardwareBuffer::new(
+ 512,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ );
+ assert!(buffer.is_some());
+ }
+
+ #[test]
+ fn create_invalid_buffer_returns_err() {
+ let buffer = AHardwareBuffer::new(512, 512, 1, 0, AHardwareBuffer_UsageFlags(0));
+ assert!(buffer.is_none());
+ }
+
+ #[test]
+ #[should_panic]
+ fn take_from_raw_panics_on_null() {
+ unsafe { AHardwareBuffer::take_from_raw(ptr::null_mut()) };
+ }
+
+ #[test]
+ fn take_from_raw_allows_getters() {
+ let buffer_desc = ffi::AHardwareBuffer_Desc {
+ width: 1024,
+ height: 512,
+ layers: 1,
+ format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN.0,
+ stride: 0,
+ rfu0: 0,
+ rfu1: 0,
+ };
+ let mut raw_buffer_ptr = ptr::null_mut();
+
+ let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut raw_buffer_ptr) };
+ assert_eq!(status, 0);
+
+ let buffer = unsafe { AHardwareBuffer::take_from_raw(raw_buffer_ptr as *mut c_void) };
+ assert_eq!(buffer.width(), 1024);
+ }
+
+ #[test]
+ fn basic_getters() {
+ let buffer = AHardwareBuffer::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ )
+ .expect("Buffer with some basic parameters was not created successfully");
+
+ assert_eq!(buffer.width(), 1024);
+ assert_eq!(buffer.height(), 512);
+ assert_eq!(buffer.layers(), 1);
+ assert_eq!(buffer.format(), AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM);
+ assert_eq!(
+ buffer.usage(),
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN
+ );
+ }
+
+ #[test]
+ fn id_getter() {
+ let buffer = AHardwareBuffer::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ )
+ .expect("Buffer with some basic parameters was not created successfully");
+
+ assert_ne!(0, buffer.id());
+ }
+}
diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h
new file mode 100644
index 0000000..e652aee
--- /dev/null
+++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/data_space.h>
+#include <android/hardware_buffer.h>
+#include <android/hdr_metadata.h>
+#include <android/native_window.h>
diff --git a/libs/permission/aidl/android/content/AttributionSourceState.aidl b/libs/permission/aidl/android/content/AttributionSourceState.aidl
index ed1b37d..b3fb7a7 100644
--- a/libs/permission/aidl/android/content/AttributionSourceState.aidl
+++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl
@@ -27,6 +27,10 @@
int pid = -1;
/** The UID that is accessing the permission protected data. */
int uid = -1;
+ /** The default device ID from where the permission protected data is read.
+ * @see Context#DEVICE_ID_DEFAULT
+ */
+ int deviceId = 0;
/** The package that is accessing the permission protected data. */
@nullable @utf8InCpp String packageName;
/** The attribution tag of the app accessing the permission protected data. */
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index fc9b4da..dad3c19 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -91,14 +91,38 @@
void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace dataspace,
SkColorType colorType) {
- GrGLTextureInfo textureInfo;
- bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
- LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
- "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i texType: %i"
- "\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u colorType %i",
- msg, tex.isValid(), dataspace, tex.width(), tex.height(), tex.hasMipmaps(),
- tex.isProtected(), static_cast<int>(tex.textureType()), retrievedTextureInfo,
- textureInfo.fTarget, textureInfo.fFormat, colorType);
+ switch (tex.backend()) {
+ case GrBackendApi::kOpenGL: {
+ GrGLTextureInfo textureInfo;
+ bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
+ LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+ "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
+ "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
+ " colorType %i",
+ msg, tex.isValid(), dataspace, tex.width(), tex.height(),
+ tex.hasMipmaps(), tex.isProtected(),
+ static_cast<int>(tex.textureType()), retrievedTextureInfo,
+ textureInfo.fTarget, textureInfo.fFormat, colorType);
+ break;
+ }
+ case GrBackendApi::kVulkan: {
+ GrVkImageInfo imageInfo;
+ bool retrievedImageInfo = tex.getVkImageInfo(&imageInfo);
+ LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+ "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
+ "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i "
+ "fSampleCount: %u fLevelCount: %u colorType %i",
+ msg, tex.isValid(), dataspace, tex.width(), tex.height(),
+ tex.hasMipmaps(), tex.isProtected(),
+ static_cast<int>(tex.textureType()), retrievedImageInfo,
+ imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount,
+ colorType);
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("%s Unexpected backend %u", msg, static_cast<unsigned>(tex.backend()));
+ break;
+ }
}
sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 5854135..29d8ba7 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -20,6 +20,7 @@
#include "SkiaRenderEngine.h"
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <GrBackendSemaphore.h>
#include <GrContextOptions.h>
#include <SkBlendMode.h>
@@ -86,6 +87,7 @@
// Debugging settings
static const bool kPrintLayerSettings = false;
static const bool kFlushAfterEveryLayer = kPrintLayerSettings;
+static constexpr bool kEnableLayerBrightening = true;
} // namespace
@@ -664,6 +666,8 @@
validateOutputBufferUsage(buffer->getBuffer());
auto grContext = getActiveGrContext();
+ LOG_ALWAYS_FATAL_IF(grContext->abandoned(), "GrContext is abandoned/device lost at start of %s",
+ __func__);
// any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
DeferTextureCleanup dtc(mTextureCleanupMgr);
@@ -699,7 +703,8 @@
// ...and compute the dimming ratio if dimming is requested
const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
- maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
+ maxLayerWhitePoint > 0.f &&
+ (kEnableLayerBrightening || display.targetLuminanceNits > maxLayerWhitePoint)
? maxLayerWhitePoint / display.targetLuminanceNits
: 1.f;
@@ -709,7 +714,9 @@
SkCanvas* canvas = dstCanvas;
SkiaCapture::OffscreenState offscreenCaptureState;
const LayerSettings* blurCompositionLayer = nullptr;
- if (mBlurFilter) {
+
+ // TODO (b/270314344): Enable blurs in protected context.
+ if (mBlurFilter && !mInProtectedContext) {
bool requiresCompositionLayer = false;
for (const auto& layer : layers) {
// if the layer doesn't have blur or it is not visible then continue
@@ -803,7 +810,8 @@
const auto [bounds, roundRectClip] =
getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
layer.geometry.roundedCornersRadius);
- if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
+ // TODO (b/270314344): Enable blurs in protected context.
+ if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha) && !mInProtectedContext) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
// if multiple layers have blur, then we need to take a snapshot now because
@@ -811,8 +819,20 @@
if (!blurInput) {
blurInput = activeSurface->makeImageSnapshot();
}
+
// rect to be blurred in the coordinate space of blurInput
- const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
+ SkRect blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
+
+ // Some layers may be much bigger than the screen. If we used
+ // `blurRect` directly, this would allocate a large buffer with no
+ // benefit. Apply the clip, which already takes the display size
+ // into account. The clipped size will then be used to calculate the
+ // size of the buffer we will create for blurring.
+ if (!blurRect.intersect(SkRect::Make(canvas->getDeviceClipBounds()))) {
+ // This should not happen, but if it did, we would use the full
+ // sized layer, which should still be fine.
+ ALOGW("blur bounds does not intersect display clip!");
+ }
// if the clip needs to be applied then apply it now and make sure
// it is restored before we attempt to draw any shadows.
@@ -1105,7 +1125,7 @@
}
if (kFlushAfterEveryLayer) {
ATRACE_NAME("flush surface");
- activeSurface->flush();
+ skgpu::ganesh::Flush(activeSurface);
}
}
for (const auto& borderRenderInfo : display.borderInfoList) {
@@ -1133,7 +1153,7 @@
{
ATRACE_NAME("flush surface");
LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
- activeSurface->flush();
+ skgpu::ganesh::Flush(activeSurface);
}
auto drawFence = sp<Fence>::make(flushAndSubmit(grContext));
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index b99e385..c16586b 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -263,7 +263,7 @@
VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties);
VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2);
VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties);
- VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties2);
VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2);
VK_GET_INST_PROC(instance, CreateDevice);
@@ -342,17 +342,37 @@
}
uint32_t queueCount;
- vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, nullptr);
+ vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, nullptr);
if (queueCount == 0) {
BAIL("Could not find queues for physical device");
}
- std::vector<VkQueueFamilyProperties> queueProps(queueCount);
- vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
+ std::vector<VkQueueFamilyProperties2> queueProps(queueCount);
+ std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount);
+ VkQueueGlobalPriorityKHR queuePriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR;
+ // Even though we don't yet know if the VK_EXT_global_priority extension is available,
+ // we can safely add the request to the pNext chain, and if the extension is not
+ // available, it will be ignored.
+ for (uint32_t i = 0; i < queueCount; ++i) {
+ queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT;
+ queuePriorityProps[i].pNext = nullptr;
+ queueProps[i].pNext = &queuePriorityProps[i];
+ }
+ vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, queueProps.data());
int graphicsQueueIndex = -1;
for (uint32_t i = 0; i < queueCount; ++i) {
- if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ // Look at potential answers to the VK_EXT_global_priority query. If answers were
+ // provided, we may adjust the queuePriority.
+ if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ for (uint32_t j = 0; j < queuePriorityProps[i].priorityCount; j++) {
+ if (queuePriorityProps[i].priorities[j] > queuePriority) {
+ queuePriority = queuePriorityProps[i].priorities[j];
+ }
+ }
+ if (queuePriority == VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR) {
+ interface.isRealtimePriority = true;
+ }
graphicsQueueIndex = i;
break;
}
@@ -419,12 +439,11 @@
VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT,
nullptr,
// If queue priority is supported, RE should always have realtime priority.
- VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT,
+ queuePriority,
};
if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
queueNextPtr = &queuePriorityCreateInfo;
- interface.isRealtimePriority = true;
}
VkDeviceQueueCreateFlags deviceQueueCreateFlags =
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 2557ac9..1e0c4cf 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -16,6 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "BlurFilter.h"
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkPaint.h>
#include <SkRRect.h>
@@ -23,6 +24,7 @@
#include <SkSize.h>
#include <SkString.h>
#include <SkSurface.h>
+#include <SkTileMode.h>
#include <log/log.h>
#include <utils/Trace.h>
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
index a77d5bf..e72c501 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -17,6 +17,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "GaussianBlurFilter.h"
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkPaint.h>
#include <SkRRect.h>
@@ -25,6 +26,7 @@
#include <SkSize.h>
#include <SkString.h>
#include <SkSurface.h>
+#include <SkTileMode.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include "include/gpu/GpuTypes.h" // from Skia
#include <log/log.h>
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
index e370c39..0c7335c 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -17,13 +17,20 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "KawaseBlurFilter.h"
+#include <SkAlphaType.h>
+#include <SkBlendMode.h>
#include <SkCanvas.h>
+#include <SkImageInfo.h>
#include <SkPaint.h>
#include <SkRRect.h>
#include <SkRuntimeEffect.h>
+#include <SkShader.h>
#include <SkSize.h>
#include <SkString.h>
#include <SkSurface.h>
+#include <SkTileMode.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <log/log.h>
#include <utils/Trace.h>
@@ -32,19 +39,18 @@
namespace skia {
KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
- SkString blurString(R"(
- uniform shader child;
- uniform float in_blurOffset;
+ SkString blurString(
+ "uniform shader child;"
+ "uniform float in_blurOffset;"
- half4 main(float2 xy) {
- half4 c = child.eval(xy);
- c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));
- c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));
- c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));
- c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));
- return half4(c.rgb * 0.2, 1.0);
- }
- )");
+ "half4 main(float2 xy) {"
+ "half4 c = child.eval(xy);"
+ "c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));"
+ "c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));"
+ "c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));"
+ "c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));"
+ "return half4(c.rgb * 0.2, 1.0);"
+ "}");
auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
if (!blurEffect) {
@@ -53,9 +59,44 @@
mBlurEffect = std::move(blurEffect);
}
-sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
- const sk_sp<SkImage> input, const SkRect& blurRect)
- const {
+// Draws the given runtime shader on a GPU (Ganesh) surface and returns the result as an
+// SkImage.
+static sk_sp<SkImage> makeImage(GrRecordingContext* context, SkRuntimeShaderBuilder* builder,
+ const SkImageInfo& resultInfo) {
+ if (resultInfo.alphaType() == kUnpremul_SkAlphaType ||
+ resultInfo.alphaType() == kUnknown_SkAlphaType) {
+ return nullptr;
+ }
+ constexpr int kSampleCount = 1;
+ constexpr bool kMipmapped = false;
+
+ sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context,
+ skgpu::Budgeted::kYes,
+ resultInfo,
+ kSampleCount,
+ kTopLeft_GrSurfaceOrigin,
+ nullptr,
+ kMipmapped);
+ if (!surface) {
+ return nullptr;
+ }
+ sk_sp<SkShader> shader = builder->makeShader(nullptr);
+ if (!shader) {
+ return nullptr;
+ }
+ SkPaint paint;
+ paint.setShader(std::move(shader));
+ paint.setBlendMode(SkBlendMode::kSrc);
+ surface->getCanvas()->drawPaint(paint);
+ return surface->makeImageSnapshot();
+}
+
+sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context,
+ const uint32_t blurRadius,
+ const sk_sp<SkImage> input,
+ const SkRect& blurRect) const {
+ LOG_ALWAYS_FATAL_IF(context == nullptr, "%s: Needs GPU context", __func__);
+ LOG_ALWAYS_FATAL_IF(input == nullptr, "%s: Invalid input image", __func__);
// Kawase is an approximation of Gaussian, but it behaves differently from it.
// A radius transformation is required for approximating them, and also to introduce
// non-integer steps, necessary to smoothly interpolate large radii.
@@ -80,14 +121,15 @@
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
- sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+ sk_sp<SkImage> tmpBlur = makeImage(context, &blurBuilder, scaledInfo);
// And now we'll build our chain of scaled blur stages
for (auto i = 1; i < numberOfPasses; i++) {
+ LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null for pass %d", __func__, i);
blurBuilder.child("child") =
tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
- tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+ tmpBlur = makeImage(context, &blurBuilder, scaledInfo);
}
return tmpBlur;
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 019d6cb..634d35a 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -64,6 +64,14 @@
Sensor s;
Vector<Sensor> v;
uint32_t n = reply.readUint32();
+ // The size of the n Sensor elements on the wire is what we really want, but
+ // this is better than nothing.
+ if (n > reply.dataAvail()) {
+ ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+ "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+ n, reply.dataAvail());
+ return v;
+ }
v.setCapacity(n);
while (n) {
n--;
@@ -86,6 +94,14 @@
Sensor s;
Vector<Sensor> v;
uint32_t n = reply.readUint32();
+ // The size of the n Sensor elements on the wire is what we really want, but
+ // this is better than nothing.
+ if (n > reply.dataAvail()) {
+ ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+ "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+ n, reply.dataAvail());
+ return v;
+ }
v.setCapacity(n);
while (n) {
n--;
@@ -109,6 +125,14 @@
Sensor s;
Vector<Sensor> v;
uint32_t n = reply.readUint32();
+ // The size of the n Sensor elements on the wire is what we really want, but
+ // this is better than nothing.
+ if (n > reply.dataAvail()) {
+ ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+ "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+ n, reply.dataAvail());
+ return v;
+ }
v.setCapacity(n);
while (n) {
n--;
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index 2be98e7..57c74ee 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -32,27 +32,12 @@
sp<hardware::ISensorPrivacyManager> SensorPrivacyManager::getService()
{
std::lock_guard<Mutex> scoped_lock(mLock);
- int64_t startTime = 0;
sp<hardware::ISensorPrivacyManager> service = mService;
- while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
- sp<IBinder> binder = defaultServiceManager()->checkService(String16("sensor_privacy"));
- if (binder == nullptr) {
- // Wait for the sensor privacy service to come back...
- if (startTime == 0) {
- startTime = uptimeMillis();
- ALOGI("Waiting for sensor privacy service");
- } else if ((uptimeMillis() - startTime) > 1000000) {
- ALOGW("Waiting too long for sensor privacy service, giving up");
- service = nullptr;
- break;
- }
- usleep(25000);
- } else {
- service = interface_cast<hardware::ISensorPrivacyManager>(binder);
- mService = service;
- }
+ if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->waitForService(String16("sensor_privacy"));
+ mService = interface_cast<hardware::ISensorPrivacyManager>(binder);
}
- return service;
+ return mService;
}
bool SensorPrivacyManager::supportsSensorToggle(int toggleType, int sensor) {
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 073da89..8675f14 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -304,6 +304,12 @@
return std::string("BGRA_8888");
case android::PIXEL_FORMAT_R_8:
return std::string("R_8");
+ case android::PIXEL_FORMAT_R_16_UINT:
+ return std::string("R_16_UINT");
+ case android::PIXEL_FORMAT_RG_1616_UINT:
+ return std::string("RG_1616_UINT");
+ case android::PIXEL_FORMAT_RGBA_10101010:
+ return std::string("RGBA_10101010");
default:
return StringPrintf("Unknown %#08x", format);
}
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index c3b2d3d..37ebfc4 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -31,10 +31,15 @@
using namespace aidl::android::hardware::graphics::common;
using namespace ::android::hardware::graphics::mapper;
+using ADataspace = aidl::android::hardware::graphics::common::Dataspace;
+using APixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+
namespace android {
static const auto kIAllocatorServiceName = IAllocator::descriptor + std::string("/default");
static const auto kIAllocatorMinimumVersion = 2;
+constexpr const char* kStandardMetadataName =
+ "android.hardware.graphics.common.StandardMetadataType";
// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
static constexpr uint64_t kRemovedUsageBits = static_cast<uint64_t>((1 << 10) | (1 << 13));
@@ -284,17 +289,205 @@
return mMapper != nullptr && mMapper->version >= AIMAPPER_VERSION_5;
}
+static bool isStandardMetadata(AIMapper_MetadataType metadataType) {
+ return strcmp(kStandardMetadataName, metadataType.name) == 0;
+}
+
+struct DumpBufferResult {
+ uint64_t bufferId;
+ std::string name;
+ uint64_t width;
+ uint64_t height;
+ uint64_t layerCount;
+ APixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ uint64_t pixelFormatModifier;
+ BufferUsage usage;
+ ADataspace dataspace;
+ uint64_t allocationSize;
+ uint64_t protectedContent;
+ ExtendableType compression;
+ ExtendableType interlaced;
+ ExtendableType chromaSiting;
+ std::vector<ui::PlaneLayout> planeLayouts;
+};
+
+#define DECODE_TO(name, output) \
+ case StandardMetadataType::name: \
+ output = StandardMetadata<StandardMetadataType::name>::value ::decode(value, valueSize) \
+ .value(); \
+ break
+
+static void dumpBufferCommon(DumpBufferResult* outResult, AIMapper_MetadataType metadataType,
+ const void* value, size_t valueSize) {
+ if (!isStandardMetadata(metadataType)) {
+ return;
+ }
+ StandardMetadataType type = (StandardMetadataType)metadataType.value;
+ switch (type) {
+ DECODE_TO(BUFFER_ID, outResult->bufferId);
+ DECODE_TO(NAME, outResult->name);
+ DECODE_TO(WIDTH, outResult->width);
+ DECODE_TO(HEIGHT, outResult->height);
+ DECODE_TO(LAYER_COUNT, outResult->layerCount);
+ DECODE_TO(PIXEL_FORMAT_REQUESTED, outResult->pixelFormatRequested);
+ DECODE_TO(PIXEL_FORMAT_FOURCC, outResult->pixelFormatFourCC);
+ DECODE_TO(PIXEL_FORMAT_MODIFIER, outResult->pixelFormatModifier);
+ DECODE_TO(USAGE, outResult->usage);
+ DECODE_TO(DATASPACE, outResult->dataspace);
+ DECODE_TO(ALLOCATION_SIZE, outResult->allocationSize);
+ DECODE_TO(PROTECTED_CONTENT, outResult->protectedContent);
+ DECODE_TO(COMPRESSION, outResult->compression);
+ DECODE_TO(INTERLACED, outResult->interlaced);
+ DECODE_TO(CHROMA_SITING, outResult->chromaSiting);
+ DECODE_TO(PLANE_LAYOUTS, outResult->planeLayouts);
+ default:
+ break;
+ }
+}
+
+#undef DECODE_TO
+
+template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>{}>>
+constexpr std::underlying_type_t<EnumT> to_underlying(EnumT e) noexcept {
+ return static_cast<std::underlying_type_t<EnumT>>(e);
+}
+
+static void writeDumpToStream(const DumpBufferResult& bufferDump, std::ostream& outDump,
+ bool less) {
+ double allocationSizeKiB = static_cast<double>(bufferDump.allocationSize) / 1024;
+
+ outDump << "+ name:" << bufferDump.name << ", id:" << bufferDump.bufferId
+ << ", size:" << std::fixed << allocationSizeKiB << "KiB, w/h:" << bufferDump.width
+ << "x" << bufferDump.height << ", usage: 0x" << std::hex
+ << to_underlying(bufferDump.usage) << std::dec
+ << ", req fmt:" << to_underlying(bufferDump.pixelFormatRequested)
+ << ", fourcc/mod:" << bufferDump.pixelFormatFourCC << "/"
+ << bufferDump.pixelFormatModifier << ", dataspace: 0x" << std::hex
+ << to_underlying(bufferDump.dataspace) << std::dec << ", compressed: ";
+
+ if (less) {
+ bool isCompressed = !gralloc4::isStandardCompression(bufferDump.compression) ||
+ (gralloc4::getStandardCompressionValue(bufferDump.compression) !=
+ ui::Compression::NONE);
+ outDump << std::boolalpha << isCompressed << "\n";
+ } else {
+ outDump << gralloc4::getCompressionName(bufferDump.compression) << "\n";
+ }
+
+ if (!less) {
+ bool firstPlane = true;
+ for (const auto& planeLayout : bufferDump.planeLayouts) {
+ if (firstPlane) {
+ firstPlane = false;
+ outDump << "\tplanes: ";
+ } else {
+ outDump << "\t ";
+ }
+
+ for (size_t i = 0; i < planeLayout.components.size(); i++) {
+ const auto& planeLayoutComponent = planeLayout.components[i];
+ outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
+ if (i < planeLayout.components.size() - 1) {
+ outDump << "/";
+ } else {
+ outDump << ":\t";
+ }
+ }
+ outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
+ << ", stride:" << planeLayout.strideInBytes
+ << " bytes, size:" << planeLayout.totalSizeInBytes;
+ outDump << ", inc:" << planeLayout.sampleIncrementInBits
+ << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
+ << planeLayout.verticalSubsampling;
+ outDump << "\n";
+ }
+
+ outDump << "\tlayer cnt: " << bufferDump.layerCount
+ << ", protected content: " << bufferDump.protectedContent
+ << ", interlaced: " << gralloc4::getInterlacedName(bufferDump.interlaced)
+ << ", chroma siting:" << gralloc4::getChromaSitingName(bufferDump.chromaSiting)
+ << "\n";
+ }
+}
+
std::string Gralloc5Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
- // TODO(b/261858392): Implement
- (void)bufferHandle;
- (void)less;
- return {};
+ DumpBufferResult bufferInfo;
+ AIMapper_DumpBufferCallback dumpBuffer = [](void* contextPtr,
+ AIMapper_MetadataType metadataType,
+ const void* _Nonnull value, size_t valueSize) {
+ DumpBufferResult* context = reinterpret_cast<DumpBufferResult*>(contextPtr);
+ dumpBufferCommon(context, metadataType, value, valueSize);
+ };
+ AIMapper_Error error = mMapper->v5.dumpBuffer(bufferHandle, dumpBuffer, &bufferInfo);
+ if (error != AIMAPPER_ERROR_NONE) {
+ ALOGE("Error dumping buffer: %d", error);
+ return std::string{};
+ }
+ std::ostringstream stream;
+ stream.precision(2);
+ writeDumpToStream(bufferInfo, stream, less);
+ return stream.str();
}
std::string Gralloc5Mapper::dumpBuffers(bool less) const {
- // TODO(b/261858392): Implement
- (void)less;
- return {};
+ class DumpAllBuffersContext {
+ private:
+ bool mHasPending = false;
+ DumpBufferResult mPending;
+ std::vector<DumpBufferResult> mResults;
+
+ public:
+ DumpAllBuffersContext() { mResults.reserve(10); }
+
+ void commit() {
+ if (mHasPending) {
+ mResults.push_back(mPending);
+ mHasPending = false;
+ }
+ }
+
+ DumpBufferResult* write() {
+ mHasPending = true;
+ return &mPending;
+ }
+
+ const std::vector<DumpBufferResult>& results() {
+ commit();
+ return mResults;
+ }
+ } context;
+
+ AIMapper_BeginDumpBufferCallback beginCallback = [](void* contextPtr) {
+ DumpAllBuffersContext* context = reinterpret_cast<DumpAllBuffersContext*>(contextPtr);
+ context->commit();
+ };
+
+ AIMapper_DumpBufferCallback dumpBuffer = [](void* contextPtr,
+ AIMapper_MetadataType metadataType,
+ const void* _Nonnull value, size_t valueSize) {
+ DumpAllBuffersContext* context = reinterpret_cast<DumpAllBuffersContext*>(contextPtr);
+ dumpBufferCommon(context->write(), metadataType, value, valueSize);
+ };
+
+ AIMapper_Error error = mMapper->v5.dumpAllBuffers(beginCallback, dumpBuffer, &context);
+ if (error != AIMAPPER_ERROR_NONE) {
+ ALOGE("Error dumping buffers: %d", error);
+ return std::string{};
+ }
+ uint64_t totalAllocationSize = 0;
+ std::ostringstream stream;
+ stream.precision(2);
+ stream << "Imported gralloc buffers:\n";
+
+ for (const auto& bufferDump : context.results()) {
+ writeDumpToStream(bufferDump, stream, less);
+ totalAllocationSize += bufferDump.allocationSize;
+ }
+
+ double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
+ stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
+ return stream.str();
}
status_t Gralloc5Mapper::importBuffer(const native_handle_t *rawHandle,
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index c0abec2..eb0bd4e 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -89,14 +89,14 @@
uint64_t total = 0;
result.append("GraphicBufferAllocator buffers:\n");
const size_t count = list.size();
- StringAppendF(&result, "%10s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
+ StringAppendF(&result, "%14s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
"W (Stride) x H", "Layers", "Format", "Usage", "Requestor");
for (size_t i = 0; i < count; i++) {
const alloc_rec_t& rec(list.valueAt(i));
std::string sizeStr = (rec.size)
? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0)
: "unknown";
- StringAppendF(&result, "%10p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
+ StringAppendF(&result, "%14p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height,
rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
total += rec.size;
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
index cb61e6a..494272b 100644
--- a/libs/ui/include/ui/FatVector.h
+++ b/libs/ui/include/ui/FatVector.h
@@ -65,6 +65,17 @@
free(p);
}
}
+
+ // The STL checks that this member type is present so that
+ // std::allocator_traits<InlineStdAllocator<T, SIZE>>::rebind_alloc<Other>
+ // works. std::vector won't be able to construct an
+ // InlineStdAllocator<Other, SIZE>, because InlineStdAllocator has no
+ // default constructor, but vector presumably doesn't rebind the allocator
+ // because it doesn't allocate internal node types.
+ template <class Other>
+ struct rebind {
+ typedef InlineStdAllocator<Other, SIZE> other;
+ };
Allocation& mAllocation;
};
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index ac75f43..334106f 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -142,6 +142,8 @@
std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
};
+using FenceTimePtr = std::shared_ptr<FenceTime>;
+
// A queue of FenceTimes that are expected to signal in FIFO order.
// Only maintains a queue of weak pointers so it doesn't keep references
// to Fences on its own.
@@ -190,8 +192,15 @@
// before the new one is added.
class FenceToFenceTimeMap {
public:
- // Create a new FenceTime with that wraps the provided Fence.
- std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence);
+ using FencePair = std::pair<sp<Fence>, FenceTimePtr>;
+
+ FencePair makePendingFenceForTest() {
+ const auto fence = sp<Fence>::make();
+ return {fence, createFenceTimeForTest(fence)};
+ }
+
+ // Create a new FenceTime that wraps the provided Fence.
+ FenceTimePtr createFenceTimeForTest(const sp<Fence>&);
// Signals all FenceTimes created through this class that are wrappers
// around |fence|.
@@ -205,7 +214,6 @@
std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap;
};
-
-}; // namespace android
+} // namespace android
#endif // ANDROID_FENCE_TIME_H
diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp
index e3f709b..9deba01 100644
--- a/libs/ultrahdr/Android.bp
+++ b/libs/ultrahdr/Android.bp
@@ -14,11 +14,10 @@
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"],
+ default_applicable_licenses: [
+ "frameworks_native_license",
+ "adobe_hdr_gain_map_license",
+ ],
}
cc_library {
diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
new file mode 100644
index 0000000..e999a8b
--- /dev/null
+++ b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
@@ -0,0 +1,19 @@
+// Copyright 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+license {
+ name: "adobe_hdr_gain_map_license",
+ license_kinds: ["legacy_by_exception_only"],
+ license_text: ["NOTICE"],
+}
diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE b/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE
new file mode 100644
index 0000000..3f6c594
--- /dev/null
+++ b/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE
@@ -0,0 +1 @@
+This product includes Gain Map technology under license by Adobe.
diff --git a/libs/ultrahdr/fuzzer/Android.bp b/libs/ultrahdr/fuzzer/Android.bp
index 27b38c3..6c0a2f5 100644
--- a/libs/ultrahdr/fuzzer/Android.bp
+++ b/libs/ultrahdr/fuzzer/Android.bp
@@ -24,7 +24,17 @@
cc_defaults {
name: "ultrahdr_fuzzer_defaults",
host_supported: true,
- static_libs: ["liblog"],
+ shared_libs: [
+ "libimage_io",
+ "libjpeg",
+ ],
+ static_libs: [
+ "libjpegdecoder",
+ "libjpegencoder",
+ "libultrahdr",
+ "libutils",
+ "liblog",
+ ],
target: {
darwin: {
enabled: false,
@@ -37,6 +47,8 @@
description: "The fuzzers target the APIs of jpeg hdr",
service_privilege: "constrained",
users: "multi_user",
+ fuzzed_code_usage: "future_version",
+ vector: "local_no_privileges_required",
},
}
@@ -46,20 +58,12 @@
srcs: [
"ultrahdr_enc_fuzzer.cpp",
],
- shared_libs: [
- "libimage_io",
- "libjpeg",
- "liblog",
- ],
- static_libs: [
- "libjpegdecoder",
- "libjpegencoder",
- "libultrahdr",
- "libutils",
- ],
- fuzz_config: {
- fuzzed_code_usage: "future_version",
- vector: "local_no_privileges_required",
- },
}
+cc_fuzz {
+ name: "ultrahdr_dec_fuzzer",
+ defaults: ["ultrahdr_fuzzer_defaults"],
+ srcs: [
+ "ultrahdr_dec_fuzzer.cpp",
+ ],
+}
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
new file mode 100644
index 0000000..ad1d57a
--- /dev/null
+++ b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// System include files
+#include <fuzzer/FuzzedDataProvider.h>
+#include <iostream>
+#include <vector>
+
+// User include files
+#include "ultrahdr/jpegr.h"
+
+using namespace android::ultrahdr;
+
+// Transfer functions for image data, sync with ultrahdr.h
+const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED + 1;
+const int kOfMax = ULTRAHDR_OUTPUT_MAX;
+
+class UltraHdrDecFuzzer {
+public:
+ UltraHdrDecFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+
+private:
+ FuzzedDataProvider mFdp;
+};
+
+void UltraHdrDecFuzzer::process() {
+ // hdr_of
+ auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
+ auto buffer = mFdp.ConsumeRemainingBytes<uint8_t>();
+ jpegr_compressed_struct jpegImgR{buffer.data(), (int)buffer.size(), (int)buffer.size(),
+ ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+
+ std::vector<uint8_t> iccData(0);
+ std::vector<uint8_t> exifData(0);
+ jpegr_info_struct info{0, 0, &iccData, &exifData};
+ JpegR jpegHdr;
+ (void)jpegHdr.getJPEGRInfo(&jpegImgR, &info);
+//#define DUMP_PARAM
+#ifdef DUMP_PARAM
+ std::cout << "input buffer size " << jpegImgR.length << std::endl;
+ std::cout << "image dimensions " << info.width << " x " << info.width << std::endl;
+#endif
+ size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_SDR) ? 4 : 8);
+ jpegr_uncompressed_struct decodedJpegR;
+ auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
+ decodedJpegR.data = decodedRaw.get();
+ ultrahdr_metadata_struct metadata;
+ jpegr_uncompressed_struct decodedGainMap{};
+ (void)jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
+ mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr, of,
+ &decodedGainMap, &metadata);
+ if (decodedGainMap.data) free(decodedGainMap.data);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ UltraHdrDecFuzzer fuzzHandle(data, size);
+ fuzzHandle.process();
+ return 0;
+}
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
index 472699b..bbe58e0 100644
--- a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
+++ b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
@@ -45,7 +45,7 @@
// Transfer functions for image data, sync with ultrahdr.h
const int kTfMin = ULTRAHDR_TF_UNSPECIFIED + 1;
-const int kTfMax = ULTRAHDR_TF_MAX;
+const int kTfMax = ULTRAHDR_TF_PQ;
// Transfer functions for image data, sync with ultrahdr.h
const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED + 1;
@@ -55,12 +55,9 @@
const int kQfMin = 0;
const int kQfMax = 100;
-// seed
-const unsigned kSeed = 0x7ab7;
-
-class JpegHDRFuzzer {
+class UltraHdrEncFuzzer {
public:
- JpegHDRFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
void process();
void fillP010Buffer(uint16_t* data, int width, int height, int stride);
void fill420Buffer(uint8_t* data, int size);
@@ -69,7 +66,7 @@
FuzzedDataProvider mFdp;
};
-void JpegHDRFuzzer::fillP010Buffer(uint16_t* data, int width, int height, int stride) {
+void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, int stride) {
uint16_t* tmp = data;
std::vector<uint16_t> buffer(16);
for (int i = 0; i < buffer.size(); i++) {
@@ -78,22 +75,24 @@
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i += buffer.size()) {
memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (width - i)));
- std::shuffle(buffer.begin(), buffer.end(), std::default_random_engine(kSeed));
+ std::shuffle(buffer.begin(), buffer.end(),
+ std::default_random_engine(std::random_device{}()));
}
tmp += stride;
}
}
-void JpegHDRFuzzer::fill420Buffer(uint8_t* data, int size) {
+void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int size) {
std::vector<uint8_t> buffer(16);
mFdp.ConsumeData(buffer.data(), buffer.size());
for (int i = 0; i < size; i += buffer.size()) {
memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (size - i)));
- std::shuffle(buffer.begin(), buffer.end(), std::default_random_engine(kSeed));
+ std::shuffle(buffer.begin(), buffer.end(),
+ std::default_random_engine(std::random_device{}()));
}
}
-void JpegHDRFuzzer::process() {
+void UltraHdrEncFuzzer::process() {
while (mFdp.remaining_bytes()) {
struct jpegr_uncompressed_struct p010Img {};
struct jpegr_uncompressed_struct yuv420Img {};
@@ -250,37 +249,55 @@
jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
ultrahdr_metadata_struct metadata;
- metadata.version = "1.3.1";
+ metadata.version = "1.0";
if (tf == ULTRAHDR_TF_HLG) {
metadata.maxContentBoost = kHlgMaxNits / kSdrWhiteNits;
} else if (tf == ULTRAHDR_TF_PQ) {
metadata.maxContentBoost = kPqMaxNits / kSdrWhiteNits;
} else {
- metadata.maxContentBoost = 0;
+ metadata.maxContentBoost = 1.0f;
}
metadata.minContentBoost = 1.0f;
+ metadata.gamma = 1.0f;
+ metadata.offsetSdr = 0.0f;
+ metadata.offsetHdr = 0.0f;
+ metadata.hdrCapacityMin = 1.0f;
+ metadata.hdrCapacityMax = metadata.maxContentBoost;
status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
}
}
}
}
if (status == android::OK) {
- jpegr_uncompressed_struct decodedJpegR;
- auto decodedRaw = std::make_unique<uint8_t[]>(width * height * 8);
- decodedJpegR.data = decodedRaw.get();
- jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
- mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr, of,
- nullptr, nullptr);
std::vector<uint8_t> iccData(0);
std::vector<uint8_t> exifData(0);
jpegr_info_struct info{0, 0, &iccData, &exifData};
- jpegHdr.getJPEGRInfo(&jpegImgR, &info);
+ status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
+ if (status == android::OK) {
+ size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_SDR) ? 4 : 8);
+ jpegr_uncompressed_struct decodedJpegR;
+ auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
+ decodedJpegR.data = decodedRaw.get();
+ ultrahdr_metadata_struct metadata;
+ jpegr_uncompressed_struct decodedGainMap{};
+ status = jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
+ mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX),
+ nullptr, of, &decodedGainMap, &metadata);
+ if (status != android::OK) {
+ ALOGE("encountered error during decoding %d", status);
+ }
+ if (decodedGainMap.data) free(decodedGainMap.data);
+ } else {
+ ALOGE("encountered error during get jpeg info %d", status);
+ }
+ } else {
+ ALOGE("encountered error during encoding %d", status);
}
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- JpegHDRFuzzer fuzzHandle(data, size);
+ UltraHdrEncFuzzer fuzzHandle(data, size);
fuzzHandle.process();
return 0;
}
diff --git a/libs/ultrahdr/gainmapmath.cpp b/libs/ultrahdr/gainmapmath.cpp
index 37c3cf3..8015a4e 100644
--- a/libs/ultrahdr/gainmapmath.cpp
+++ b/libs/ultrahdr/gainmapmath.cpp
@@ -119,34 +119,39 @@
return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value;
}
-// See IEC 61966-2-1, Equation F.7.
+// See IEC 61966-2-1/Amd 1:2003, Equation F.7.
static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f;
float srgbLuminance(Color e) {
return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b;
}
-// See ECMA TR/98, Section 7.
-static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f;
-
-Color srgbYuvToRgb(Color e_gamma) {
- return {{{ clampPixelFloat(e_gamma.y + kSrgbRCr * e_gamma.v),
- clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v),
- clampPixelFloat(e_gamma.y + kSrgbBCb * e_gamma.u) }}};
-}
-
-// See ECMA TR/98, Section 7.
-static const float kSrgbYR = 0.299f, kSrgbYG = 0.587f, kSrgbYB = 0.114f;
-static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f;
-static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f;
+// See ITU-R BT.709-6, Section 3.
+// Uses the same coefficients for deriving luma signal as
+// IEC 61966-2-1/Amd 1:2003 states for luminance, so we reuse the luminance
+// function above.
+static const float kSrgbCb = 1.8556f, kSrgbCr = 1.5748f;
Color srgbRgbToYuv(Color e_gamma) {
- return {{{ kSrgbYR * e_gamma.r + kSrgbYG * e_gamma.g + kSrgbYB * e_gamma.b,
- kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b,
- kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}};
+ float y_gamma = srgbLuminance(e_gamma);
+ return {{{ y_gamma,
+ (e_gamma.b - y_gamma) / kSrgbCb,
+ (e_gamma.r - y_gamma) / kSrgbCr }}};
}
-// See IEC 61966-2-1, Equations F.5 and F.6.
+// See ITU-R BT.709-6, Section 3.
+// Same derivation to BT.2100's YUV->RGB, below. Similar to srgbRgbToYuv, we
+// can reuse the luminance coefficients since they are the same.
+static const float kSrgbGCb = kSrgbB * kSrgbCb / kSrgbG;
+static const float kSrgbGCr = kSrgbR * kSrgbCr / kSrgbG;
+
+Color srgbYuvToRgb(Color e_gamma) {
+ return {{{ clampPixelFloat(e_gamma.y + kSrgbCr * e_gamma.v),
+ clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v),
+ clampPixelFloat(e_gamma.y + kSrgbCb * e_gamma.u) }}};
+}
+
+// See IEC 61966-2-1/Amd 1:2003, Equations F.5 and F.6.
float srgbInvOetf(float e_gamma) {
if (e_gamma <= 0.04045f) {
return e_gamma / 12.92f;
@@ -178,13 +183,38 @@
////////////////////////////////////////////////////////////////////////////////
// Display-P3 transformations
-// See SMPTE EG 432-1, Table 7-2.
+// See SMPTE EG 432-1, Equation 7-8.
static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f;
float p3Luminance(Color e) {
return kP3R * e.r + kP3G * e.g + kP3B * e.b;
}
+// See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2.
+// Unfortunately, calculation of luma signal differs from calculation of
+// luminance for Display-P3, so we can't reuse p3Luminance here.
+static const float kP3YR = 0.299f, kP3YG = 0.587f, kP3YB = 0.114f;
+static const float kP3Cb = 1.772f, kP3Cr = 1.402f;
+
+Color p3RgbToYuv(Color e_gamma) {
+ float y_gamma = kP3YR * e_gamma.r + kP3YG * e_gamma.g + kP3YB * e_gamma.b;
+ return {{{ y_gamma,
+ (e_gamma.b - y_gamma) / kP3Cb,
+ (e_gamma.r - y_gamma) / kP3Cr }}};
+}
+
+// See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2.
+// Same derivation to BT.2100's YUV->RGB, below. Similar to p3RgbToYuv, we must
+// use luma signal coefficients rather than the luminance coefficients.
+static const float kP3GCb = kP3YB * kP3Cb / kP3YG;
+static const float kP3GCr = kP3YR * kP3Cr / kP3YG;
+
+Color p3YuvToRgb(Color e_gamma) {
+ return {{{ clampPixelFloat(e_gamma.y + kP3Cr * e_gamma.v),
+ clampPixelFloat(e_gamma.y - kP3GCb * e_gamma.u - kP3GCr * e_gamma.v),
+ clampPixelFloat(e_gamma.y + kP3Cb * e_gamma.u) }}};
+}
+
////////////////////////////////////////////////////////////////////////////////
// BT.2100 transformations - according to ITU-R BT.2100-2
@@ -197,6 +227,8 @@
}
// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
+// BT.2100 uses the same coefficients for calculating luma signal and luminance,
+// so we reuse the luminance function here.
static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
Color bt2100RgbToYuv(Color e_gamma) {
@@ -206,6 +238,10 @@
(e_gamma.r - y_gamma) / kBt2100Cr }}};
}
+// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
+//
+// Similar to bt2100RgbToYuv above, we can reuse the luminance coefficients.
+//
// Derived by inversing bt2100RgbToYuv. The derivation for R and B are pretty
// straight forward; we just invert the formulas for U and V above. But deriving
// the formula for G is a bit more complicated:
@@ -440,6 +476,85 @@
}
}
+// All of these conversions are derived from the respective input YUV->RGB conversion followed by
+// the RGB->YUV for the receiving encoding. They are consistent with the RGB<->YUV functions in this
+// file, given that we uses BT.709 encoding for sRGB and BT.601 encoding for Display-P3, to match
+// DataSpace.
+
+Color yuv709To601(Color e_gamma) {
+ return {{{ 1.0f * e_gamma.y + 0.101579f * e_gamma.u + 0.196076f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.989854f * e_gamma.u + -0.110653f * e_gamma.v,
+ 0.0f * e_gamma.y + -0.072453f * e_gamma.u + 0.983398f * e_gamma.v }}};
+}
+
+Color yuv709To2100(Color e_gamma) {
+ return {{{ 1.0f * e_gamma.y + -0.016969f * e_gamma.u + 0.096312f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.995306f * e_gamma.u + -0.051192f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.011507f * e_gamma.u + 1.002637f * e_gamma.v }}};
+}
+
+Color yuv601To709(Color e_gamma) {
+ return {{{ 1.0f * e_gamma.y + -0.118188f * e_gamma.u + -0.212685f * e_gamma.v,
+ 0.0f * e_gamma.y + 1.018640f * e_gamma.u + 0.114618f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.075049f * e_gamma.u + 1.025327f * e_gamma.v }}};
+}
+
+Color yuv601To2100(Color e_gamma) {
+ return {{{ 1.0f * e_gamma.y + -0.128245f * e_gamma.u + -0.115879f * e_gamma.v,
+ 0.0f * e_gamma.y + 1.010016f * e_gamma.u + 0.061592f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.086969f * e_gamma.u + 1.029350f * e_gamma.v }}};
+}
+
+Color yuv2100To709(Color e_gamma) {
+ return {{{ 1.0f * e_gamma.y + 0.018149f * e_gamma.u + -0.095132f * e_gamma.v,
+ 0.0f * e_gamma.y + 1.004123f * e_gamma.u + 0.051267f * e_gamma.v,
+ 0.0f * e_gamma.y + -0.011524f * e_gamma.u + 0.996782f * e_gamma.v }}};
+}
+
+Color yuv2100To601(Color e_gamma) {
+ return {{{ 1.0f * e_gamma.y + 0.117887f * e_gamma.u + 0.105521f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.995211f * e_gamma.u + -0.059549f * e_gamma.v,
+ 0.0f * e_gamma.y + -0.084085f * e_gamma.u + 0.976518f * e_gamma.v }}};
+}
+
+void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma,
+ ColorTransformFn fn) {
+ Color yuv1 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 );
+ Color yuv2 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 );
+ Color yuv3 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 + 1);
+ Color yuv4 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 + 1);
+
+ yuv1 = fn(yuv1);
+ yuv2 = fn(yuv2);
+ yuv3 = fn(yuv3);
+ yuv4 = fn(yuv4);
+
+ Color new_uv = (yuv1 + yuv2 + yuv3 + yuv4) / 4.0f;
+
+ size_t pixel_y1_idx = x_chroma * 2 + y_chroma * 2 * image->width;
+ size_t pixel_y2_idx = (x_chroma * 2 + 1) + y_chroma * 2 * image->width;
+ size_t pixel_y3_idx = x_chroma * 2 + (y_chroma * 2 + 1) * image->width;
+ size_t pixel_y4_idx = (x_chroma * 2 + 1) + (y_chroma * 2 + 1) * image->width;
+
+ uint8_t& y1_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y1_idx];
+ uint8_t& y2_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y2_idx];
+ uint8_t& y3_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y3_idx];
+ uint8_t& y4_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y4_idx];
+
+ size_t pixel_count = image->width * image->height;
+ size_t pixel_uv_idx = x_chroma + y_chroma * (image->width / 2);
+
+ uint8_t& u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx];
+ uint8_t& v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx];
+
+ y1_uint = static_cast<uint8_t>(CLIP3((yuv1.y * 255.0f + 0.5f), 0, 255));
+ y2_uint = static_cast<uint8_t>(CLIP3((yuv2.y * 255.0f + 0.5f), 0, 255));
+ y3_uint = static_cast<uint8_t>(CLIP3((yuv3.y * 255.0f + 0.5f), 0, 255));
+ y4_uint = static_cast<uint8_t>(CLIP3((yuv4.y * 255.0f + 0.5f), 0, 255));
+
+ u_uint = static_cast<uint8_t>(CLIP3((new_uv.u * 255.0f + 128.0f + 0.5f), 0, 255));
+ v_uint = static_cast<uint8_t>(CLIP3((new_uv.v * 255.0f + 128.0f + 0.5f), 0, 255));
+}
////////////////////////////////////////////////////////////////////////////////
// Gain map calculations
diff --git a/libs/ultrahdr/icc.cpp b/libs/ultrahdr/icc.cpp
index c807705..e41b645 100644
--- a/libs/ultrahdr/icc.cpp
+++ b/libs/ultrahdr/icc.cpp
@@ -14,8 +14,11 @@
* limitations under the License.
*/
+#ifndef USE_BIG_ENDIAN
+#define USE_BIG_ENDIAN true
+#endif
+
#include <ultrahdr/icc.h>
-#include <ultrahdr/gainmapmath.h>
#include <vector>
#include <utils/Log.h>
@@ -180,7 +183,7 @@
uint32_t total_length = text_length * 2 + sizeof(header);
total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- sp<DataStruct> dataStruct = new DataStruct(total_length);
+ sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
if (!dataStruct->write(header, sizeof(header))) {
ALOGE("write_text_tag(): error in writing data");
@@ -204,7 +207,7 @@
static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(y))),
static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(z))),
};
- sp<DataStruct> dataStruct = new DataStruct(sizeof(data));
+ sp<DataStruct> dataStruct = sp<DataStruct>::make(sizeof(data));
dataStruct->write(&data, sizeof(data));
return dataStruct;
}
@@ -212,10 +215,10 @@
sp<DataStruct> IccHelper::write_trc_tag(const int table_entries, const void* table_16) {
int total_length = 4 + 4 + 4 + table_entries * 2;
total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- sp<DataStruct> dataStruct = new DataStruct(total_length);
+ sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
dataStruct->write32(Endian_SwapBE32(kTAG_CurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count
for (size_t i = 0; i < table_entries; ++i) {
uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i];
dataStruct->write16(value);
@@ -223,14 +226,30 @@
return dataStruct;
}
-sp<DataStruct> IccHelper::write_trc_tag_for_linear() {
- int total_length = 16;
+sp<DataStruct> IccHelper::write_trc_tag(const TransferFunction& fn) {
+ if (fn.a == 1.f && fn.b == 0.f && fn.c == 0.f
+ && fn.d == 0.f && fn.e == 0.f && fn.f == 0.f) {
+ int total_length = 16;
+ sp<DataStruct> dataStruct = new DataStruct(total_length);
+ dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+ return dataStruct;
+ }
+
+ int total_length = 40;
sp<DataStruct> dataStruct = new DataStruct(total_length);
dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(1.0)));
-
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE16(kGABCDEF_ParaCurveType));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.a)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.b)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.c)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.d)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.e)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.f)));
return dataStruct;
}
@@ -263,7 +282,7 @@
sp<DataStruct> IccHelper::write_cicp_tag(uint32_t color_primaries,
uint32_t transfer_characteristics) {
int total_length = 12; // 4 + 4 + 1 + 1 + 1 + 1
- sp<DataStruct> dataStruct = new DataStruct(total_length);
+ sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
dataStruct->write32(Endian_SwapBE32(kTAG_cicp)); // Type signature
dataStruct->write32(0); // Reserved
dataStruct->write8(color_primaries); // Color primaries
@@ -314,7 +333,7 @@
int total_length = 20 + 2 * value_count;
total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- sp<DataStruct> dataStruct = new DataStruct(total_length);
+ sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
for (size_t i = 0; i < 16; ++i) {
dataStruct->write8(i < kNumChannels ? grid_points[i] : 0); // Grid size
@@ -345,7 +364,7 @@
// The "B" curve is required.
for (size_t i = 0; i < kNumChannels; ++i) {
- b_curves_data[i] = write_trc_tag_for_linear();
+ b_curves_data[i] = write_trc_tag(kLinear_TransFun);
}
// The "A" curve and CLUT are optional.
@@ -358,7 +377,7 @@
a_curves_offset = clut_offset + clut->getLength();
for (size_t i = 0; i < kNumChannels; ++i) {
- a_curves_data[i] = write_trc_tag_for_linear();
+ a_curves_data[i] = write_trc_tag(kLinear_TransFun);
}
}
@@ -372,7 +391,7 @@
total_length += a_curves_data[i]->getLength();
}
}
- sp<DataStruct> dataStruct = new DataStruct(total_length);
+ sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
dataStruct->write32(Endian_SwapBE32(type)); // Type signature
dataStruct->write32(0); // Reserved
dataStruct->write8(kNumChannels); // Input channels
@@ -421,7 +440,7 @@
break;
default:
// Should not fall here.
- return new DataStruct(0);
+ return nullptr;
}
// Compute primaries.
@@ -456,9 +475,9 @@
tags.emplace_back(kTAG_bTRC,
write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
} else {
- tags.emplace_back(kTAG_rTRC, write_trc_tag_for_linear());
- tags.emplace_back(kTAG_gTRC, write_trc_tag_for_linear());
- tags.emplace_back(kTAG_bTRC, write_trc_tag_for_linear());
+ tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun));
+ tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun));
+ tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun));
}
}
@@ -540,13 +559,21 @@
size_t tag_table_size = kICCTagTableEntrySize * tags.size();
size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size;
+ sp<DataStruct> dataStruct = sp<DataStruct>::make(profile_size + kICCIdentifierSize);
+
+ // Write identifier, chunk count, and chunk ID
+ if (!dataStruct->write(kICCIdentifier, sizeof(kICCIdentifier)) ||
+ !dataStruct->write8(1) || !dataStruct->write8(1)) {
+ ALOGE("writeIccProfile(): error in identifier");
+ return dataStruct;
+ }
+
// Write the header.
header.data_color_space = Endian_SwapBE32(Signature_RGB);
header.pcs = Endian_SwapBE32(tf == ULTRAHDR_TF_PQ ? Signature_Lab : Signature_XYZ);
header.size = Endian_SwapBE32(profile_size);
header.tag_count = Endian_SwapBE32(tags.size());
- sp<DataStruct> dataStruct = new DataStruct(profile_size);
if (!dataStruct->write(&header, sizeof(header))) {
ALOGE("writeIccProfile(): error in header");
return dataStruct;
@@ -582,4 +609,84 @@
return dataStruct;
}
-} // namespace android::ultrahdr
\ No newline at end of file
+bool IccHelper::tagsEqualToMatrix(const Matrix3x3& matrix,
+ const uint8_t* red_tag,
+ const uint8_t* green_tag,
+ const uint8_t* blue_tag) {
+ sp<DataStruct> red_tag_test = write_xyz_tag(matrix.vals[0][0], matrix.vals[1][0],
+ matrix.vals[2][0]);
+ sp<DataStruct> green_tag_test = write_xyz_tag(matrix.vals[0][1], matrix.vals[1][1],
+ matrix.vals[2][1]);
+ sp<DataStruct> blue_tag_test = write_xyz_tag(matrix.vals[0][2], matrix.vals[1][2],
+ matrix.vals[2][2]);
+ return memcmp(red_tag, red_tag_test->getData(), kColorantTagSize) == 0 &&
+ memcmp(green_tag, green_tag_test->getData(), kColorantTagSize) == 0 &&
+ memcmp(blue_tag, blue_tag_test->getData(), kColorantTagSize) == 0;
+}
+
+ultrahdr_color_gamut IccHelper::readIccColorGamut(void* icc_data, size_t icc_size) {
+ // Each tag table entry consists of 3 fields of 4 bytes each.
+ static const size_t kTagTableEntrySize = 12;
+
+ if (icc_data == nullptr || icc_size < sizeof(ICCHeader) + kICCIdentifierSize) {
+ return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ }
+
+ if (memcmp(icc_data, kICCIdentifier, sizeof(kICCIdentifier)) != 0) {
+ return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ }
+
+ uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc_data) + kICCIdentifierSize;
+
+ ICCHeader* header = reinterpret_cast<ICCHeader*>(icc_bytes);
+
+ // Use 0 to indicate not found, since offsets are always relative to start
+ // of ICC data and therefore a tag offset of zero would never be valid.
+ size_t red_primary_offset = 0, green_primary_offset = 0, blue_primary_offset = 0;
+ size_t red_primary_size = 0, green_primary_size = 0, blue_primary_size = 0;
+ for (size_t tag_idx = 0; tag_idx < Endian_SwapBE32(header->tag_count); ++tag_idx) {
+ uint32_t* tag_entry_start = reinterpret_cast<uint32_t*>(
+ icc_bytes + sizeof(ICCHeader) + tag_idx * kTagTableEntrySize);
+ // first 4 bytes are the tag signature, next 4 bytes are the tag offset,
+ // last 4 bytes are the tag length in bytes.
+ if (red_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_rXYZ)) {
+ red_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
+ red_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
+ } else if (green_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_gXYZ)) {
+ green_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
+ green_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
+ } else if (blue_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_bXYZ)) {
+ blue_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
+ blue_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
+ }
+ }
+
+ if (red_primary_offset == 0 || red_primary_size != kColorantTagSize ||
+ kICCIdentifierSize + red_primary_offset + red_primary_size > icc_size ||
+ green_primary_offset == 0 || green_primary_size != kColorantTagSize ||
+ kICCIdentifierSize + green_primary_offset + green_primary_size > icc_size ||
+ blue_primary_offset == 0 || blue_primary_size != kColorantTagSize ||
+ kICCIdentifierSize + blue_primary_offset + blue_primary_size > icc_size) {
+ return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ }
+
+ uint8_t* red_tag = icc_bytes + red_primary_offset;
+ uint8_t* green_tag = icc_bytes + green_primary_offset;
+ uint8_t* blue_tag = icc_bytes + blue_primary_offset;
+
+ // Serialize tags as we do on encode and compare what we find to that to
+ // determine the gamut (since we don't have a need yet for full deserialize).
+ if (tagsEqualToMatrix(kSRGB, red_tag, green_tag, blue_tag)) {
+ return ULTRAHDR_COLORGAMUT_BT709;
+ } else if (tagsEqualToMatrix(kDisplayP3, red_tag, green_tag, blue_tag)) {
+ return ULTRAHDR_COLORGAMUT_P3;
+ } else if (tagsEqualToMatrix(kRec2020, red_tag, green_tag, blue_tag)) {
+ return ULTRAHDR_COLORGAMUT_BT2100;
+ }
+
+ // Didn't find a match to one of the profiles we write; indicate the gamut
+ // is unspecified since we don't understand it.
+ return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+}
+
+} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
index abc9356..50b4d2f 100644
--- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h
+++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
@@ -51,6 +51,23 @@
typedef Color (*ColorTransformFn)(Color);
typedef float (*ColorCalculationFn)(Color);
+// A transfer function mapping encoded values to linear values,
+// represented by this 7-parameter piecewise function:
+//
+// linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d
+// = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
+//
+// (A simple gamma transfer function sets g to gamma and a to 1.)
+typedef struct TransferFunction {
+ float g, a,b,c,d,e,f;
+} TransferFunction;
+
+static constexpr TransferFunction kSRGB_TransFun =
+ { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f };
+
+static constexpr TransferFunction kLinear_TransFun =
+ { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
inline Color operator+=(Color& lhs, const Color& rhs) {
lhs.r += rhs.r;
lhs.g += rhs.g;
@@ -218,24 +235,30 @@
// except for those concerning transfer functions.
/*
- * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1.
+ * Calculate the luminance of a linear RGB sRGB pixel, according to
+ * IEC 61966-2-1/Amd 1:2003.
*
* [0.0, 1.0] range in and out.
*/
float srgbLuminance(Color e);
/*
- * Convert from OETF'd srgb YUV to RGB, according to ECMA TR/98.
+ * Convert from OETF'd srgb RGB to YUV, according to ITU-R BT.709-6.
+ *
+ * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace.
+ */
+Color srgbRgbToYuv(Color e_gamma);
+
+
+/*
+ * Convert from OETF'd srgb YUV to RGB, according to ITU-R BT.709-6.
+ *
+ * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace.
*/
Color srgbYuvToRgb(Color e_gamma);
/*
- * Convert from OETF'd srgb RGB to YUV, according to ECMA TR/98.
- */
-Color srgbRgbToYuv(Color e_gamma);
-
-/*
- * Convert from srgb to linear, according to IEC 61966-2-1.
+ * Convert from srgb to linear, according to IEC 61966-2-1/Amd 1:2003.
*
* [0.0, 1.0] range in and out.
*/
@@ -257,6 +280,20 @@
*/
float p3Luminance(Color e);
+/*
+ * Convert from OETF'd P3 RGB to YUV, according to ITU-R BT.601-7.
+ *
+ * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace.
+ */
+Color p3RgbToYuv(Color e_gamma);
+
+/*
+ * Convert from OETF'd P3 YUV to RGB, according to ITU-R BT.601-7.
+ *
+ * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace.
+ */
+Color p3YuvToRgb(Color e_gamma);
+
////////////////////////////////////////////////////////////////////////////////
// BT.2100 transformations - according to ITU-R BT.2100-2
@@ -269,12 +306,16 @@
float bt2100Luminance(Color e);
/*
- * Convert from OETF'd BT.2100 RGB to YUV.
+ * Convert from OETF'd BT.2100 RGB to YUV, according to ITU-R BT.2100-2.
+ *
+ * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace.
*/
Color bt2100RgbToYuv(Color e_gamma);
/*
- * Convert from OETF'd BT.2100 YUV to RGB.
+ * Convert from OETF'd BT.2100 YUV to RGB, according to ITU-R BT.2100-2.
+ *
+ * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace.
*/
Color bt2100YuvToRgb(Color e_gamma);
@@ -288,7 +329,7 @@
float hlgOetfLUT(float e);
Color hlgOetfLUT(Color e);
-constexpr size_t kHlgOETFPrecision = 10;
+constexpr size_t kHlgOETFPrecision = 16;
constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;
/*
@@ -301,7 +342,7 @@
float hlgInvOetfLUT(float e_gamma);
Color hlgInvOetfLUT(Color e_gamma);
-constexpr size_t kHlgInvOETFPrecision = 10;
+constexpr size_t kHlgInvOETFPrecision = 12;
constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;
/*
@@ -314,7 +355,7 @@
float pqOetfLUT(float e);
Color pqOetfLUT(Color e);
-constexpr size_t kPqOETFPrecision = 10;
+constexpr size_t kPqOETFPrecision = 16;
constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
/*
@@ -327,7 +368,7 @@
float pqInvOetfLUT(float e_gamma);
Color pqInvOetfLUT(Color e_gamma);
-constexpr size_t kPqInvOETFPrecision = 10;
+constexpr size_t kPqInvOETFPrecision = 12;
constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
@@ -358,6 +399,31 @@
*/
ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut, ultrahdr_color_gamut hdr_gamut);
+/*
+ * Convert between YUV encodings, according to ITU-R BT.709-6, ITU-R BT.601-7, and ITU-R BT.2100-2.
+ *
+ * Bt.709 and Bt.2100 have well-defined YUV encodings; Display-P3's is less well defined, but is
+ * treated as Bt.601 by DataSpace, hence we do the same.
+ */
+Color yuv709To601(Color e_gamma);
+Color yuv709To2100(Color e_gamma);
+Color yuv601To709(Color e_gamma);
+Color yuv601To2100(Color e_gamma);
+Color yuv2100To709(Color e_gamma);
+Color yuv2100To601(Color e_gamma);
+
+/*
+ * Performs a transformation at the chroma x and y coordinates provided on a YUV420 image.
+ *
+ * Apply the transformation by determining transformed YUV for each of the 4 Y + 1 UV; each Y gets
+ * this result, and UV gets the averaged result.
+ *
+ * x_chroma and y_chroma should be less than or equal to half the image's width and height
+ * respecitively, since input is 4:2:0 subsampled.
+ */
+void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma,
+ ColorTransformFn fn);
+
////////////////////////////////////////////////////////////////////////////////
// Gain map calculations
@@ -365,6 +431,10 @@
/*
* Calculate the 8-bit unsigned integer gain value for the given SDR and HDR
* luminances in linear space, and the hdr ratio to encode against.
+ *
+ * Note: since this library always uses gamma of 1.0, offsetSdr of 0.0, and
+ * offsetHdr of 0.0, this function doesn't handle different metadata values for
+ * these fields.
*/
uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata);
uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
@@ -373,6 +443,10 @@
/*
* Calculates the linear luminance in nits after applying the given gain
* value, with the given hdr ratio, to the given sdr input in the range [0, 1].
+ *
+ * Note: similar to encodeGain(), this function only supports gamma 1.0,
+ * offsetSdr 0.0, offsetHdr 0.0, hdrCapacityMin 1.0, and hdrCapacityMax equal to
+ * gainMapMax, as this library encodes.
*/
Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata);
Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata, float displayBoost);
diff --git a/libs/ultrahdr/include/ultrahdr/icc.h b/libs/ultrahdr/include/ultrahdr/icc.h
index 7f6ab88..971b267 100644
--- a/libs/ultrahdr/include/ultrahdr/icc.h
+++ b/libs/ultrahdr/include/ultrahdr/icc.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_ULTRAHDR_ICC_H
#define ANDROID_ULTRAHDR_ICC_H
+#include <ultrahdr/gainmapmath.h>
#include <ultrahdr/jpegr.h>
#include <ultrahdr/jpegrutils.h>
#include <utils/RefBase.h>
@@ -56,12 +57,16 @@
Signature_XYZ = 0x58595A20,
};
-
typedef uint32_t FourByteTag;
static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) {
return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d);
}
+static constexpr char kICCIdentifier[] = "ICC_PROFILE";
+// 12 for the actual identifier, +2 for the chunk count and chunk index which
+// will always follow.
+static constexpr size_t kICCIdentifierSize = 14;
+
// This is equal to the header size according to the ICC specification (128)
// plus the size of the tag count (4). We include the tag count since we
// always require it to be present anyway.
@@ -70,6 +75,10 @@
// Contains a signature (4), offset (4), and size (4).
static constexpr size_t kICCTagTableEntrySize = 12;
+// size should be 20; 4 bytes for type descriptor, 4 bytes reserved, 12
+// bytes for a single XYZ number type (4 bytes per coordinate).
+static constexpr size_t kColorantTagSize = 20;
+
static constexpr uint32_t kDisplay_Profile = SetFourByteTag('m', 'n', 't', 'r');
static constexpr uint32_t kRGB_ColorSpace = SetFourByteTag('R', 'G', 'B', ' ');
static constexpr uint32_t kXYZ_PCSSpace = SetFourByteTag('X', 'Y', 'Z', ' ');
@@ -214,7 +223,7 @@
const ultrahdr_color_gamut gamut);
static sp<DataStruct> write_xyz_tag(float x, float y, float z);
static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
- static sp<DataStruct> write_trc_tag_for_linear();
+ static sp<DataStruct> write_trc_tag(const TransferFunction& fn);
static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L);
static sp<DataStruct> write_cicp_tag(uint32_t color_primaries,
uint32_t transfer_characteristics);
@@ -225,10 +234,23 @@
static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]);
static sp<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16);
+ // Checks if a set of xyz tags is equivalent to a 3x3 Matrix. Each input
+ // tag buffer assumed to be at least kColorantTagSize in size.
+ static bool tagsEqualToMatrix(const Matrix3x3& matrix,
+ const uint8_t* red_tag,
+ const uint8_t* green_tag,
+ const uint8_t* blue_tag);
+
public:
+ // Output includes JPEG embedding identifier and chunk information, but not
+ // APPx information.
static sp<DataStruct> writeIccProfile(const ultrahdr_transfer_function tf,
const ultrahdr_color_gamut gamut);
+ // NOTE: this function is not robust; it can infer gamuts that IccHelper
+ // writes out but should not be considered a reference implementation for
+ // robust parsing of ICC profiles or their gamuts.
+ static ultrahdr_color_gamut readIccColorGamut(void* icc_data, size_t icc_size);
};
} // namespace android::ultrahdr
-#endif //ANDROID_ULTRAHDR_ICC_H
\ No newline at end of file
+#endif //ANDROID_ULTRAHDR_ICC_H
diff --git a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
index f642bad..8b5499a 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
@@ -25,6 +25,10 @@
}
#include <utils/Errors.h>
#include <vector>
+
+static const int kMaxWidth = 8192;
+static const int kMaxHeight = 8192;
+
namespace android::ultrahdr {
/*
* Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
@@ -79,11 +83,14 @@
*/
size_t getEXIFSize();
/*
- * Returns the position offset of EXIF package
- * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>),
- * or -1 if no EXIF exists.
+ * Returns the ICC data from the image.
*/
- int getEXIFPos() { return mExifPos; }
+ void* getICCPtr();
+ /*
+ * Returns the decompressed ICC buffer size. This method must be called only after
+ * calling decompressImage() or getCompressedImageParameters().
+ */
+ size_t getICCSize();
/*
* Decompresses metadata of the image. All vectors are owned by the caller.
*/
@@ -108,12 +115,12 @@
std::vector<JOCTET> mXMPBuffer;
// The buffer that holds EXIF Data.
std::vector<JOCTET> mEXIFBuffer;
+ // The buffer that holds ICC Data.
+ std::vector<JOCTET> mICCBuffer;
// Resolution of the decompressed image.
size_t mWidth;
size_t mHeight;
- // Position of EXIF package, default value is -1 which means no EXIF package appears.
- size_t mExifPos;
};
} /* namespace android::ultrahdr */
diff --git a/libs/ultrahdr/include/ultrahdr/jpegr.h b/libs/ultrahdr/include/ultrahdr/jpegr.h
index 1f9bd0f..f80496a 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegr.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegr.h
@@ -125,7 +125,7 @@
*
* Generate gain map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
* the gain map to the end of the compressed JPEG. HDR and SDR inputs must be the same
- * resolution.
+ * resolution. SDR input is assumed to use the sRGB transfer function.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param hdr_tf transfer function of the HDR image
@@ -152,7 +152,9 @@
* This method requires HAL Hardware JPEG encoder.
*
* Generate gain map from the HDR and SDR inputs, append the gain map to the end of the
- * compressed JPEG. HDR and SDR inputs must be the same resolution and color space.
+ * compressed JPEG. Adds an ICC profile if one isn't present in the input JPEG image. HDR and
+ * SDR inputs must be the same resolution and color space. SDR image is assumed to use the sRGB
+ * transfer function.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* Note: the SDR image must be the decoded version of the JPEG
@@ -178,8 +180,9 @@
* This method requires HAL Hardware JPEG encoder.
*
* Decode the compressed 8-bit JPEG image to YUV SDR, generate gain map from the HDR input
- * and the decoded SDR result, append the gain map to the end of the compressed JPEG. HDR
- * and SDR inputs must be the same resolution.
+ * and the decoded SDR result, append the gain map to the end of the compressed JPEG. Adds an
+ * ICC profile if one isn't present in the input JPEG image. HDR and SDR inputs must be the same
+ * resolution. JPEG image is assumed to use the sRGB transfer function.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param compressed_jpeg_image compressed 8-bit JPEG image
* @param hdr_tf transfer function of the HDR image
@@ -198,7 +201,8 @@
* Encode API-4
* Assemble JPEGR image from SDR JPEG and gainmap JPEG.
*
- * Assemble the primary JPEG image, the gain map and the metadata to JPEG/R format.
+ * Assemble the primary JPEG image, the gain map and the metadata to JPEG/R format. Adds an ICC
+ * profile if one isn't present in the input JPEG image.
* @param compressed_jpeg_image compressed 8-bit JPEG image
* @param compressed_gainmap compressed 8-bit JPEG single channel image
* @param metadata metadata to be written in XMP of the primary jpeg
@@ -217,6 +221,13 @@
* Decode API
* Decompress JPEGR image.
*
+ * This method assumes that the JPEGR image contains an ICC profile with primaries that match
+ * those of a color gamut that this library is aware of; Bt.709, Display-P3, or Bt.2100. It also
+ * assumes the base image uses the sRGB transfer function.
+ *
+ * This method only supports single gain map metadata values for fields that allow multi-channel
+ * metadata values.
+ *
* @param compressed_jpegr_image compressed JPEGR image.
* @param dest destination of the uncompressed JPEGR image.
* @param max_display_boost (optional) the maximum available boost supported by a display,
@@ -258,6 +269,9 @@
/*
* Gets Info from JPEGR file without decoding it.
*
+ * This method only supports single gain map metadata values for fields that allow multi-channel
+ * metadata values.
+ *
* The output is filled jpegr_info structure
* @param compressed_jpegr_image compressed JPEGR image
* @param jpegr_info pointer to output JPEGR info. Members of jpegr_info
@@ -270,26 +284,30 @@
/*
* This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
* 10-bit yuv images as input, and calculate the uncompressed gain map. The input images
- * must be the same resolution.
+ * must be the same resolution. The SDR input is assumed to use the sRGB transfer function.
*
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param hdr_tf transfer function of the HDR image
* @param dest gain map; caller responsible for memory of data
* @param metadata max_content_boost is filled in
+ * @param sdr_is_601 if true, then use BT.601 decoding of YUV regardless of SDR image gamut
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_p010_image,
ultrahdr_transfer_function hdr_tf,
ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr dest);
+ jr_uncompressed_ptr dest,
+ bool sdr_is_601 = false);
/*
* This method is called in the decoding pipeline. It will take the uncompressed (decoded)
* 8-bit yuv image, the uncompressed (decoded) gain map, and extracted JPEG/R metadata as
* input, and calculate the 10-bit recovered image. The recovered output image is the same
* color gamut as the SDR image, with HLG transfer function, and is in RGBA1010102 data format.
+ * The SDR image is assumed to use the sRGB transfer function. The SDR image is also assumed to
+ * be a decoded JPEG for the purpose of YUV interpration.
*
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_gain_map uncompressed gain map
@@ -330,16 +348,6 @@
status_t extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr_image,
jr_compressed_ptr primary_image,
jr_compressed_ptr gain_map);
- /*
- * This method is called in the decoding pipeline. It will read XMP metadata to find the start
- * position of the compressed gain map, and will extract the compressed gain map.
- *
- * @param compressed_jpegr_image compressed JPEGR image
- * @param dest destination of compressed gain map
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t extractGainMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr dest);
/*
* This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
@@ -353,6 +361,8 @@
* @param compressed_jpeg_image compressed 8-bit JPEG image
* @param compress_gain_map compressed recover map
* @param (nullable) exif EXIF package
+ * @param (nullable) icc ICC package
+ * @param icc_size length in bytes of ICC package
* @param metadata JPEG/R metadata to encode in XMP of the jpeg
* @param dest compressed JPEGR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
@@ -360,6 +370,7 @@
status_t appendGainMap(jr_compressed_ptr compressed_jpeg_image,
jr_compressed_ptr compressed_gain_map,
jr_exif_ptr exif,
+ void* icc, size_t icc_size,
ultrahdr_metadata_ptr metadata,
jr_compressed_ptr dest);
@@ -374,6 +385,22 @@
jr_uncompressed_ptr dest);
/*
+ * This method will convert a YUV420 image from one YUV encoding to another in-place (eg.
+ * Bt.709 to Bt.601 YUV encoding).
+ *
+ * src_encoding and dest_encoding indicate the encoding via the YUV conversion defined for that
+ * gamut. P3 indicates Rec.601, since this is how DataSpace encodes Display-P3 YUV data.
+ *
+ * @param image the YUV420 image to convert
+ * @param src_encoding input YUV encoding
+ * @param dest_encoding output YUV encoding
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t convertYuv(jr_uncompressed_ptr image,
+ ultrahdr_color_gamut src_encoding,
+ ultrahdr_color_gamut dest_encoding);
+
+ /*
* This method will check the validity of the input arguments.
*
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
diff --git a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
index 9f59c3e..5420e1c 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
@@ -42,6 +42,9 @@
ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4,
ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5,
ERROR_JPEGR_INVALID_TRANS_FUNC = JPEGR_IO_ERROR_BASE - 6,
+ ERROR_JPEGR_INVALID_METADATA = JPEGR_IO_ERROR_BASE - 7,
+ ERROR_JPEGR_UNSUPPORTED_METADATA = JPEGR_IO_ERROR_BASE - 8,
+ ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND = JPEGR_IO_ERROR_BASE - 9,
JPEGR_RUNTIME_ERROR_BASE = -20000,
ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
index d6153e9..17cc971 100644
--- a/libs/ultrahdr/include/ultrahdr/ultrahdr.h
+++ b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
@@ -20,7 +20,7 @@
namespace android::ultrahdr {
// Color gamuts for image data
typedef enum {
- ULTRAHDR_COLORGAMUT_UNSPECIFIED,
+ ULTRAHDR_COLORGAMUT_UNSPECIFIED = -1,
ULTRAHDR_COLORGAMUT_BT709,
ULTRAHDR_COLORGAMUT_P3,
ULTRAHDR_COLORGAMUT_BT2100,
@@ -49,14 +49,28 @@
/*
* Holds information for gain map related metadata.
+ *
+ * Not: all values stored in linear. This differs from the metadata encoding in XMP, where
+ * maxContentBoost (aka gainMapMax), minContentBoost (aka gainMapMin), hdrCapacityMin, and
+ * hdrCapacityMax are stored in log2 space.
*/
struct ultrahdr_metadata_struct {
- // Ultra HDR library version
- const char* version;
+ // Ultra HDR format version
+ std::string version;
// Max Content Boost for the map
float maxContentBoost;
// Min Content Boost for the map
float minContentBoost;
+ // Gamma of the map data
+ float gamma;
+ // Offset for SDR data in map calculations
+ float offsetSdr;
+ // Offset for HDR data in map calculations
+ float offsetHdr;
+ // HDR capacity to apply the map at all
+ float hdrCapacityMin;
+ // HDR capacity to apply the map completely
+ float hdrCapacityMax;
};
typedef struct ultrahdr_metadata_struct* ultrahdr_metadata_ptr;
diff --git a/libs/ultrahdr/jpegdecoderhelper.cpp b/libs/ultrahdr/jpegdecoderhelper.cpp
index 6c3a8c1..c077bcb 100644
--- a/libs/ultrahdr/jpegdecoderhelper.cpp
+++ b/libs/ultrahdr/jpegdecoderhelper.cpp
@@ -93,7 +93,6 @@
}
JpegDecoderHelper::JpegDecoderHelper() {
- mExifPos = 0;
}
JpegDecoderHelper::~JpegDecoderHelper() {
@@ -138,6 +137,14 @@
return mEXIFBuffer.size();
}
+void* JpegDecoderHelper::getICCPtr() {
+ return mICCBuffer.data();
+}
+
+size_t JpegDecoderHelper::getICCSize() {
+ return mICCBuffer.size();
+}
+
size_t JpegDecoderHelper::getDecompressedImageWidth() {
return mWidth;
}
@@ -150,6 +157,7 @@
jpeg_decompress_struct cinfo;
jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
jpegrerror_mgr myerr;
+ bool status = true;
cinfo.err = jpeg_std_error(&myerr.pub);
myerr.pub.error_exit = jpegrerror_exit;
@@ -167,31 +175,21 @@
cinfo.src = &mgr;
jpeg_read_header(&cinfo, TRUE);
- // Save XMP data and EXIF data.
- // Here we only handle the first XMP / EXIF package.
- // The parameter pos is used for capturing start offset of EXIF, which is hacky, but working...
+ // Save XMP data, EXIF data, and ICC data.
+ // Here we only handle the first XMP / EXIF / ICC package.
// We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
// two bytes of package length which is stored in marker->original_length, and the real data
- // which is stored in marker->data. The pos is adding up all previous package lengths (
- // 4 bytes marker and length, marker->original_length) before EXIF appears. Note that here we
- // we are using marker->original_length instead of marker->data_length because in case the real
- // package length is larger than the limitation, jpeg-turbo will only copy the data within the
- // limitation (represented by data_length) and this may vary from original_length / real offset.
- // A better solution is making jpeg_marker_struct holding the offset, but currently it doesn't.
+ // which is stored in marker->data.
bool exifAppears = false;
bool xmpAppears = false;
- size_t pos = 2; // position after SOI
+ bool iccAppears = false;
for (jpeg_marker_struct* marker = cinfo.marker_list;
- marker && !(exifAppears && xmpAppears);
+ marker && !(exifAppears && xmpAppears && iccAppears);
marker = marker->next) {
- pos += 4;
- pos += marker->original_length;
-
- if (marker->marker != kAPP1Marker) {
+ if (marker->marker != kAPP1Marker && marker->marker != kAPP2Marker) {
continue;
}
-
const unsigned int len = marker->data_length;
if (!xmpAppears &&
len > kXmpNameSpace.size() &&
@@ -209,17 +207,31 @@
mEXIFBuffer.resize(len, 0);
memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
exifAppears = true;
- mExifPos = pos - marker->original_length;
+ } else if (!iccAppears &&
+ len > sizeof(kICCSig) &&
+ !memcmp(marker->data, kICCSig, sizeof(kICCSig))) {
+ mICCBuffer.resize(len, 0);
+ memcpy(static_cast<void*>(mICCBuffer.data()), marker->data, len);
+ iccAppears = true;
}
}
+ if (cinfo.image_width > kMaxWidth || cinfo.image_height > kMaxHeight) {
+ // constraint on max width and max height is only due to alloc constraints
+ // tune these values basing on the target device
+ status = false;
+ goto CleanUp;
+ }
+
mWidth = cinfo.image_width;
mHeight = cinfo.image_height;
if (decodeToRGBA) {
if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
// We don't intend to support decoding grayscale to RGBA
- return false;
+ status = false;
+ ALOGE("%s: decoding grayscale to RGBA is unsupported", __func__);
+ goto CleanUp;
}
// 4 bytes per pixel
mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
@@ -232,7 +244,9 @@
cinfo.comp_info[0].v_samp_factor != 2 ||
cinfo.comp_info[1].v_samp_factor != 1 ||
cinfo.comp_info[2].v_samp_factor != 1) {
- return false;
+ status = false;
+ ALOGE("%s: decoding to YUV only supports 4:2:0 subsampling", __func__);
+ goto CleanUp;
}
mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
} else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
@@ -248,13 +262,15 @@
if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
- return false;
+ status = false;
+ goto CleanUp;
}
+CleanUp:
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
- return true;
+ return status;
}
bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
@@ -292,8 +308,12 @@
return false;
}
- *pWidth = cinfo.image_width;
- *pHeight = cinfo.image_height;
+ if (pWidth != nullptr) {
+ *pWidth = cinfo.image_width;
+ }
+ if (pHeight != nullptr) {
+ *pHeight = cinfo.image_height;
+ }
if (iccData != nullptr) {
for (jpeg_marker_struct* marker = cinfo.marker_list; marker;
@@ -306,9 +326,7 @@
continue;
}
- const unsigned int len = marker->data_length - kICCMarkerHeaderSize;
- const uint8_t *src = marker->data + kICCMarkerHeaderSize;
- iccData->insert(iccData->end(), src, src+len);
+ iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length);
}
}
@@ -361,7 +379,7 @@
uint8_t* y_plane = const_cast<uint8_t*>(dest);
uint8_t* u_plane = const_cast<uint8_t*>(dest + y_plane_size);
uint8_t* v_plane = const_cast<uint8_t*>(dest + y_plane_size + uv_plane_size);
- std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
memset(empty.get(), 0, cinfo->image_width);
const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
@@ -435,7 +453,7 @@
JSAMPARRAY planes[1] {y};
uint8_t* y_plane = const_cast<uint8_t*>(dest);
- std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
memset(empty.get(), 0, cinfo->image_width);
int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
diff --git a/libs/ultrahdr/jpegencoderhelper.cpp b/libs/ultrahdr/jpegencoderhelper.cpp
index 867ffbb..de621bd 100644
--- a/libs/ultrahdr/jpegencoderhelper.cpp
+++ b/libs/ultrahdr/jpegencoderhelper.cpp
@@ -22,6 +22,8 @@
namespace android::ultrahdr {
+#define ALIGNM(x, m) ((((x) + ((m) - 1)) / (m)) * (m))
+
// The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
struct destination_mgr {
public:
@@ -105,12 +107,11 @@
jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
}
- if (!compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel)) {
- return false;
- }
+ bool status = compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel);
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
- return true;
+
+ return status;
}
void JpegEncoderHelper::setJpegDestination(jpeg_compress_struct* cinfo) {
@@ -172,9 +173,40 @@
uint8_t* y_plane = const_cast<uint8_t*>(yuv);
uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size);
uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size);
- std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
memset(empty.get(), 0, cinfo->image_width);
+ const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+ const bool is_width_aligned = (aligned_width == cinfo->image_width);
+ std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
+ uint8_t* y_plane_intrm = nullptr;
+ uint8_t* u_plane_intrm = nullptr;
+ uint8_t* v_plane_intrm = nullptr;
+ JSAMPROW y_intrm[kCompressBatchSize];
+ JSAMPROW cb_intrm[kCompressBatchSize / 2];
+ JSAMPROW cr_intrm[kCompressBatchSize / 2];
+ JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
+ if (!is_width_aligned) {
+ size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
+ buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
+ y_plane_intrm = buffer_intrm.get();
+ u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
+ v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ y_intrm[i] = y_plane_intrm + i * aligned_width;
+ memset(y_intrm[i] + cinfo->image_width, 0, aligned_width - cinfo->image_width);
+ }
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ int offset_intrm = i * (aligned_width / 2);
+ cb_intrm[i] = u_plane_intrm + offset_intrm;
+ cr_intrm[i] = v_plane_intrm + offset_intrm;
+ memset(cb_intrm[i] + cinfo->image_width / 2, 0,
+ (aligned_width - cinfo->image_width) / 2);
+ memset(cr_intrm[i] + cinfo->image_width / 2, 0,
+ (aligned_width - cinfo->image_width) / 2);
+ }
+ }
+
while (cinfo->next_scanline < cinfo->image_height) {
for (int i = 0; i < kCompressBatchSize; ++i) {
size_t scanline = cinfo->next_scanline + i;
@@ -183,6 +215,9 @@
} else {
y[i] = empty.get();
}
+ if (!is_width_aligned) {
+ memcpy(y_intrm[i], y[i], cinfo->image_width);
+ }
}
// cb, cr only have half scanlines
for (int i = 0; i < kCompressBatchSize / 2; ++i) {
@@ -194,9 +229,13 @@
} else {
cb[i] = cr[i] = empty.get();
}
+ if (!is_width_aligned) {
+ memcpy(cb_intrm[i], cb[i], cinfo->image_width / 2);
+ memcpy(cr_intrm[i], cr[i], cinfo->image_width / 2);
+ }
}
-
- int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
+ kCompressBatchSize);
if (processed != kCompressBatchSize) {
ALOGE("Number of processed lines does not equal input lines.");
return false;
@@ -210,9 +249,26 @@
JSAMPARRAY planes[1] {y};
uint8_t* y_plane = const_cast<uint8_t*>(image);
- std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
memset(empty.get(), 0, cinfo->image_width);
+ const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+ bool is_width_aligned = (aligned_width == cinfo->image_width);
+ std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
+ uint8_t* y_plane_intrm = nullptr;
+ uint8_t* u_plane_intrm = nullptr;
+ JSAMPROW y_intrm[kCompressBatchSize];
+ JSAMPARRAY planes_intrm[]{y_intrm};
+ if (!is_width_aligned) {
+ size_t mcu_row_size = aligned_width * kCompressBatchSize;
+ buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
+ y_plane_intrm = buffer_intrm.get();
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ y_intrm[i] = y_plane_intrm + i * aligned_width;
+ memset(y_intrm[i] + cinfo->image_width, 0, aligned_width - cinfo->image_width);
+ }
+ }
+
while (cinfo->next_scanline < cinfo->image_height) {
for (int i = 0; i < kCompressBatchSize; ++i) {
size_t scanline = cinfo->next_scanline + i;
@@ -221,8 +277,12 @@
} else {
y[i] = empty.get();
}
+ if (!is_width_aligned) {
+ memcpy(y_intrm[i], y[i], cinfo->image_width);
+ }
}
- int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
+ kCompressBatchSize);
if (processed != kCompressBatchSize / 2) {
ALOGE("Number of processed lines does not equal input lines.");
return false;
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index c250aa0..5a601bd 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -76,9 +76,9 @@
// JPEG encoding / decoding will require block based DCT transform 16 x 16 for luma,
// and 8 x 8 for chroma.
// Width must be 16 dividable for luma, and 8 dividable for chroma.
-// If this criteria is not ficilitated, we will pad zeros based on the required block size.
+// If this criteria is not facilitated, we will pad zeros based to each line on the
+// required block size.
static const size_t kJpegBlock = JpegEncoderHelper::kCompressBatchSize;
-static const size_t kJpegBlockSquare = kJpegBlock * kJpegBlock;
// JPEG compress quality (0 ~ 100) for gain map
static const int kMapCompressQuality = 85;
@@ -119,6 +119,13 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
+ if (uncompressed_p010_image->width > kMaxWidth
+ || uncompressed_p010_image->height > kMaxHeight) {
+ ALOGE("Image dimensions cannot be larger than %dx%d, image dimensions %dx%d",
+ kMaxWidth, kMaxHeight, uncompressed_p010_image->width, uncompressed_p010_image->height);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
if (uncompressed_p010_image->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED
|| uncompressed_p010_image->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
ALOGE("Unrecognized p010 color gamut %d", uncompressed_p010_image->colorGamut);
@@ -145,7 +152,8 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX) {
+ if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX
+ || hdr_tf == ULTRAHDR_TF_SRGB) {
ALOGE("Invalid hdr transfer function %d", hdr_tf);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
@@ -228,13 +236,8 @@
metadata.version = kJpegrVersion;
jpegr_uncompressed_struct uncompressed_yuv_420_image;
- size_t gain_map_length = uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2;
- // Pad a pseudo chroma block (kJpegBlock / 2) x (kJpegBlock / 2)
- // if width is not kJpegBlock aligned.
- if (uncompressed_p010_image->width % kJpegBlock != 0) {
- gain_map_length += kJpegBlockSquare / 4;
- }
- unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(gain_map_length);
+ unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
+ uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
@@ -255,6 +258,10 @@
sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
uncompressed_yuv_420_image.colorGamut);
+ // Convert to Bt601 YUV encoding for JPEG encode
+ JPEGR_CHECK(convertYuv(&uncompressed_yuv_420_image, uncompressed_yuv_420_image.colorGamut,
+ ULTRAHDR_COLORGAMUT_P3));
+
JpegEncoderHelper jpeg_encoder;
if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
uncompressed_yuv_420_image.width,
@@ -266,7 +273,9 @@
jpeg.data = jpeg_encoder.getCompressedImagePtr();
jpeg.length = jpeg_encoder.getCompressedImageSize();
- JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, &metadata, dest));
+ // No ICC since JPEG encode already did it
+ JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
+ &metadata, dest));
return NO_ERROR;
}
@@ -314,10 +323,22 @@
sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
uncompressed_yuv_420_image->colorGamut);
+ // Convert to Bt601 YUV encoding for JPEG encode; make a copy so as to no clobber client data
+ unique_ptr<uint8_t[]> yuv_420_bt601_data = make_unique<uint8_t[]>(
+ uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 3 / 2);
+ memcpy(yuv_420_bt601_data.get(), uncompressed_yuv_420_image->data,
+ uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 3 / 2);
+
+ jpegr_uncompressed_struct yuv_420_bt601_image = {
+ yuv_420_bt601_data.get(), uncompressed_yuv_420_image->width, uncompressed_yuv_420_image->height,
+ uncompressed_yuv_420_image->colorGamut };
+ JPEGR_CHECK(convertYuv(&yuv_420_bt601_image, yuv_420_bt601_image.colorGamut,
+ ULTRAHDR_COLORGAMUT_P3));
+
JpegEncoderHelper jpeg_encoder;
- if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
- uncompressed_yuv_420_image->width,
- uncompressed_yuv_420_image->height, quality,
+ if (!jpeg_encoder.compressImage(yuv_420_bt601_image.data,
+ yuv_420_bt601_image.width,
+ yuv_420_bt601_image.height, quality,
icc->getData(), icc->getLength())) {
return ERROR_JPEGR_ENCODE_ERROR;
}
@@ -325,7 +346,9 @@
jpeg.data = jpeg_encoder.getCompressedImagePtr();
jpeg.length = jpeg_encoder.getCompressedImageSize();
- JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, &metadata, dest));
+ // No ICC since jpeg encode already did it
+ JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
+ &metadata, dest));
return NO_ERROR;
}
@@ -368,7 +391,24 @@
compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, nullptr, &metadata, dest));
+ // We just want to check if ICC is present, so don't do a full decode. Note,
+ // this doesn't verify that the ICC is valid.
+ JpegDecoderHelper decoder;
+ std::vector<uint8_t> icc;
+ decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
+ /* pWidth */ nullptr, /* pHeight */ nullptr,
+ &icc, /* exifData */ nullptr);
+
+ // Add ICC if not already present.
+ if (icc.size() > 0) {
+ JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
+ /* icc */ nullptr, /* icc size */ 0, &metadata, dest));
+ } else {
+ sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
+ uncompressed_yuv_420_image->colorGamut);
+ JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
+ newIcc->getData(), newIcc->getLength(), &metadata, dest));
+ }
return NO_ERROR;
}
@@ -389,6 +429,7 @@
return ret;
}
+ // Note: output is Bt.601 YUV encoded regardless of gamut, due to jpeg decode.
JpegDecoderHelper jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
return ERROR_JPEGR_DECODE_ERROR;
@@ -408,8 +449,10 @@
metadata.version = kJpegrVersion;
jpegr_uncompressed_struct map;
+ // Indicate that the SDR image is Bt.601 YUV encoded.
JPEGR_CHECK(generateGainMap(
- &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
+ &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map,
+ true /* sdr_is_601 */ ));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
@@ -421,7 +464,24 @@
compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, nullptr, &metadata, dest));
+ // We just want to check if ICC is present, so don't do a full decode. Note,
+ // this doesn't verify that the ICC is valid.
+ JpegDecoderHelper decoder;
+ std::vector<uint8_t> icc;
+ decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
+ /* pWidth */ nullptr, /* pHeight */ nullptr,
+ &icc, /* exifData */ nullptr);
+
+ // Add ICC if not already present.
+ if (icc.size() > 0) {
+ JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
+ /* icc */ nullptr, /* icc size */ 0, &metadata, dest));
+ } else {
+ sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
+ uncompressed_yuv_420_image.colorGamut);
+ JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
+ newIcc->getData(), newIcc->getLength(), &metadata, dest));
+ }
return NO_ERROR;
}
@@ -446,8 +506,25 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, compressed_gainmap, /* exif */ nullptr,
- metadata, dest));
+ // We just want to check if ICC is present, so don't do a full decode. Note,
+ // this doesn't verify that the ICC is valid.
+ JpegDecoderHelper decoder;
+ std::vector<uint8_t> icc;
+ decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
+ /* pWidth */ nullptr, /* pHeight */ nullptr,
+ &icc, /* exifData */ nullptr);
+
+ // Add ICC if not already present.
+ if (icc.size() > 0) {
+ JPEGR_CHECK(appendGainMap(compressed_jpeg_image, compressed_gainmap, /* exif */ nullptr,
+ /* icc */ nullptr, /* icc size */ 0, metadata, dest));
+ } else {
+ sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
+ compressed_jpeg_image->colorGamut);
+ JPEGR_CHECK(appendGainMap(compressed_jpeg_image, compressed_gainmap, /* exif */ nullptr,
+ newIcc->getData(), newIcc->getLength(), metadata, dest));
+ }
+
return NO_ERROR;
}
@@ -462,9 +539,12 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- jpegr_compressed_struct primary_image, gain_map;
- JPEGR_CHECK(extractPrimaryImageAndGainMap(compressed_jpegr_image,
- &primary_image, &gain_map));
+ jpegr_compressed_struct primary_image, gainmap_image;
+ status_t status =
+ extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image);
+ if (status != NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
+ return status;
+ }
JpegDecoderHelper jpeg_decoder;
if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
@@ -473,7 +553,7 @@
return ERROR_JPEGR_DECODE_ERROR;
}
- return NO_ERROR;
+ return status;
}
/* Decode API */
@@ -509,82 +589,36 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- if (gain_map != nullptr && gain_map->data == nullptr) {
- ALOGE("received nullptr address for gain map data");
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
-
- if (output_format == ULTRAHDR_OUTPUT_SDR) {
- JpegDecoderHelper jpeg_decoder;
- if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
- true)) {
- return ERROR_JPEGR_DECODE_ERROR;
+ jpegr_compressed_struct primary_image, gainmap_image;
+ status_t status =
+ extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image);
+ if (status != NO_ERROR) {
+ if (output_format != ULTRAHDR_OUTPUT_SDR || status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
+ ALOGE("received invalid compressed jpegr image");
+ return status;
}
- jpegr_uncompressed_struct uncompressed_rgba_image;
- uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr();
- uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth();
- uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight();
- memcpy(dest->data, uncompressed_rgba_image.data,
- uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
- dest->width = uncompressed_rgba_image.width;
- dest->height = uncompressed_rgba_image.height;
-
- if (gain_map == nullptr && exif == nullptr) {
- return NO_ERROR;
- }
-
- if (exif != nullptr) {
- if (exif->data == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (exif->length < jpeg_decoder.getEXIFSize()) {
- return ERROR_JPEGR_BUFFER_TOO_SMALL;
- }
- memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize());
- exif->length = jpeg_decoder.getEXIFSize();
- }
- if (gain_map == nullptr) {
- return NO_ERROR;
- }
- }
-
- jpegr_compressed_struct compressed_map;
- JPEGR_CHECK(extractGainMap(compressed_jpegr_image, &compressed_map));
-
- JpegDecoderHelper gain_map_decoder;
- if (!gain_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
-
- if (gain_map != nullptr) {
- gain_map->width = gain_map_decoder.getDecompressedImageWidth();
- gain_map->height = gain_map_decoder.getDecompressedImageHeight();
- int size = gain_map->width * gain_map->height;
- gain_map->data = malloc(size);
- memcpy(gain_map->data, gain_map_decoder.getDecompressedImagePtr(), size);
- }
-
- ultrahdr_metadata_struct uhdr_metadata;
- if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
- gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
-
- if (metadata != nullptr) {
- metadata->version = uhdr_metadata.version;
- metadata->minContentBoost = uhdr_metadata.minContentBoost;
- metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
- }
-
- if (output_format == ULTRAHDR_OUTPUT_SDR) {
- return NO_ERROR;
}
JpegDecoderHelper jpeg_decoder;
- if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
+ if (!jpeg_decoder.decompressImage(primary_image.data, primary_image.length,
+ (output_format == ULTRAHDR_OUTPUT_SDR))) {
return ERROR_JPEGR_DECODE_ERROR;
}
+ if (output_format == ULTRAHDR_OUTPUT_SDR) {
+ if ((jpeg_decoder.getDecompressedImageWidth() *
+ jpeg_decoder.getDecompressedImageHeight() * 4) >
+ jpeg_decoder.getDecompressedImageSize()) {
+ return ERROR_JPEGR_CALCULATION_ERROR;
+ }
+ } else {
+ if ((jpeg_decoder.getDecompressedImageWidth() *
+ jpeg_decoder.getDecompressedImageHeight() * 3 / 2) >
+ jpeg_decoder.getDecompressedImageSize()) {
+ return ERROR_JPEGR_CALCULATION_ERROR;
+ }
+ }
+
if (exif != nullptr) {
if (exif->data == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -596,15 +630,59 @@
exif->length = jpeg_decoder.getEXIFSize();
}
+ if (output_format == ULTRAHDR_OUTPUT_SDR) {
+ dest->width = jpeg_decoder.getDecompressedImageWidth();
+ dest->height = jpeg_decoder.getDecompressedImageHeight();
+ memcpy(dest->data, jpeg_decoder.getDecompressedImagePtr(), dest->width * dest->height * 4);
+ return NO_ERROR;
+ }
+
+ JpegDecoderHelper gain_map_decoder;
+ if (!gain_map_decoder.decompressImage(gainmap_image.data, gainmap_image.length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+ if ((gain_map_decoder.getDecompressedImageWidth() *
+ gain_map_decoder.getDecompressedImageHeight()) >
+ gain_map_decoder.getDecompressedImageSize()) {
+ return ERROR_JPEGR_CALCULATION_ERROR;
+ }
+
jpegr_uncompressed_struct map;
map.data = gain_map_decoder.getDecompressedImagePtr();
map.width = gain_map_decoder.getDecompressedImageWidth();
map.height = gain_map_decoder.getDecompressedImageHeight();
+ if (gain_map != nullptr) {
+ gain_map->width = map.width;
+ gain_map->height = map.height;
+ int size = gain_map->width * gain_map->height;
+ gain_map->data = malloc(size);
+ memcpy(gain_map->data, map.data, size);
+ }
+
+ ultrahdr_metadata_struct uhdr_metadata;
+ if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
+ gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
+ return ERROR_JPEGR_INVALID_METADATA;
+ }
+
+ if (metadata != nullptr) {
+ metadata->version = uhdr_metadata.version;
+ metadata->minContentBoost = uhdr_metadata.minContentBoost;
+ metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
+ metadata->gamma = uhdr_metadata.gamma;
+ metadata->offsetSdr = uhdr_metadata.offsetSdr;
+ metadata->offsetHdr = uhdr_metadata.offsetHdr;
+ metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin;
+ metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax;
+ }
+
jpegr_uncompressed_struct uncompressed_yuv_420_image;
uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
+ uncompressed_yuv_420_image.colorGamut = IccHelper::readIccColorGamut(
+ jpeg_decoder.getICCPtr(), jpeg_decoder.getICCSize());
JPEGR_CHECK(applyGainMap(&uncompressed_yuv_420_image, &map, &uhdr_metadata, output_format,
max_display_boost, dest));
@@ -617,6 +695,7 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
+ // Don't need to convert YUV to Bt601 since single channel
if (!jpeg_encoder->compressImage(uncompressed_gain_map->data,
uncompressed_gain_map->width,
uncompressed_gain_map->height,
@@ -692,7 +771,8 @@
jr_uncompressed_ptr uncompressed_p010_image,
ultrahdr_transfer_function hdr_tf,
ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr dest) {
+ jr_uncompressed_ptr dest,
+ bool sdr_is_601) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_p010_image == nullptr
|| metadata == nullptr
@@ -726,7 +806,7 @@
map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
ColorTransformFn hdrInvOetf = nullptr;
- float hdr_white_nits = 0.0f;
+ float hdr_white_nits = kSdrWhiteNits;
switch (hdr_tf) {
case ULTRAHDR_TF_LINEAR:
hdrInvOetf = identityConversion;
@@ -754,6 +834,12 @@
metadata->maxContentBoost = hdr_white_nits / kSdrWhiteNits;
metadata->minContentBoost = 1.0f;
+ metadata->gamma = 1.0f;
+ metadata->offsetSdr = 0.0f;
+ metadata->offsetHdr = 0.0f;
+ metadata->hdrCapacityMin = 1.0f;
+ metadata->hdrCapacityMax = metadata->maxContentBoost;
+
float log2MinBoost = log2(metadata->minContentBoost);
float log2MaxBoost = log2(metadata->maxContentBoost);
@@ -761,15 +847,38 @@
uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
ColorCalculationFn luminanceFn = nullptr;
+ ColorTransformFn sdrYuvToRgbFn = nullptr;
switch (uncompressed_yuv_420_image->colorGamut) {
case ULTRAHDR_COLORGAMUT_BT709:
luminanceFn = srgbLuminance;
+ sdrYuvToRgbFn = srgbYuvToRgb;
break;
case ULTRAHDR_COLORGAMUT_P3:
luminanceFn = p3Luminance;
+ sdrYuvToRgbFn = p3YuvToRgb;
break;
case ULTRAHDR_COLORGAMUT_BT2100:
luminanceFn = bt2100Luminance;
+ sdrYuvToRgbFn = bt2100YuvToRgb;
+ break;
+ case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
+ // Should be impossible to hit after input validation.
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+ if (sdr_is_601) {
+ sdrYuvToRgbFn = p3YuvToRgb;
+ }
+
+ ColorTransformFn hdrYuvToRgbFn = nullptr;
+ switch (uncompressed_p010_image->colorGamut) {
+ case ULTRAHDR_COLORGAMUT_BT709:
+ hdrYuvToRgbFn = srgbYuvToRgb;
+ break;
+ case ULTRAHDR_COLORGAMUT_P3:
+ hdrYuvToRgbFn = p3YuvToRgb;
+ break;
+ case ULTRAHDR_COLORGAMUT_BT2100:
+ hdrYuvToRgbFn = bt2100YuvToRgb;
break;
case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
// Should be impossible to hit after input validation.
@@ -783,8 +892,8 @@
std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image,
metadata, dest, hdrInvOetf, hdrGamutConversionFn,
- luminanceFn, hdr_white_nits, log2MinBoost, log2MaxBoost,
- &jobQueue]() -> void {
+ luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, hdr_white_nits,
+ log2MinBoost, log2MaxBoost, &jobQueue]() -> void {
size_t rowStart, rowEnd;
size_t dest_map_width = uncompressed_yuv_420_image->width / kMapDimensionScaleFactor;
size_t dest_map_stride = dest->width;
@@ -793,7 +902,8 @@
for (size_t x = 0; x < dest_map_width; ++x) {
Color sdr_yuv_gamma =
sampleYuv420(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y);
- Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
+ Color sdr_rgb_gamma = sdrYuvToRgbFn(sdr_yuv_gamma);
+ // We are assuming the SDR input is always sRGB transfer.
#if USE_SRGB_INVOETF_LUT
Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma);
#else
@@ -802,7 +912,7 @@
float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
- Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
+ Color hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
hdr_rgb = hdrGamutConversionFn(hdr_rgb);
float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
@@ -848,6 +958,40 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
+ if (metadata->version.compare("1.0")) {
+ ALOGE("Unsupported metadata version: %s", metadata->version.c_str());
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ }
+ if (metadata->gamma != 1.0f) {
+ ALOGE("Unsupported metadata gamma: %f", metadata->gamma);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ }
+ if (metadata->offsetSdr != 0.0f || metadata->offsetHdr != 0.0f) {
+ ALOGE("Unsupported metadata offset sdr, hdr: %f, %f", metadata->offsetSdr,
+ metadata->offsetHdr);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ }
+ if (metadata->hdrCapacityMin != metadata->minContentBoost
+ || metadata->hdrCapacityMax != metadata->maxContentBoost) {
+ ALOGE("Unsupported metadata hdr capacity min, max: %f, %f", metadata->hdrCapacityMin,
+ metadata->hdrCapacityMax);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ }
+
+ // TODO: remove once map scaling factor is computed based on actual map dims
+ size_t image_width = uncompressed_yuv_420_image->width;
+ size_t image_height = uncompressed_yuv_420_image->height;
+ size_t map_width = image_width / kMapDimensionScaleFactor;
+ size_t map_height = image_height / kMapDimensionScaleFactor;
+ map_width = static_cast<size_t>(
+ floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock;
+ map_height = ((map_height + 1) >> 1) << 1;
+ if (map_width != uncompressed_gain_map->width
+ || map_height != uncompressed_gain_map->height) {
+ ALOGE("gain map dimensions and primary image dimensions are not to scale");
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
dest->width = uncompressed_yuv_420_image->width;
dest->height = uncompressed_yuv_420_image->height;
ShepardsIDW idwTable(kMapDimensionScaleFactor);
@@ -866,7 +1010,9 @@
for (size_t y = rowStart; y < rowEnd; ++y) {
for (size_t x = 0; x < width; ++x) {
Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
- Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
+ // Assuming the sdr image is a decoded JPEG, we should always use Rec.601 YUV coefficients
+ Color rgb_gamma_sdr = p3YuvToRgb(yuv_gamma_sdr);
+ // We are assuming the SDR base image is always sRGB transfer.
#if USE_SRGB_INVOETF_LUT
Color rgb_sdr = srgbInvOetfLUT(rgb_gamma_sdr);
#else
@@ -913,7 +1059,7 @@
}
case ULTRAHDR_OUTPUT_HDR_PQ:
{
-#if USE_HLG_OETF_LUT
+#if USE_PQ_OETF_LUT
ColorTransformFn hdrOetf = pqOetfLUT;
#else
ColorTransformFn hdrOetf = pqOetf;
@@ -974,12 +1120,8 @@
const auto& jpeg_info = jpeg_info_builder.GetInfo();
const auto& image_ranges = jpeg_info.GetImageRanges();
- if (image_ranges.empty()) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (image_ranges.size() != 2) {
- // Must be 2 JPEG Images
+ if (image_ranges.empty()) {
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
@@ -989,23 +1131,23 @@
primary_image->length = image_ranges[0].GetLength();
}
+ if (image_ranges.size() == 1) {
+ return ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND;
+ }
+
if (gain_map != nullptr) {
gain_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
image_ranges[1].GetBegin();
gain_map->length = image_ranges[1].GetLength();
}
- return NO_ERROR;
-}
-
-
-status_t JpegR::extractGainMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr dest) {
- if (compressed_jpegr_image == nullptr || dest == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ // TODO: choose primary image and gain map image carefully
+ if (image_ranges.size() > 2) {
+ ALOGW("Number of jpeg images present %d, primary, gain map images may not be correctly chosen",
+ (int)image_ranges.size());
}
- return extractPrimaryImageAndGainMap(compressed_jpegr_image, nullptr, dest);
+ return NO_ERROR;
}
// JPEG/R structure:
@@ -1044,6 +1186,7 @@
status_t JpegR::appendGainMap(jr_compressed_ptr compressed_jpeg_image,
jr_compressed_ptr compressed_gain_map,
jr_exif_ptr exif,
+ void* icc, size_t icc_size,
ultrahdr_metadata_ptr metadata,
jr_compressed_ptr dest) {
if (compressed_jpeg_image == nullptr
@@ -1053,6 +1196,33 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
+ if (metadata->version.compare("1.0")) {
+ ALOGE("received bad value for version: %s", metadata->version.c_str());
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+ if (metadata->maxContentBoost < metadata->minContentBoost) {
+ ALOGE("received bad value for content boost min %f, max %f", metadata->minContentBoost,
+ metadata->maxContentBoost);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (metadata->hdrCapacityMax < metadata->hdrCapacityMin || metadata->hdrCapacityMin < 1.0f) {
+ ALOGE("received bad value for hdr capacity min %f, max %f", metadata->hdrCapacityMin,
+ metadata->hdrCapacityMax);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (metadata->offsetSdr < 0.0f || metadata->offsetHdr < 0.0f) {
+ ALOGE("received bad value for offset sdr %f, hdr %f", metadata->offsetSdr,
+ metadata->offsetHdr);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (metadata->gamma <= 0.0f) {
+ ALOGE("received bad value for gamma %f", metadata->gamma);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
const string nameSpace = "http://ns.adobe.com/xap/1.0/";
const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
@@ -1101,6 +1271,18 @@
JPEGR_CHECK(Write(dest, (void*)xmp_primary.c_str(), xmp_primary.size(), pos));
}
+ // Write ICC
+ if (icc != nullptr && icc_size > 0) {
+ const int length = icc_size + 2;
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, icc, icc_size, pos));
+ }
+
// Prepare and write MPF
{
const int length = 2 + calculateMpfSize();
@@ -1208,4 +1390,82 @@
return NO_ERROR;
}
+status_t JpegR::convertYuv(jr_uncompressed_ptr image,
+ ultrahdr_color_gamut src_encoding,
+ ultrahdr_color_gamut dest_encoding) {
+ if (image == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (src_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED
+ || dest_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
+ ColorTransformFn conversionFn = nullptr;
+ switch (src_encoding) {
+ case ULTRAHDR_COLORGAMUT_BT709:
+ switch (dest_encoding) {
+ case ULTRAHDR_COLORGAMUT_BT709:
+ return NO_ERROR;
+ case ULTRAHDR_COLORGAMUT_P3:
+ conversionFn = yuv709To601;
+ break;
+ case ULTRAHDR_COLORGAMUT_BT2100:
+ conversionFn = yuv709To2100;
+ break;
+ default:
+ // Should be impossible to hit after input validation
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+ break;
+ case ULTRAHDR_COLORGAMUT_P3:
+ switch (dest_encoding) {
+ case ULTRAHDR_COLORGAMUT_BT709:
+ conversionFn = yuv601To709;
+ break;
+ case ULTRAHDR_COLORGAMUT_P3:
+ return NO_ERROR;
+ case ULTRAHDR_COLORGAMUT_BT2100:
+ conversionFn = yuv601To2100;
+ break;
+ default:
+ // Should be impossible to hit after input validation
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+ break;
+ case ULTRAHDR_COLORGAMUT_BT2100:
+ switch (dest_encoding) {
+ case ULTRAHDR_COLORGAMUT_BT709:
+ conversionFn = yuv2100To709;
+ break;
+ case ULTRAHDR_COLORGAMUT_P3:
+ conversionFn = yuv2100To601;
+ break;
+ case ULTRAHDR_COLORGAMUT_BT2100:
+ return NO_ERROR;
+ default:
+ // Should be impossible to hit after input validation
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+ break;
+ default:
+ // Should be impossible to hit after input validation
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
+ if (conversionFn == nullptr) {
+ // Should be impossible to hit after input validation
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
+ for (size_t y = 0; y < image->height / 2; ++y) {
+ for (size_t x = 0; x < image->width / 2; ++x) {
+ transformYuv420(image, x, y, conversionFn);
+ }
+ }
+
+ return NO_ERROR;
+}
+
} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/jpegrutils.cpp b/libs/ultrahdr/jpegrutils.cpp
index 6430af1..c434eb6 100644
--- a/libs/ultrahdr/jpegrutils.cpp
+++ b/libs/ultrahdr/jpegrutils.cpp
@@ -113,6 +113,15 @@
XMPXmlHandler() : XmlHandler() {
state = NotStrarted;
+ versionFound = false;
+ minContentBoostFound = false;
+ maxContentBoostFound = false;
+ gammaFound = false;
+ offsetSdrFound = false;
+ offsetHdrFound = false;
+ hdrCapacityMinFound = false;
+ hdrCapacityMaxFound = false;
+ baseRenditionIsHdrFound = false;
}
enum ParseState {
@@ -147,10 +156,24 @@
string val;
if (state == Started) {
if (context.BuildTokenValue(&val)) {
- if (!val.compare(maxContentBoostAttrName)) {
+ if (!val.compare(versionAttrName)) {
+ lastAttributeName = versionAttrName;
+ } else if (!val.compare(maxContentBoostAttrName)) {
lastAttributeName = maxContentBoostAttrName;
} else if (!val.compare(minContentBoostAttrName)) {
lastAttributeName = minContentBoostAttrName;
+ } else if (!val.compare(gammaAttrName)) {
+ lastAttributeName = gammaAttrName;
+ } else if (!val.compare(offsetSdrAttrName)) {
+ lastAttributeName = offsetSdrAttrName;
+ } else if (!val.compare(offsetHdrAttrName)) {
+ lastAttributeName = offsetHdrAttrName;
+ } else if (!val.compare(hdrCapacityMinAttrName)) {
+ lastAttributeName = hdrCapacityMinAttrName;
+ } else if (!val.compare(hdrCapacityMaxAttrName)) {
+ lastAttributeName = hdrCapacityMaxAttrName;
+ } else if (!val.compare(baseRenditionIsHdrAttrName)) {
+ lastAttributeName = baseRenditionIsHdrAttrName;
} else {
lastAttributeName = "";
}
@@ -163,18 +186,52 @@
string val;
if (state == Started) {
if (context.BuildTokenValue(&val, true)) {
- if (!lastAttributeName.compare(maxContentBoostAttrName)) {
+ if (!lastAttributeName.compare(versionAttrName)) {
+ versionStr = val;
+ versionFound = true;
+ } else if (!lastAttributeName.compare(maxContentBoostAttrName)) {
maxContentBoostStr = val;
+ maxContentBoostFound = true;
} else if (!lastAttributeName.compare(minContentBoostAttrName)) {
minContentBoostStr = val;
+ minContentBoostFound = true;
+ } else if (!lastAttributeName.compare(gammaAttrName)) {
+ gammaStr = val;
+ gammaFound = true;
+ } else if (!lastAttributeName.compare(offsetSdrAttrName)) {
+ offsetSdrStr = val;
+ offsetSdrFound = true;
+ } else if (!lastAttributeName.compare(offsetHdrAttrName)) {
+ offsetHdrStr = val;
+ offsetHdrFound = true;
+ } else if (!lastAttributeName.compare(hdrCapacityMinAttrName)) {
+ hdrCapacityMinStr = val;
+ hdrCapacityMinFound = true;
+ } else if (!lastAttributeName.compare(hdrCapacityMaxAttrName)) {
+ hdrCapacityMaxStr = val;
+ hdrCapacityMaxFound = true;
+ } else if (!lastAttributeName.compare(baseRenditionIsHdrAttrName)) {
+ baseRenditionIsHdrStr = val;
+ baseRenditionIsHdrFound = true;
}
}
}
return context.GetResult();
}
- bool getMaxContentBoost(float* max_content_boost) {
+ bool getVersion(string* version, bool* present) {
if (state == Done) {
+ *version = versionStr;
+ *present = versionFound;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool getMaxContentBoost(float* max_content_boost, bool* present) {
+ if (state == Done) {
+ *present = maxContentBoostFound;
stringstream ss(maxContentBoostStr);
float val;
if (ss >> val) {
@@ -188,8 +245,9 @@
}
}
- bool getMinContentBoost(float* min_content_boost) {
+ bool getMinContentBoost(float* min_content_boost, bool* present) {
if (state == Done) {
+ *present = minContentBoostFound;
stringstream ss(minContentBoostStr);
float val;
if (ss >> val) {
@@ -203,12 +261,141 @@
}
}
+ bool getGamma(float* gamma, bool* present) {
+ if (state == Done) {
+ *present = gammaFound;
+ stringstream ss(gammaStr);
+ float val;
+ if (ss >> val) {
+ *gamma = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getOffsetSdr(float* offset_sdr, bool* present) {
+ if (state == Done) {
+ *present = offsetSdrFound;
+ stringstream ss(offsetSdrStr);
+ float val;
+ if (ss >> val) {
+ *offset_sdr = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getOffsetHdr(float* offset_hdr, bool* present) {
+ if (state == Done) {
+ *present = offsetHdrFound;
+ stringstream ss(offsetHdrStr);
+ float val;
+ if (ss >> val) {
+ *offset_hdr = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getHdrCapacityMin(float* hdr_capacity_min, bool* present) {
+ if (state == Done) {
+ *present = hdrCapacityMinFound;
+ stringstream ss(hdrCapacityMinStr);
+ float val;
+ if (ss >> val) {
+ *hdr_capacity_min = exp2(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getHdrCapacityMax(float* hdr_capacity_max, bool* present) {
+ if (state == Done) {
+ *present = hdrCapacityMaxFound;
+ stringstream ss(hdrCapacityMaxStr);
+ float val;
+ if (ss >> val) {
+ *hdr_capacity_max = exp2(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getBaseRenditionIsHdr(bool* base_rendition_is_hdr, bool* present) {
+ if (state == Done) {
+ *present = baseRenditionIsHdrFound;
+ if (!baseRenditionIsHdrStr.compare("False")) {
+ *base_rendition_is_hdr = false;
+ return true;
+ } else if (!baseRenditionIsHdrStr.compare("True")) {
+ *base_rendition_is_hdr = true;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+
private:
static const string containerName;
+
+ static const string versionAttrName;
+ string versionStr;
+ bool versionFound;
static const string maxContentBoostAttrName;
string maxContentBoostStr;
+ bool maxContentBoostFound;
static const string minContentBoostAttrName;
string minContentBoostStr;
+ bool minContentBoostFound;
+ static const string gammaAttrName;
+ string gammaStr;
+ bool gammaFound;
+ static const string offsetSdrAttrName;
+ string offsetSdrStr;
+ bool offsetSdrFound;
+ static const string offsetHdrAttrName;
+ string offsetHdrStr;
+ bool offsetHdrFound;
+ static const string hdrCapacityMinAttrName;
+ string hdrCapacityMinStr;
+ bool hdrCapacityMinFound;
+ static const string hdrCapacityMaxAttrName;
+ string hdrCapacityMaxStr;
+ bool hdrCapacityMaxFound;
+ static const string baseRenditionIsHdrAttrName;
+ string baseRenditionIsHdrStr;
+ bool baseRenditionIsHdrFound;
+
string lastAttributeName;
ParseState state;
};
@@ -253,8 +440,15 @@
const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR");
// GainMap XMP constants - names for XMP handlers
+const string XMPXmlHandler::versionAttrName = kMapVersion;
const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
+const string XMPXmlHandler::gammaAttrName = kMapGamma;
+const string XMPXmlHandler::offsetSdrAttrName = kMapOffsetSdr;
+const string XMPXmlHandler::offsetHdrAttrName = kMapOffsetHdr;
+const string XMPXmlHandler::hdrCapacityMinAttrName = kMapHDRCapacityMin;
+const string XMPXmlHandler::hdrCapacityMaxAttrName = kMapHDRCapacityMax;
+const string XMPXmlHandler::baseRenditionIsHdrAttrName = kMapBaseRenditionIsHDR;
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata) {
string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -291,11 +485,48 @@
return false;
}
- if (!handler.getMaxContentBoost(&metadata->maxContentBoost)) {
+ // Apply default values to any not-present fields, except for Version,
+ // maxContentBoost, and hdrCapacityMax, which are required. Return false if
+ // we encounter a present field that couldn't be parsed, since this
+ // indicates it is invalid (eg. string where there should be a float).
+ bool present = false;
+ if (!handler.getVersion(&metadata->version, &present) || !present) {
return false;
}
+ if (!handler.getMaxContentBoost(&metadata->maxContentBoost, &present) || !present) {
+ return false;
+ }
+ if (!handler.getHdrCapacityMax(&metadata->hdrCapacityMax, &present) || !present) {
+ return false;
+ }
+ if (!handler.getMinContentBoost(&metadata->minContentBoost, &present)) {
+ if (present) return false;
+ metadata->minContentBoost = 1.0f;
+ }
+ if (!handler.getGamma(&metadata->gamma, &present)) {
+ if (present) return false;
+ metadata->gamma = 1.0f;
+ }
+ if (!handler.getOffsetSdr(&metadata->offsetSdr, &present)) {
+ if (present) return false;
+ metadata->offsetSdr = 1.0f / 64.0f;
+ }
+ if (!handler.getOffsetHdr(&metadata->offsetHdr, &present)) {
+ if (present) return false;
+ metadata->offsetHdr = 1.0f / 64.0f;
+ }
+ if (!handler.getHdrCapacityMin(&metadata->hdrCapacityMin, &present)) {
+ if (present) return false;
+ metadata->hdrCapacityMin = 1.0f;
+ }
- if (!handler.getMinContentBoost(&metadata->minContentBoost)) {
+ bool base_rendition_is_hdr;
+ if (!handler.getBaseRenditionIsHdr(&base_rendition_is_hdr, &present)) {
+ if (present) return false;
+ base_rendition_is_hdr = false;
+ }
+ if (base_rendition_is_hdr) {
+ ALOGE("Base rendition of HDR is not supported!");
return false;
}
@@ -355,12 +586,11 @@
writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
- writer.WriteAttributeNameAndValue(kMapGamma, "1");
- writer.WriteAttributeNameAndValue(kMapOffsetSdr, "0");
- writer.WriteAttributeNameAndValue(kMapOffsetHdr, "0");
- writer.WriteAttributeNameAndValue(
- kMapHDRCapacityMin, std::max(log2(metadata.minContentBoost), 0.0f));
- writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.maxContentBoost));
+ writer.WriteAttributeNameAndValue(kMapGamma, metadata.gamma);
+ writer.WriteAttributeNameAndValue(kMapOffsetSdr, metadata.offsetSdr);
+ writer.WriteAttributeNameAndValue(kMapOffsetHdr, metadata.offsetHdr);
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, log2(metadata.hdrCapacityMin));
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.hdrCapacityMax));
writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False");
writer.FinishWriting();
diff --git a/libs/ultrahdr/multipictureformat.cpp b/libs/ultrahdr/multipictureformat.cpp
index 7a265c6..f1679ef 100644
--- a/libs/ultrahdr/multipictureformat.cpp
+++ b/libs/ultrahdr/multipictureformat.cpp
@@ -30,7 +30,7 @@
sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
int secondary_image_size, int secondary_image_offset) {
size_t mpf_size = calculateMpfSize();
- sp<DataStruct> dataStruct = new DataStruct(mpf_size);
+ sp<DataStruct> dataStruct = sp<DataStruct>::make(mpf_size);
dataStruct->write(static_cast<const void*>(kMpfSig), sizeof(kMpfSig));
#if USE_BIG_ENDIAN
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
index 7dd9d04..004a582 100644
--- a/libs/ultrahdr/tests/Android.bp
+++ b/libs/ultrahdr/tests/Android.bp
@@ -22,11 +22,12 @@
}
cc_test {
- name: "libultrahdr_test",
+ name: "ultrahdr_unit_test",
test_suites: ["device-tests"],
srcs: [
- "jpegr_test.cpp",
"gainmapmath_test.cpp",
+ "icchelper_test.cpp",
+ "jpegr_test.cpp",
],
shared_libs: [
"libimage_io",
@@ -44,7 +45,7 @@
}
cc_test {
- name: "libjpegencoderhelper_test",
+ name: "jpegencoderhelper_test",
test_suites: ["device-tests"],
srcs: [
"jpegencoderhelper_test.cpp",
@@ -60,7 +61,7 @@
}
cc_test {
- name: "libjpegdecoderhelper_test",
+ name: "jpegdecoderhelper_test",
test_suites: ["device-tests"],
srcs: [
"jpegdecoderhelper_test.cpp",
@@ -72,5 +73,7 @@
static_libs: [
"libgtest",
"libjpegdecoder",
+ "libultrahdr",
+ "libutils",
],
}
diff --git a/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg b/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg
new file mode 100644
index 0000000..c7f4538
--- /dev/null
+++ b/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg
Binary files differ
diff --git a/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010 b/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010
deleted file mode 100644
index e7a5dc8..0000000
--- a/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/gainmapmath_test.cpp b/libs/ultrahdr/tests/gainmapmath_test.cpp
index c456653..af90365 100644
--- a/libs/ultrahdr/tests/gainmapmath_test.cpp
+++ b/libs/ultrahdr/tests/gainmapmath_test.cpp
@@ -28,6 +28,7 @@
float ComparisonEpsilon() { return 1e-4f; }
float LuminanceEpsilon() { return 1e-2f; }
+ float YuvConversionEpsilon() { return 1.0f / (255.0f * 2.0f); }
Color Yuv420(uint8_t y, uint8_t u, uint8_t v) {
return {{{ static_cast<float>(y) / 255.0f,
@@ -63,9 +64,13 @@
Color YuvBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
Color YuvWhite() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
- Color SrgbYuvRed() { return {{{ 0.299f, -0.1687f, 0.5f }}}; }
- Color SrgbYuvGreen() { return {{{ 0.587f, -0.3313f, -0.4187f }}}; }
- Color SrgbYuvBlue() { return {{{ 0.114f, 0.5f, -0.0813f }}}; }
+ Color SrgbYuvRed() { return {{{ 0.2126f, -0.11457f, 0.5f }}}; }
+ Color SrgbYuvGreen() { return {{{ 0.7152f, -0.38543f, -0.45415f }}}; }
+ Color SrgbYuvBlue() { return {{{ 0.0722f, 0.5f, -0.04585f }}}; }
+
+ Color P3YuvRed() { return {{{ 0.299f, -0.16874f, 0.5f }}}; }
+ Color P3YuvGreen() { return {{{ 0.587f, -0.33126f, -0.41869f }}}; }
+ Color P3YuvBlue() { return {{{ 0.114f, 0.5f, -0.08131f }}}; }
Color Bt2100YuvRed() { return {{{ 0.2627f, -0.13963f, 0.5f }}}; }
Color Bt2100YuvGreen() { return {{{ 0.6780f, -0.36037f, -0.45979f }}}; }
@@ -78,6 +83,13 @@
return luminance_scaled * kSdrWhiteNits;
}
+ float P3YuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
+ Color rgb_gamma = p3YuvToRgb(yuv_gamma);
+ Color rgb = srgbInvOetf(rgb_gamma);
+ float luminance_scaled = luminanceFn(rgb);
+ return luminance_scaled * kSdrWhiteNits;
+ }
+
float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf,
ColorTransformFn gamutConversionFn, ColorCalculationFn luminanceFn,
float scale_factor) {
@@ -402,6 +414,56 @@
EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f);
}
+TEST_F(GainMapMathTest, P3YuvToRgb) {
+ Color rgb_black = p3YuvToRgb(YuvBlack());
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = p3YuvToRgb(YuvWhite());
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = p3YuvToRgb(P3YuvRed());
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = p3YuvToRgb(P3YuvGreen());
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = p3YuvToRgb(P3YuvBlue());
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(GainMapMathTest, P3RgbToYuv) {
+ Color yuv_black = p3RgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv_black, YuvBlack());
+
+ Color yuv_white = p3RgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv_white, YuvWhite());
+
+ Color yuv_r = p3RgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv_r, P3YuvRed());
+
+ Color yuv_g = p3RgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv_g, P3YuvGreen());
+
+ Color yuv_b = p3RgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv_b, P3YuvBlue());
+}
+
+TEST_F(GainMapMathTest, P3RgbYuvRoundtrip) {
+ Color rgb_black = p3YuvToRgb(p3RgbToYuv(RgbBlack()));
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = p3YuvToRgb(p3RgbToYuv(RgbWhite()));
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = p3YuvToRgb(p3RgbToYuv(RgbRed()));
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = p3YuvToRgb(p3RgbToYuv(RgbGreen()));
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = p3YuvToRgb(p3RgbToYuv(RgbBlue()));
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
TEST_F(GainMapMathTest, Bt2100Luminance) {
EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f);
EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f);
@@ -461,6 +523,163 @@
EXPECT_RGB_NEAR(rgb_b, RgbBlue());
}
+TEST_F(GainMapMathTest, Bt709ToBt601YuvConversion) {
+ Color yuv_black = srgbRgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv709To601(yuv_black), YuvBlack());
+
+ Color yuv_white = srgbRgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv709To601(yuv_white), YuvWhite());
+
+ Color yuv_r = srgbRgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv709To601(yuv_r), P3YuvRed());
+
+ Color yuv_g = srgbRgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv709To601(yuv_g), P3YuvGreen());
+
+ Color yuv_b = srgbRgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv709To601(yuv_b), P3YuvBlue());
+}
+
+TEST_F(GainMapMathTest, Bt709ToBt2100YuvConversion) {
+ Color yuv_black = srgbRgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv709To2100(yuv_black), YuvBlack());
+
+ Color yuv_white = srgbRgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv709To2100(yuv_white), YuvWhite());
+
+ Color yuv_r = srgbRgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv709To2100(yuv_r), Bt2100YuvRed());
+
+ Color yuv_g = srgbRgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv709To2100(yuv_g), Bt2100YuvGreen());
+
+ Color yuv_b = srgbRgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv709To2100(yuv_b), Bt2100YuvBlue());
+}
+
+TEST_F(GainMapMathTest, Bt601ToBt709YuvConversion) {
+ Color yuv_black = p3RgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv601To709(yuv_black), YuvBlack());
+
+ Color yuv_white = p3RgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv601To709(yuv_white), YuvWhite());
+
+ Color yuv_r = p3RgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv601To709(yuv_r), SrgbYuvRed());
+
+ Color yuv_g = p3RgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv601To709(yuv_g), SrgbYuvGreen());
+
+ Color yuv_b = p3RgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv601To709(yuv_b), SrgbYuvBlue());
+}
+
+TEST_F(GainMapMathTest, Bt601ToBt2100YuvConversion) {
+ Color yuv_black = p3RgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv601To2100(yuv_black), YuvBlack());
+
+ Color yuv_white = p3RgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv601To2100(yuv_white), YuvWhite());
+
+ Color yuv_r = p3RgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv601To2100(yuv_r), Bt2100YuvRed());
+
+ Color yuv_g = p3RgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv601To2100(yuv_g), Bt2100YuvGreen());
+
+ Color yuv_b = p3RgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv601To2100(yuv_b), Bt2100YuvBlue());
+}
+
+TEST_F(GainMapMathTest, Bt2100ToBt709YuvConversion) {
+ Color yuv_black = bt2100RgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv2100To709(yuv_black), YuvBlack());
+
+ Color yuv_white = bt2100RgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv2100To709(yuv_white), YuvWhite());
+
+ Color yuv_r = bt2100RgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv2100To709(yuv_r), SrgbYuvRed());
+
+ Color yuv_g = bt2100RgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv2100To709(yuv_g), SrgbYuvGreen());
+
+ Color yuv_b = bt2100RgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv2100To709(yuv_b), SrgbYuvBlue());
+}
+
+TEST_F(GainMapMathTest, Bt2100ToBt601YuvConversion) {
+ Color yuv_black = bt2100RgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv2100To601(yuv_black), YuvBlack());
+
+ Color yuv_white = bt2100RgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv2100To601(yuv_white), YuvWhite());
+
+ Color yuv_r = bt2100RgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv2100To601(yuv_r), P3YuvRed());
+
+ Color yuv_g = bt2100RgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv2100To601(yuv_g), P3YuvGreen());
+
+ Color yuv_b = bt2100RgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv2100To601(yuv_b), P3YuvBlue());
+}
+
+TEST_F(GainMapMathTest, TransformYuv420) {
+ ColorTransformFn transforms[] = { yuv709To601, yuv709To2100, yuv601To709, yuv601To2100,
+ yuv2100To709, yuv2100To601 };
+ for (const ColorTransformFn& transform : transforms) {
+ jpegr_uncompressed_struct input = Yuv420Image();
+
+ size_t out_buf_size = input.width * input.height * 3 / 2;
+ std::unique_ptr<uint8_t[]> out_buf = std::make_unique<uint8_t[]>(out_buf_size);
+ memcpy(out_buf.get(), input.data, out_buf_size);
+ jpegr_uncompressed_struct output = Yuv420Image();
+ output.data = out_buf.get();
+
+ transformYuv420(&output, 1, 1, transform);
+
+ for (size_t y = 0; y < 4; ++y) {
+ for (size_t x = 0; x < 4; ++x) {
+ // Skip the last chroma sample, which we modified above
+ if (x >= 2 && y >= 2) {
+ continue;
+ }
+
+ // All other pixels should remain unchanged
+ EXPECT_YUV_EQ(getYuv420Pixel(&input, x, y), getYuv420Pixel(&output, x, y));
+ }
+ }
+
+ // modified pixels should be updated as intended by the transformYuv420 algorithm
+ Color in1 = getYuv420Pixel(&input, 2, 2);
+ Color in2 = getYuv420Pixel(&input, 3, 2);
+ Color in3 = getYuv420Pixel(&input, 2, 3);
+ Color in4 = getYuv420Pixel(&input, 3, 3);
+ Color out1 = getYuv420Pixel(&output, 2, 2);
+ Color out2 = getYuv420Pixel(&output, 3, 2);
+ Color out3 = getYuv420Pixel(&output, 2, 3);
+ Color out4 = getYuv420Pixel(&output, 3, 3);
+
+ EXPECT_NEAR(transform(in1).y, out1.y, YuvConversionEpsilon());
+ EXPECT_NEAR(transform(in2).y, out2.y, YuvConversionEpsilon());
+ EXPECT_NEAR(transform(in3).y, out3.y, YuvConversionEpsilon());
+ EXPECT_NEAR(transform(in4).y, out4.y, YuvConversionEpsilon());
+
+ Color expect_uv = (transform(in1) + transform(in2) + transform(in3) + transform(in4)) / 4.0f;
+
+ EXPECT_NEAR(expect_uv.u, out1.u, YuvConversionEpsilon());
+ EXPECT_NEAR(expect_uv.u, out2.u, YuvConversionEpsilon());
+ EXPECT_NEAR(expect_uv.u, out3.u, YuvConversionEpsilon());
+ EXPECT_NEAR(expect_uv.u, out4.u, YuvConversionEpsilon());
+
+ EXPECT_NEAR(expect_uv.v, out1.v, YuvConversionEpsilon());
+ EXPECT_NEAR(expect_uv.v, out2.v, YuvConversionEpsilon());
+ EXPECT_NEAR(expect_uv.v, out3.v, YuvConversionEpsilon());
+ EXPECT_NEAR(expect_uv.v, out4.v, YuvConversionEpsilon());
+ }
+}
+
TEST_F(GainMapMathTest, HlgOetf) {
EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f);
EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon());
@@ -693,7 +912,7 @@
TEST_F(GainMapMathTest, EncodeGain) {
ultrahdr_metadata_struct metadata = { .maxContentBoost = 4.0f,
- .minContentBoost = 1.0f / 4.0f };
+ .minContentBoost = 1.0f / 4.0f };
EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 127);
EXPECT_EQ(encodeGain(0.0f, 1.0f, &metadata), 127);
@@ -751,7 +970,7 @@
TEST_F(GainMapMathTest, ApplyGain) {
ultrahdr_metadata_struct metadata = { .maxContentBoost = 4.0f,
- .minContentBoost = 1.0f / 4.0f };
+ .minContentBoost = 1.0f / 4.0f };
float displayBoost = metadata.maxContentBoost;
EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.0f, &metadata), RgbBlack());
diff --git a/libs/ultrahdr/tests/icchelper_test.cpp b/libs/ultrahdr/tests/icchelper_test.cpp
new file mode 100644
index 0000000..ff61c08
--- /dev/null
+++ b/libs/ultrahdr/tests/icchelper_test.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <ultrahdr/icc.h>
+#include <ultrahdr/ultrahdr.h>
+#include <utils/Log.h>
+
+namespace android::ultrahdr {
+
+class IccHelperTest : public testing::Test {
+public:
+ IccHelperTest();
+ ~IccHelperTest();
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+};
+
+IccHelperTest::IccHelperTest() {}
+
+IccHelperTest::~IccHelperTest() {}
+
+void IccHelperTest::SetUp() {}
+
+void IccHelperTest::TearDown() {}
+
+TEST_F(IccHelperTest, iccWriteThenRead) {
+ sp<DataStruct> iccBt709 = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
+ ULTRAHDR_COLORGAMUT_BT709);
+ ASSERT_NE(iccBt709->getLength(), 0);
+ ASSERT_NE(iccBt709->getData(), nullptr);
+ EXPECT_EQ(IccHelper::readIccColorGamut(iccBt709->getData(), iccBt709->getLength()),
+ ULTRAHDR_COLORGAMUT_BT709);
+
+ sp<DataStruct> iccP3 = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_P3);
+ ASSERT_NE(iccP3->getLength(), 0);
+ ASSERT_NE(iccP3->getData(), nullptr);
+ EXPECT_EQ(IccHelper::readIccColorGamut(iccP3->getData(), iccP3->getLength()),
+ ULTRAHDR_COLORGAMUT_P3);
+
+ sp<DataStruct> iccBt2100 = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
+ ULTRAHDR_COLORGAMUT_BT2100);
+ ASSERT_NE(iccBt2100->getLength(), 0);
+ ASSERT_NE(iccBt2100->getData(), nullptr);
+ EXPECT_EQ(IccHelper::readIccColorGamut(iccBt2100->getData(), iccBt2100->getLength()),
+ ULTRAHDR_COLORGAMUT_BT2100);
+}
+
+TEST_F(IccHelperTest, iccEndianness) {
+ sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_BT709);
+ size_t profile_size = icc->getLength() - kICCIdentifierSize;
+
+ uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc->getData()) + kICCIdentifierSize;
+ uint32_t encoded_size = static_cast<uint32_t>(icc_bytes[0]) << 24 |
+ static_cast<uint32_t>(icc_bytes[1]) << 16 |
+ static_cast<uint32_t>(icc_bytes[2]) << 8 |
+ static_cast<uint32_t>(icc_bytes[3]);
+
+ EXPECT_EQ(static_cast<size_t>(encoded_size), profile_size);
+}
+
+} // namespace android::ultrahdr
+
diff --git a/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp b/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
index c79dbe3..e2da01c 100644
--- a/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
+++ b/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
@@ -15,6 +15,7 @@
*/
#include <ultrahdr/jpegdecoderhelper.h>
+#include <ultrahdr/icc.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
@@ -22,11 +23,19 @@
namespace android::ultrahdr {
+// No ICC or EXIF
#define YUV_IMAGE "/sdcard/Documents/minnie-320x240-yuv.jpg"
#define YUV_IMAGE_SIZE 20193
+// Has ICC and EXIF
+#define YUV_ICC_IMAGE "/sdcard/Documents/minnie-320x240-yuv-icc.jpg"
+#define YUV_ICC_IMAGE_SIZE 34266
+// No ICC or EXIF
#define GREY_IMAGE "/sdcard/Documents/minnie-320x240-y.jpg"
#define GREY_IMAGE_SIZE 20193
+#define IMAGE_WIDTH 320
+#define IMAGE_HEIGHT 240
+
class JpegDecoderHelperTest : public testing::Test {
public:
struct Image {
@@ -39,7 +48,7 @@
virtual void SetUp();
virtual void TearDown();
- Image mYuvImage, mGreyImage;
+ Image mYuvImage, mYuvIccImage, mGreyImage;
};
JpegDecoderHelperTest::JpegDecoderHelperTest() {}
@@ -79,6 +88,10 @@
FAIL() << "Load file " << YUV_IMAGE << " failed";
}
mYuvImage.size = YUV_IMAGE_SIZE;
+ if (!loadFile(YUV_ICC_IMAGE, &mYuvIccImage)) {
+ FAIL() << "Load file " << YUV_ICC_IMAGE << " failed";
+ }
+ mYuvIccImage.size = YUV_ICC_IMAGE_SIZE;
if (!loadFile(GREY_IMAGE, &mGreyImage)) {
FAIL() << "Load file " << GREY_IMAGE << " failed";
}
@@ -91,6 +104,16 @@
JpegDecoderHelper decoder;
EXPECT_TRUE(decoder.decompressImage(mYuvImage.buffer.get(), mYuvImage.size));
ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+ EXPECT_EQ(IccHelper::readIccColorGamut(decoder.getICCPtr(), decoder.getICCSize()),
+ ULTRAHDR_COLORGAMUT_UNSPECIFIED);
+}
+
+TEST_F(JpegDecoderHelperTest, decodeYuvIccImage) {
+ JpegDecoderHelper decoder;
+ EXPECT_TRUE(decoder.decompressImage(mYuvIccImage.buffer.get(), mYuvIccImage.size));
+ ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+ EXPECT_EQ(IccHelper::readIccColorGamut(decoder.getICCPtr(), decoder.getICCSize()),
+ ULTRAHDR_COLORGAMUT_BT709);
}
TEST_F(JpegDecoderHelperTest, decodeGreyImage) {
@@ -99,4 +122,35 @@
ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
}
-} // namespace android::ultrahdr
\ No newline at end of file
+TEST_F(JpegDecoderHelperTest, getCompressedImageParameters) {
+ size_t width = 0, height = 0;
+ std::vector<uint8_t> icc, exif;
+
+ JpegDecoderHelper decoder;
+ EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvImage.buffer.get(), mYuvImage.size,
+ &width, &height, &icc, &exif));
+
+ EXPECT_EQ(width, IMAGE_WIDTH);
+ EXPECT_EQ(height, IMAGE_HEIGHT);
+ EXPECT_EQ(icc.size(), 0);
+ EXPECT_EQ(exif.size(), 0);
+}
+
+TEST_F(JpegDecoderHelperTest, getCompressedImageParametersIcc) {
+ size_t width = 0, height = 0;
+ std::vector<uint8_t> icc, exif;
+
+ JpegDecoderHelper decoder;
+ EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvIccImage.buffer.get(), mYuvIccImage.size,
+ &width, &height, &icc, &exif));
+
+ EXPECT_EQ(width, IMAGE_WIDTH);
+ EXPECT_EQ(height, IMAGE_HEIGHT);
+ EXPECT_GT(icc.size(), 0);
+ EXPECT_GT(exif.size(), 0);
+
+ EXPECT_EQ(IccHelper::readIccColorGamut(icc.data(), icc.size()),
+ ULTRAHDR_COLORGAMUT_BT709);
+}
+
+} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
index 8f18ac0..f0e1fa4 100644
--- a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
+++ b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
@@ -108,18 +108,9 @@
ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
}
-// The width of the "unaligned" image is not 16-aligned, and will fail if encoded directly.
-// Should pass with the padding zero method.
TEST_F(JpegEncoderHelperTest, encodeUnalignedImage) {
JpegEncoderHelper encoder;
- const size_t paddingZeroLength = JpegEncoderHelper::kCompressBatchSize
- * JpegEncoderHelper::kCompressBatchSize / 4;
- std::unique_ptr<uint8_t[]> imageWithPaddingZeros(
- new uint8_t[UNALIGNED_IMAGE_WIDTH * UNALIGNED_IMAGE_HEIGHT * 3 / 2
- + paddingZeroLength]);
- memcpy(imageWithPaddingZeros.get(), mUnalignedImage.buffer.get(),
- UNALIGNED_IMAGE_WIDTH * UNALIGNED_IMAGE_HEIGHT * 3 / 2);
- EXPECT_TRUE(encoder.compressImage(imageWithPaddingZeros.get(), mUnalignedImage.width,
+ EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(), mUnalignedImage.width,
mUnalignedImage.height, JPEG_QUALITY, NULL, 0));
ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
}
diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
index d482ea1..7837bcf 100644
--- a/libs/ultrahdr/tests/jpegr_test.cpp
+++ b/libs/ultrahdr/tests/jpegr_test.cpp
@@ -14,165 +14,1738 @@
* limitations under the License.
*/
+#include <sys/time.h>
+#include <fstream>
+#include <iostream>
+
+#include <ultrahdr/gainmapmath.h>
#include <ultrahdr/jpegr.h>
#include <ultrahdr/jpegrutils.h>
-#include <ultrahdr/gainmapmath.h>
-#include <fcntl.h>
-#include <fstream>
+
#include <gtest/gtest.h>
-#include <sys/time.h>
#include <utils/Log.h>
-#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
-#define RAW_P010_IMAGE_WITH_STRIDE "/sdcard/Documents/raw_p010_image_with_stride.p010"
-#define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420"
-#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
-#define TEST_IMAGE_WIDTH 1280
-#define TEST_IMAGE_HEIGHT 720
-#define TEST_IMAGE_STRIDE 1288
-#define DEFAULT_JPEG_QUALITY 90
-
-#define SAVE_ENCODING_RESULT true
-#define SAVE_DECODING_RESULT true
-#define SAVE_INPUT_RGBA true
+//#define DUMP_OUTPUT
namespace android::ultrahdr {
-struct Timer {
- struct timeval StartingTime;
- struct timeval EndingTime;
- struct timeval ElapsedMicroseconds;
-};
+// resources used by unit tests
+const char* kYCbCrP010FileName = "raw_p010_image.p010";
+const char* kYCbCr420FileName = "raw_yuv420_image.yuv420";
+const char* kSdrJpgFileName = "jpeg_image.jpg";
+const int kImageWidth = 1280;
+const int kImageHeight = 720;
+const int kQuality = 90;
-void timerStart(Timer *t) {
- gettimeofday(&t->StartingTime, nullptr);
-}
+// Wrapper to describe the input type
+typedef enum {
+ YCbCr_p010 = 0,
+ YCbCr_420 = 1,
+} UhdrInputFormat;
-void timerStop(Timer *t) {
- gettimeofday(&t->EndingTime, nullptr);
-}
-
-int64_t elapsedTime(Timer *t) {
- t->ElapsedMicroseconds.tv_sec = t->EndingTime.tv_sec - t->StartingTime.tv_sec;
- t->ElapsedMicroseconds.tv_usec = t->EndingTime.tv_usec - t->StartingTime.tv_usec;
- return t->ElapsedMicroseconds.tv_sec * 1000000 + t->ElapsedMicroseconds.tv_usec;
-}
-
-static size_t getFileSize(int fd) {
- struct stat st;
- if (fstat(fd, &st) < 0) {
- ALOGW("%s : fstat failed", __func__);
- return 0;
- }
- return st.st_size; // bytes
-}
-
-static bool loadFile(const char filename[], void*& result, int* fileLength) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
- return false;
- }
- int length = getFileSize(fd);
- if (length == 0) {
- close(fd);
- return false;
- }
- if (fileLength != nullptr) {
- *fileLength = length;
- }
- result = malloc(length);
- if (read(fd, result, length) != static_cast<ssize_t>(length)) {
- close(fd);
- return false;
- }
- close(fd);
- return true;
-}
-
-static bool loadP010Image(const char *filename, jr_uncompressed_ptr img,
- bool isUVContiguous) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
- return false;
- }
- const int bpp = 2;
- int lumaStride = img->luma_stride == 0 ? img->width : img->luma_stride;
- int lumaSize = bpp * lumaStride * img->height;
- int chromaSize = bpp * (img->height / 2) *
- (isUVContiguous ? lumaStride : img->chroma_stride);
- img->data = malloc(lumaSize + (isUVContiguous ? chromaSize : 0));
- if (img->data == nullptr) {
- ALOGE("loadP010Image(): failed to allocate memory for luma data.");
- return false;
- }
- uint8_t *mem = static_cast<uint8_t *>(img->data);
- for (int i = 0; i < img->height; i++) {
- if (read(fd, mem, img->width * bpp) != img->width * bpp) {
- close(fd);
- return false;
- }
- mem += lumaStride * bpp;
- }
- int chromaStride = lumaStride;
- if (!isUVContiguous) {
- img->chroma_data = malloc(chromaSize);
- if (img->chroma_data == nullptr) {
- ALOGE("loadP010Image(): failed to allocate memory for chroma data.");
- return false;
- }
- mem = static_cast<uint8_t *>(img->chroma_data);
- chromaStride = img->chroma_stride;
- }
- for (int i = 0; i < img->height / 2; i++) {
- if (read(fd, mem, img->width * bpp) != img->width * bpp) {
- close(fd);
- return false;
- }
- mem += chromaStride * bpp;
- }
- close(fd);
- return true;
-}
-
-class JpegRTest : public testing::Test {
+/**
+ * Wrapper class for raw resource
+ * Sample usage:
+ * UhdrUnCompressedStructWrapper rawImg(width, height, YCbCr_p010);
+ * rawImg.setImageColorGamut(colorGamut));
+ * rawImg.setImageStride(strideLuma, strideChroma); // optional
+ * rawImg.setChromaMode(false); // optional
+ * rawImg.allocateMemory();
+ * rawImg.loadRawResource(kYCbCrP010FileName);
+ */
+class UhdrUnCompressedStructWrapper {
public:
- JpegRTest();
- ~JpegRTest();
+ UhdrUnCompressedStructWrapper(uint32_t width, uint32_t height, UhdrInputFormat format);
+ ~UhdrUnCompressedStructWrapper() = default;
-protected:
- virtual void SetUp();
- virtual void TearDown();
+ bool setChromaMode(bool isChromaContiguous);
+ bool setImageStride(int lumaStride, int chromaStride);
+ bool setImageColorGamut(ultrahdr_color_gamut colorGamut);
+ bool allocateMemory();
+ bool loadRawResource(const char* fileName);
+ jr_uncompressed_ptr getImageHandle();
- struct jpegr_uncompressed_struct mRawP010Image{};
- struct jpegr_uncompressed_struct mRawP010ImageWithStride{};
- struct jpegr_uncompressed_struct mRawP010ImageWithChromaData{};
- struct jpegr_uncompressed_struct mRawYuv420Image{};
- struct jpegr_compressed_struct mJpegImage{};
+private:
+ std::unique_ptr<uint8_t[]> mLumaData;
+ std::unique_ptr<uint8_t[]> mChromaData;
+ jpegr_uncompressed_struct mImg;
+ UhdrInputFormat mFormat;
+ bool mIsChromaContiguous;
};
-JpegRTest::JpegRTest() {}
-JpegRTest::~JpegRTest() {}
+/**
+ * Wrapper class for compressed resource
+ * Sample usage:
+ * UhdrCompressedStructWrapper jpgImg(width, height);
+ * rawImg.allocateMemory();
+ */
+class UhdrCompressedStructWrapper {
+public:
+ UhdrCompressedStructWrapper(uint32_t width, uint32_t height);
+ ~UhdrCompressedStructWrapper() = default;
-void JpegRTest::SetUp() {}
-void JpegRTest::TearDown() {
- free(mRawP010Image.data);
- free(mRawP010Image.chroma_data);
- free(mRawP010ImageWithStride.data);
- free(mRawP010ImageWithStride.chroma_data);
- free(mRawP010ImageWithChromaData.data);
- free(mRawP010ImageWithChromaData.chroma_data);
- free(mRawYuv420Image.data);
- free(mJpegImage.data);
+ bool allocateMemory();
+ jr_compressed_ptr getImageHandle();
+
+private:
+ std::unique_ptr<uint8_t[]> mData;
+ jpegr_compressed_struct mImg{};
+ uint32_t mWidth;
+ uint32_t mHeight;
+};
+
+UhdrUnCompressedStructWrapper::UhdrUnCompressedStructWrapper(uint32_t width, uint32_t height,
+ UhdrInputFormat format) {
+ mImg.data = nullptr;
+ mImg.width = width;
+ mImg.height = height;
+ mImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ mImg.chroma_data = nullptr;
+ mImg.luma_stride = 0;
+ mImg.chroma_stride = 0;
+ mFormat = format;
+ mIsChromaContiguous = true;
}
+bool UhdrUnCompressedStructWrapper::setChromaMode(bool isChromaContiguous) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
+ return false;
+ }
+ mIsChromaContiguous = isChromaContiguous;
+ return true;
+}
+
+bool UhdrUnCompressedStructWrapper::setImageStride(int lumaStride, int chromaStride) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
+ return false;
+ }
+ if (lumaStride != 0) {
+ if (lumaStride < mImg.width) {
+ std::cerr << "Bad luma stride received" << std::endl;
+ return false;
+ }
+ mImg.luma_stride = lumaStride;
+ }
+ if (chromaStride != 0) {
+ if (mFormat == YCbCr_p010 && chromaStride < mImg.width) {
+ std::cerr << "Bad chroma stride received for format YCbCrP010" << std::endl;
+ return false;
+ }
+ if (mFormat == YCbCr_420 && chromaStride < (mImg.width >> 1)) {
+ std::cerr << "Bad chroma stride received for format YCbCr420" << std::endl;
+ return false;
+ }
+ mImg.chroma_stride = chromaStride;
+ }
+ return true;
+}
+
+bool UhdrUnCompressedStructWrapper::setImageColorGamut(ultrahdr_color_gamut colorGamut) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
+ return false;
+ }
+ mImg.colorGamut = colorGamut;
+ return true;
+}
+
+bool UhdrUnCompressedStructWrapper::allocateMemory() {
+ if (mImg.width == 0 || (mImg.width % 2 != 0) || mImg.height == 0 || (mImg.height % 2 != 0) ||
+ (mFormat != YCbCr_p010 && mFormat != YCbCr_420)) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
+ return false;
+ }
+ int lumaStride = mImg.luma_stride == 0 ? mImg.width : mImg.luma_stride;
+ int lumaSize = lumaStride * mImg.height * (mFormat == YCbCr_p010 ? 2 : 1);
+ int chromaSize = (mImg.height >> 1) * (mFormat == YCbCr_p010 ? 2 : 1);
+ if (mIsChromaContiguous) {
+ chromaSize *= lumaStride;
+ } else {
+ if (mImg.chroma_stride == 0) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
+ return false;
+ }
+ if (mFormat == YCbCr_p010) {
+ chromaSize *= mImg.chroma_stride;
+ } else {
+ chromaSize *= (mImg.chroma_stride * 2);
+ }
+ }
+ if (mIsChromaContiguous) {
+ mLumaData = std::make_unique<uint8_t[]>(lumaSize + chromaSize);
+ mImg.data = mLumaData.get();
+ mImg.chroma_data = nullptr;
+ } else {
+ mLumaData = std::make_unique<uint8_t[]>(lumaSize);
+ mImg.data = mLumaData.get();
+ mChromaData = std::make_unique<uint8_t[]>(chromaSize);
+ mImg.chroma_data = mChromaData.get();
+ }
+ return true;
+}
+
+bool UhdrUnCompressedStructWrapper::loadRawResource(const char* fileName) {
+ if (!mImg.data) {
+ std::cerr << "memory is not allocated, read not possible" << std::endl;
+ return false;
+ }
+ std::ifstream ifd(fileName, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ int bpp = mFormat == YCbCr_p010 ? 2 : 1;
+ int size = ifd.tellg();
+ int length = mImg.width * mImg.height * bpp * 3 / 2; // 2x2 subsampling
+ if (size < length) {
+ std::cerr << "requested to read " << length << " bytes from file : " << fileName
+ << ", file contains only " << length << " bytes" << std::endl;
+ return false;
+ }
+ ifd.seekg(0, std::ios::beg);
+ int lumaStride = mImg.luma_stride == 0 ? mImg.width : mImg.luma_stride;
+ char* mem = static_cast<char*>(mImg.data);
+ for (int i = 0; i < mImg.height; i++) {
+ ifd.read(mem, mImg.width * bpp);
+ mem += lumaStride * bpp;
+ }
+ if (!mIsChromaContiguous) {
+ mem = static_cast<char*>(mImg.chroma_data);
+ }
+ int chromaStride;
+ if (mIsChromaContiguous) {
+ chromaStride = mFormat == YCbCr_p010 ? lumaStride : lumaStride / 2;
+ } else {
+ if (mFormat == YCbCr_p010) {
+ chromaStride = mImg.chroma_stride == 0 ? lumaStride : mImg.chroma_stride;
+ } else {
+ chromaStride = mImg.chroma_stride == 0 ? (lumaStride / 2) : mImg.chroma_stride;
+ }
+ }
+ if (mFormat == YCbCr_p010) {
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, mImg.width * 2);
+ mem += chromaStride * 2;
+ }
+ } else {
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, (mImg.width / 2));
+ mem += chromaStride;
+ }
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, (mImg.width / 2));
+ mem += chromaStride;
+ }
+ }
+ return true;
+ }
+ std::cerr << "unable to open file : " << fileName << std::endl;
+ return false;
+}
+
+jr_uncompressed_ptr UhdrUnCompressedStructWrapper::getImageHandle() {
+ return &mImg;
+}
+
+UhdrCompressedStructWrapper::UhdrCompressedStructWrapper(uint32_t width, uint32_t height) {
+ mWidth = width;
+ mHeight = height;
+}
+
+bool UhdrCompressedStructWrapper::allocateMemory() {
+ if (mWidth == 0 || (mWidth % 2 != 0) || mHeight == 0 || (mHeight % 2 != 0)) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
+ return false;
+ }
+ int maxLength = std::max(8 * 1024 /* min size 8kb */, (int)(mWidth * mHeight * 3 * 2));
+ mData = std::make_unique<uint8_t[]>(maxLength);
+ mImg.data = mData.get();
+ mImg.length = 0;
+ mImg.maxLength = maxLength;
+ return true;
+}
+
+jr_compressed_ptr UhdrCompressedStructWrapper::getImageHandle() {
+ return &mImg;
+}
+
+static bool writeFile(const char* filename, void*& result, int length) {
+ std::ofstream ofd(filename, std::ios::binary);
+ if (ofd.is_open()) {
+ ofd.write(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to write to file : " << filename << std::endl;
+ return false;
+}
+
+static bool readFile(const char* fileName, void*& result, int maxLength, int& length) {
+ std::ifstream ifd(fileName, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ length = ifd.tellg();
+ if (length > maxLength) {
+ std::cerr << "not enough space to read file" << std::endl;
+ return false;
+ }
+ ifd.seekg(0, std::ios::beg);
+ ifd.read(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to read file : " << fileName << std::endl;
+ return false;
+}
+
+void decodeJpegRImg(jr_compressed_ptr img, [[maybe_unused]] const char* outFileName) {
+ std::vector<uint8_t> iccData(0);
+ std::vector<uint8_t> exifData(0);
+ jpegr_info_struct info{0, 0, &iccData, &exifData};
+ JpegR jpegHdr;
+ ASSERT_EQ(OK, jpegHdr.getJPEGRInfo(img, &info));
+ ASSERT_EQ(kImageWidth, info.width);
+ ASSERT_EQ(kImageHeight, info.height);
+ size_t outSize = info.width * info.height * 8;
+ std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
+ jpegr_uncompressed_struct destImage{};
+ destImage.data = data.get();
+ ASSERT_EQ(OK, jpegHdr.decodeJPEGR(img, &destImage));
+ ASSERT_EQ(kImageWidth, destImage.width);
+ ASSERT_EQ(kImageHeight, destImage.height);
+#ifdef DUMP_OUTPUT
+ if (!writeFile(outFileName, destImage.data, outSize)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+}
+
+// ============================================================================
+// Unit Tests
+// ============================================================================
+
+// Test Encode API-0 invalid arguments
+TEST(JpegRTest, EncodeAPI0WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), -1, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), 101, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
+
+ // test dest
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
+ nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
+
+ // test p010 input
+ {
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
+
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1)));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+ }
+
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
+}
+
+/* Test Encode API-1 invalid arguments */
+TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), -1, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), 101, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
+
+ // test dest
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
+ nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
+
+ // test p010 input
+ {
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
+
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
+
+ // test 420 input
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr 420 image";
+
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr 420 image";
+ }
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad 420 color gamut";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad 420 color gamut";
+
+ rawImg420->width = kWidth - 1;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = 0;
+ rawImg420->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = kWidth;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows luma stride for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = 0;
+ rawImg420->chroma_data = rawImgP010->data;
+ rawImg420->chroma_stride = kWidth;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma pointer for 420";
+ }
+}
+
+/* Test Encode API-2 invalid arguments */
+TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
+
+ // test dest
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
+
+ // test compressed image
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ }
+
+ // test p010 input
+ {
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
+
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
+
+ // test 420 input
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr 420 image";
+
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr 420 image";
+ }
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad 420 color gamut";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad 420 color gamut";
+
+ rawImg420->width = kWidth - 1;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = 0;
+ rawImg420->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = kWidth;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows luma stride for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = 0;
+ rawImg420->chroma_data = rawImgP010->data;
+ rawImg420->chroma_stride = kWidth;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma pointer for 420";
+ }
+}
+
+/* Test Encode API-3 invalid arguments */
+TEST(JpegRTest, EncodeAPI3WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
+
+ // test dest
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
+
+ // test compressed image
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ }
+
+ // test p010 input
+ {
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
+
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
+}
+
+/* Test Encode API-4 invalid arguments */
+TEST(JpegRTest, EncodeAPI4WithInvalidArgs) {
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ JpegR uHdrLib;
+
+ // test dest
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
+
+ // test primary image
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(), nullptr, jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr primary image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg2.getImageHandle(), jpgImg.getImageHandle(), nullptr,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr primary image";
+
+ // test gain map
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), nullptr, nullptr, jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr gain map image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg2.getImageHandle(), nullptr,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr gain map image";
+
+ // test metadata
+ ultrahdr_metadata_struct good_metadata;
+ good_metadata.version = "1.0";
+ good_metadata.minContentBoost = 1.0f;
+ good_metadata.maxContentBoost = 2.0f;
+ good_metadata.gamma = 1.0f;
+ good_metadata.offsetSdr = 0.0f;
+ good_metadata.offsetHdr = 0.0f;
+ good_metadata.hdrCapacityMin = 1.0f;
+ good_metadata.hdrCapacityMax = 2.0f;
+
+ ultrahdr_metadata_struct metadata = good_metadata;
+ metadata.version = "1.1";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata version";
+
+ metadata = good_metadata;
+ metadata.minContentBoost = 3.0f;
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata content boost";
+
+ metadata = good_metadata;
+ metadata.gamma = -0.1f;
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata gamma";
+
+ metadata = good_metadata;
+ metadata.offsetSdr = -0.1f;
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata offset sdr";
+
+ metadata = good_metadata;
+ metadata.offsetHdr = -0.1f;
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata offset hdr";
+
+ metadata = good_metadata;
+ metadata.hdrCapacityMax = 0.5f;
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata hdr capacity max";
+
+ metadata = good_metadata;
+ metadata.hdrCapacityMin = 0.5f;
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata hdr capacity min";
+}
+
+/* Test Decode API invalid arguments */
+TEST(JpegRTest, DecodeAPIWithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ jpegr_uncompressed_struct destImage{};
+ size_t outSize = 16 * 16 * 8;
+ std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
+ destImage.data = data.get();
+
+ // test jpegr image
+ ASSERT_NE(uHdrLib.decodeJPEGR(nullptr, &destImage), OK)
+ << "fail, API allows nullptr for jpegr img";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
+ << "fail, API allows nullptr for jpegr img";
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test dest image
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), nullptr), OK)
+ << "fail, API allows nullptr for dest";
+ destImage.data = nullptr;
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
+ << "fail, API allows nullptr for dest";
+ destImage.data = data.get();
+
+ // test max display boost
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, 0.5), OK)
+ << "fail, API allows invalid max display boost";
+
+ // test output format
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
+ static_cast<ultrahdr_output_format>(-1)),
+ OK)
+ << "fail, API allows invalid output format";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
+ static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)),
+ OK)
+ << "fail, API allows invalid output format";
+}
+
+TEST(JpegRTest, writeXmpThenRead) {
+ ultrahdr_metadata_struct metadata_expected;
+ metadata_expected.version = "1.0";
+ metadata_expected.maxContentBoost = 1.25f;
+ metadata_expected.minContentBoost = 0.75f;
+ metadata_expected.gamma = 1.0f;
+ metadata_expected.offsetSdr = 0.0f;
+ metadata_expected.offsetHdr = 0.0f;
+ metadata_expected.hdrCapacityMin = 1.0f;
+ metadata_expected.hdrCapacityMax = metadata_expected.maxContentBoost;
+ const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+
+ std::string xmp = generateXmpForSecondaryImage(metadata_expected);
+
+ std::vector<uint8_t> xmpData;
+ xmpData.reserve(nameSpaceLength + xmp.size());
+ xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(nameSpace.c_str()),
+ reinterpret_cast<const uint8_t*>(nameSpace.c_str()) + nameSpaceLength);
+ xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
+ reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
+
+ ultrahdr_metadata_struct metadata_read;
+ EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
+ EXPECT_FLOAT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
+ EXPECT_FLOAT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
+ EXPECT_FLOAT_EQ(metadata_expected.gamma, metadata_read.gamma);
+ EXPECT_FLOAT_EQ(metadata_expected.offsetSdr, metadata_read.offsetSdr);
+ EXPECT_FLOAT_EQ(metadata_expected.offsetHdr, metadata_read.offsetHdr);
+ EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMin, metadata_read.hdrCapacityMin);
+ EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMax, metadata_read.hdrCapacityMax);
+}
+
+/* Test Encode API-0 and Decode */
+TEST(JpegRTest, EncodeAPI0AndDecodeTest) {
+ // reference encode
+ UhdrUnCompressedStructWrapper rawImg(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_TRUE(rawImg.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2.setChromaMode(false));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2.setChromaMode(false));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+
+ auto jpg1 = jpgImg.getImageHandle();
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api0_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api0_output.rgb"));
+}
+
+/* Test Encode API-1 and Decode */
+TEST(JpegRTest, EncodeAPI1AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+
+ auto jpg1 = jpgImg.getImageHandle();
+
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api1_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api1_output.rgb"));
+}
+
+/* Test Encode API-2 and Decode */
+TEST(JpegRTest, EncodeAPI2AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgSdr(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgSdr.allocateMemory());
+ auto sdr = jpgSdr.getImageHandle();
+ ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+
+ auto jpg1 = jpgImg.getImageHandle();
+
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api2_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api2_output.rgb"));
+}
+
+/* Test Encode API-3 and Decode */
+TEST(JpegRTest, EncodeAPI3AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgSdr(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgSdr.allocateMemory());
+ auto sdr = jpgSdr.getImageHandle();
+ ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+
+ auto jpg1 = jpgImg.getImageHandle();
+
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api3_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api3_output.rgb"));
+}
+
+// ============================================================================
+// Profiling
+// ============================================================================
+
+class Profiler {
+public:
+ void timerStart() { gettimeofday(&mStartingTime, nullptr); }
+
+ void timerStop() { gettimeofday(&mEndingTime, nullptr); }
+
+ int64_t elapsedTime() {
+ struct timeval elapsedMicroseconds;
+ elapsedMicroseconds.tv_sec = mEndingTime.tv_sec - mStartingTime.tv_sec;
+ elapsedMicroseconds.tv_usec = mEndingTime.tv_usec - mStartingTime.tv_usec;
+ return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
+ }
+
+private:
+ struct timeval mStartingTime;
+ struct timeval mEndingTime;
+};
+
class JpegRBenchmark : public JpegR {
public:
- void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
- void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
+ void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
+ ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
+ void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
+ ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
+
private:
- const int kProfileCount = 10;
+ const int kProfileCount = 10;
};
void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
@@ -181,1141 +1754,62 @@
jr_uncompressed_ptr map) {
ASSERT_EQ(yuv420Image->width, p010Image->width);
ASSERT_EQ(yuv420Image->height, p010Image->height);
-
- Timer genRecMapTime;
-
- timerStart(&genRecMapTime);
+ Profiler profileGenerateMap;
+ profileGenerateMap.timerStart();
for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK, generateGainMap(
- yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, metadata, map));
- if (i != kProfileCount - 1) delete[] static_cast<uint8_t *>(map->data);
+ ASSERT_EQ(OK,
+ generateGainMap(yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ metadata, map));
+ if (i != kProfileCount - 1) delete[] static_cast<uint8_t*>(map->data);
}
- timerStop(&genRecMapTime);
-
- ALOGE("Generate Gain Map:- Res = %i x %i, time = %f ms",
- yuv420Image->width, yuv420Image->height,
- elapsedTime(&genRecMapTime) / (kProfileCount * 1000.f));
-
+ profileGenerateMap.timerStop();
+ ALOGE("Generate Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height,
+ profileGenerateMap.elapsedTime() / (kProfileCount * 1000.f));
}
-void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image,
- jr_uncompressed_ptr map,
+void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
ultrahdr_metadata_ptr metadata,
jr_uncompressed_ptr dest) {
- Timer applyRecMapTime;
-
- timerStart(&applyRecMapTime);
+ Profiler profileRecMap;
+ profileRecMap.timerStart();
for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK, applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
- metadata->maxContentBoost /* displayBoost */, dest));
+ ASSERT_EQ(OK,
+ applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
+ metadata->maxContentBoost /* displayBoost */, dest));
}
- timerStop(&applyRecMapTime);
-
- ALOGE("Apply Gain Map:- Res = %i x %i, time = %f ms",
- yuv420Image->width, yuv420Image->height,
- elapsedTime(&applyRecMapTime) / (kProfileCount * 1000.f));
+ profileRecMap.timerStop();
+ ALOGE("Apply Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height,
+ profileRecMap.elapsedTime() / (kProfileCount * 1000.f));
}
-TEST_F(JpegRTest, build) {
- // Force all of the gain map lib to be linked by calling all public functions.
- JpegR jpegRCodec;
- jpegRCodec.encodeJPEGR(nullptr, static_cast<ultrahdr_transfer_function>(0), nullptr, 0, nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0),
- nullptr, 0, nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0),
- nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0), nullptr);
- jpegRCodec.decodeJPEGR(nullptr, nullptr);
-}
-
-/* Test Encode API-0 invalid arguments */
-TEST_F(JpegRTest, encodeAPI0ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // test quality factor
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- -1, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- 101, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
-
- // test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr dest";
-
- // test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr p010 image";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride";
-
- mRawP010ImageWithStride.chroma_data = nullptr;
-
- free(jpegR.data);
-}
-
-/* Test Encode API-1 invalid arguments */
-TEST_F(JpegRTest, encodeAPI1ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawYuv420Image.data = malloc(16);
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- // test quality factor
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, -1, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, 101, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
-
- // test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- nullptr, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr dest";
-
- // test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr p010 image";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride";
-
- // test 420 input
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = nullptr;
- mRawP010ImageWithStride.chroma_stride = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr for 420 image";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 image width";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH - 2;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 image height";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = TEST_IMAGE_STRIDE;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride for 420";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = mRawYuv420Image.data;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows chroma pointer for 420";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = nullptr;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 color gamut";
-
- mRawYuv420Image.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 color gamut";
-
- free(jpegR.data);
-}
-
-/* Test Encode API-2 invalid arguments */
-TEST_F(JpegRTest, encodeAPI2ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawYuv420Image.data = malloc(16);
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- // test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr)) << "fail, API allows nullptr dest";
-
- // test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr p010 image";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad luma stride";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad chroma stride";
-
- // test 420 input
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = nullptr;
- mRawP010ImageWithStride.chroma_stride = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr for 420 image";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 image width";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH - 2;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 image height";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = TEST_IMAGE_STRIDE;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad luma stride for 420";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = mRawYuv420Image.data;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows chroma pointer for 420";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = nullptr;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
-
- mRawYuv420Image.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
-
- // bad compressed image
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, nullptr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
-
- free(jpegR.data);
-}
-
-/* Test Encode API-3 invalid arguments */
-TEST_F(JpegRTest, encodeAPI3ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, static_cast<ultrahdr_transfer_function>(-10),
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- // test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- nullptr)) << "fail, API allows nullptr dest";
-
- // test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr p010 image";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad luma stride";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad chroma stride";
- mRawP010ImageWithStride.chroma_data = nullptr;
-
- // bad compressed image
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
-
- free(jpegR.data);
-}
-
-/* Test Encode API-4 invalid arguments */
-TEST_F(JpegRTest, encodeAPI4ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, &jpegR, nullptr, nullptr)) << "fail, API allows nullptr dest";
-
- // test primary image
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &jpegR, nullptr, &jpegR)) << "fail, API allows nullptr primary image";
-
- // test gain map
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, nullptr, &jpegR)) << "fail, API allows nullptr gainmap image";
-
- free(jpegR.data);
-}
-
-/* Test Decode API invalid arguments */
-TEST_F(JpegRTest, decodeAPIForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- // we are not really decoding anything so lets keep allocs to a minimum
- mRawP010Image.data = malloc(16);
-
- JpegR jpegRCodec;
-
- // test jpegr image
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- nullptr, &mRawP010Image)) << "fail, API allows nullptr for jpegr img";
-
- // test dest image
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, nullptr)) << "fail, API allows nullptr for dest";
-
- // test max display boost
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5)) << "fail, API allows invalid max display boost";
-
- // test output format
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5, nullptr,
- static_cast<ultrahdr_output_format>(-1))) << "fail, API allows invalid output format";
-
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5, nullptr,
- static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)))
- << "fail, API allows invalid output format";
-
- free(jpegR.data);
-}
-
-TEST_F(JpegRTest, writeXmpThenRead) {
- ultrahdr_metadata_struct metadata_expected;
- metadata_expected.version = "1.0";
- metadata_expected.maxContentBoost = 1.25;
- metadata_expected.minContentBoost = 0.75;
- const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
-
- std::string xmp = generateXmpForSecondaryImage(metadata_expected);
-
- std::vector<uint8_t> xmpData;
- xmpData.reserve(nameSpaceLength + xmp.size());
- xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(nameSpace.c_str()),
- reinterpret_cast<const uint8_t*>(nameSpace.c_str()) + nameSpaceLength);
- xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
- reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
-
- ultrahdr_metadata_struct metadata_read;
- EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
- EXPECT_FLOAT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
- EXPECT_FLOAT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
-}
-
-/* Test Encode API-0 */
-TEST_F(JpegRTest, encodeFromP010) {
- int ret;
-
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010Image, true)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH + 128;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithStride, true)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
-
- jpegr_compressed_struct jpegRWithStride;
- jpegRWithStride.maxLength = jpegR.length;
- jpegRWithStride.data = malloc(jpegRWithStride.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegRWithStride,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- ASSERT_EQ(jpegR.length, jpegRWithStride.length)
- << "Same input is yielding different output";
- ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithStride.data, jpegR.length))
- << "Same input is yielding different output";
-
- mRawP010ImageWithChromaData.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithChromaData.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithChromaData.luma_stride = TEST_IMAGE_WIDTH + 64;
- mRawP010ImageWithChromaData.chroma_stride = TEST_IMAGE_WIDTH + 256;
- mRawP010ImageWithChromaData.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithChromaData, false)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- jpegr_compressed_struct jpegRWithChromaData;
- jpegRWithChromaData.maxLength = jpegR.length;
- jpegRWithChromaData.data = malloc(jpegRWithChromaData.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithChromaData, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegRWithChromaData, DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- ASSERT_EQ(jpegR.length, jpegRWithChromaData.length)
- << "Same input is yielding different output";
- ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithChromaData.data, jpegR.length))
- << "Same input is yielding different output";
-
- free(jpegR.data);
- free(jpegRWithStride.data);
- free(jpegRWithChromaData.data);
-}
-
-/* Test Encode API-0 and decode */
-TEST_F(JpegRTest, encodeFromP010ThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
-
- free(jpegR.data);
- free(decodedJpegR.data);
-}
-
-/* Test Encode API-0 (with stride) and decode */
-TEST_F(JpegRTest, encodeFromP010WithStrideThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE_WITH_STRIDE, mRawP010ImageWithStride.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE_WITH_STRIDE << " failed";
- }
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
-
- free(jpegR.data);
- free(decodedJpegR.data);
-}
-
-/* Test Encode API-1 and decode */
-TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
-
- free(jpegR.data);
- free(decodedJpegR.data);
-}
-
-/* Test Encode API-2 and decode */
-TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
- FAIL() << "Load file " << JPEG_IMAGE << " failed";
- }
- mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mRawYuv420Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_jpeg_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
-
- free(jpegR.data);
- free(decodedJpegR.data);
-}
-
-/* Test Encode API-3 and decode */
-TEST_F(JpegRTest, encodeFromJpegThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (SAVE_INPUT_RGBA) {
- size_t rgbaSize = mRawP010Image.width * mRawP010Image.height * sizeof(uint32_t);
- uint32_t *data = (uint32_t *)malloc(rgbaSize);
-
- for (size_t y = 0; y < mRawP010Image.height; ++y) {
- for (size_t x = 0; x < mRawP010Image.width; ++x) {
- Color hdr_yuv_gamma = getP010Pixel(&mRawP010Image, x, y);
- Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
- uint32_t rgba1010102 = colorToRgba1010102(hdr_rgb_gamma);
- size_t pixel_idx = x + y * mRawP010Image.width;
- reinterpret_cast<uint32_t*>(data)[pixel_idx] = rgba1010102;
- }
- }
-
- // Output image data to file
- std::string filePath = "/sdcard/Documents/input_from_p010.rgb10";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)data, rgbaSize);
- free(data);
- }
- if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
- FAIL() << "Load file " << JPEG_IMAGE << " failed";
- }
- mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_jpeg_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
-
- free(jpegR.data);
- free(decodedJpegR.data);
-}
-
-TEST_F(JpegRTest, ProfileGainMapFuncs) {
- const size_t kWidth = TEST_IMAGE_WIDTH;
- const size_t kHeight = TEST_IMAGE_HEIGHT;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = kWidth;
- mRawP010Image.height = kHeight;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawYuv420Image.width = kWidth;
- mRawYuv420Image.height = kHeight;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
+TEST(JpegRTest, ProfileGainMapFuncs) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ ultrahdr_metadata_struct metadata = {.version = "1.0"};
+ jpegr_uncompressed_struct map = {.data = NULL,
+ .width = 0,
+ .height = 0,
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
JpegRBenchmark benchmark;
+ ASSERT_NO_FATAL_FAILURE(benchmark.BenchmarkGenerateGainMap(rawImg420.getImageHandle(),
+ rawImgP010.getImageHandle(), &metadata,
+ &map));
- ultrahdr_metadata_struct metadata = { .version = "1.0",
- .maxContentBoost = 8.0f,
- .minContentBoost = 1.0f / 8.0f };
-
- jpegr_uncompressed_struct map = { .data = NULL,
+ const int dstSize = kImageWidth * kImageWidth * 4;
+ auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
+ jpegr_uncompressed_struct dest = {.data = bufferDst.get(),
.width = 0,
.height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED };
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
- benchmark.BenchmarkGenerateGainMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map);
-
- const int dstSize = mRawYuv420Image.width * mRawYuv420Image.height * 4;
- auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
- jpegr_uncompressed_struct dest = { .data = bufferDst.get(),
- .width = 0,
- .height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED };
-
- benchmark.BenchmarkApplyGainMap(&mRawYuv420Image, &map, &metadata, &dest);
+ ASSERT_NO_FATAL_FAILURE(
+ benchmark.BenchmarkApplyGainMap(rawImg420.getImageHandle(), &map, &metadata, &dest));
}
} // namespace android::ultrahdr
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 32c21f6..c787fc9 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -699,7 +699,7 @@
#ifndef EGL_EXT_gl_colorspace_bt2020_hlg
#define EGL_EXT_gl_colorspace_bt2020_hlg 1
-#define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x333E
+#define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x3540
#endif /* EGL_EXT_gl_colorspace_bt2020_hlg */
#ifndef EGL_EXT_gl_colorspace_bt2020_linear
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 2c3ce16..8d0eb59 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -137,24 +137,36 @@
#endif
#endif
-static const char* DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
+static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl";
+static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
+static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform";
static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = {
- "persist.graphics.egl",
- DRIVER_SUFFIX_PROPERTY,
- "ro.board.platform",
+ PERSIST_DRIVER_SUFFIX_PROPERTY,
+ RO_DRIVER_SUFFIX_PROPERTY,
+ RO_BOARD_PLATFORM_PROPERTY,
};
+// Check whether the loaded system drivers should be unloaded in order to
+// load ANGLE or the updatable graphics drivers.
+// If ANGLE namespace is set, it means the application is identified to run on top of ANGLE.
+// If updatable graphics driver namespace is set, it means the application is identified to
+// run on top of updatable graphics drivers.
static bool should_unload_system_driver(egl_connection_t* cnx) {
// Return false if the system driver has been unloaded once.
if (cnx->systemDriverUnloaded) {
return false;
}
- // Return true if Angle namespace is set.
+ // Return true if ANGLE namespace is set.
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
if (ns) {
- return true;
+ // Unless the default GLES driver is ANGLE and the process should use system ANGLE, since
+ // the intended GLES driver is already loaded.
+ // This should be updated in a later patch that cleans up namespaces
+ if (!(cnx->angleLoaded && android::GraphicsEnv::getInstance().shouldUseSystemAngle())) {
+ return true;
+ }
}
// Return true if updated driver namespace is set.
@@ -199,17 +211,17 @@
do_android_unload_sphal_library(hnd->dso[0]);
}
cnx->dso = nullptr;
+ cnx->angleLoaded = false;
}
cnx->systemDriverUnloaded = true;
}
-void* Loader::open(egl_connection_t* cnx)
-{
+void* Loader::open(egl_connection_t* cnx) {
ATRACE_CALL();
const nsecs_t openTime = systemTime();
- if (should_unload_system_driver(cnx)) {
+ if (cnx->dso && should_unload_system_driver(cnx)) {
unload_system_driver(cnx);
}
@@ -218,8 +230,12 @@
return cnx->dso;
}
- // Firstly, try to load ANGLE driver.
- driver_t* hnd = attempt_to_load_angle(cnx);
+ driver_t* hnd = nullptr;
+ // Firstly, try to load ANGLE driver, if ANGLE should be loaded and fail, abort.
+ if (android::GraphicsEnv::getInstance().shouldUseAngle()) {
+ hnd = attempt_to_load_angle(cnx);
+ LOG_ALWAYS_FATAL_IF(!hnd, "Failed to load ANGLE.");
+ }
if (!hnd) {
// Secondly, try to load from driver apk.
@@ -245,17 +261,20 @@
continue;
}
hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true);
- if (hnd) {
- break;
- } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
+ if (!hnd) {
+ ALOGD("Failed to load drivers from property %s with value %s", key, prop.c_str());
failToLoadFromDriverSuffixProperty = true;
}
+
+ // Abort regardless of whether subsequent properties are set, the value must be set
+ // correctly with the first property that has a value.
+ break;
}
}
if (!hnd) {
- // Can't find graphics driver by appending system properties, now search for the exact name
- // without any suffix of the GLES userspace driver in both locations.
+ // Can't find graphics driver by appending the value from system properties, now search for
+ // the exact name without any suffix of the GLES userspace driver in both locations.
// i.e.:
// libGLES.so, or:
// libEGL.so, libGLESv1_CM.so, libGLESv2.so
@@ -274,10 +293,10 @@
false, systemTime() - openTime);
} else {
// init_angle_backend will check if loaded driver is ANGLE or not,
- // will set cnx->useAngle appropriately.
+ // will set cnx->angleLoaded appropriately.
// Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
// not just loading ANGLE as option.
- init_angle_backend(hnd->dso[2], cnx);
+ attempt_to_init_angle_backend(hnd->dso[2], cnx);
}
LOG_ALWAYS_FATAL_IF(!hnd,
@@ -319,7 +338,7 @@
delete hnd;
cnx->dso = nullptr;
- cnx->useAngle = false;
+ cnx->angleLoaded = false;
}
void Loader::init_api(void* dso,
@@ -531,10 +550,6 @@
Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) {
ATRACE_CALL();
- if (!android::GraphicsEnv::getInstance().shouldUseAngle()) {
- return nullptr;
- }
-
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
if (!ns) {
return nullptr;
@@ -560,14 +575,14 @@
return hnd;
}
-void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) {
+void Loader::attempt_to_init_angle_backend(void* dso, egl_connection_t* cnx) {
void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
if (pANGLEGetDisplayPlatform) {
- ALOGV("ANGLE GLES library in use");
- cnx->useAngle = true;
+ ALOGV("ANGLE GLES library loaded");
+ cnx->angleLoaded = true;
} else {
- ALOGV("Native GLES library in use");
- cnx->useAngle = false;
+ ALOGV("Native GLES library loaded");
+ cnx->angleLoaded = false;
}
}
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 81742ab..cadbd46 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -57,7 +57,7 @@
driver_t* attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, const bool exact);
void unload_system_driver(egl_connection_t* cnx);
void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask);
- void init_angle_backend(void* dso, egl_connection_t* cnx);
+ void attempt_to_init_angle_backend(void* dso, egl_connection_t* cnx);
static __attribute__((noinline)) void init_api(void* dso, const char* const* api,
const char* const* ref_api,
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 525fed1..3317347 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -191,7 +191,7 @@
if (cnx->dso) {
EGLDisplay dpy = EGL_NO_DISPLAY;
- if (cnx->useAngle) {
+ if (cnx->angleLoaded) {
EGLint error;
dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error);
if (error != EGL_NONE) {
@@ -324,7 +324,7 @@
// b/269060366 Conditionally enabled EGL_ANDROID_get_frame_timestamps extension if the
// device's present timestamps are reliable (which may not be the case on emulators).
- if (cnx->useAngle) {
+ if (cnx->angleLoaded) {
if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
}
@@ -432,7 +432,7 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
// If we're using ANGLE reset any custom DisplayPlatform
- if (cnx->useAngle) {
+ if (cnx->angleLoaded) {
angle::resetAnglePlatform(disp.dpy);
}
if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index efbe613..33a77c4 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -84,7 +84,7 @@
if (win != nullptr && connected) {
// NOTE: When using Vulkan backend, the Vulkan runtime makes all the
// native_window_* calls, so don't do them here.
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
native_window_set_buffers_format(win, 0);
if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
ALOGW("EGLNativeWindowType %p disconnect failed", win);
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 48718bb..440eb17 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -49,6 +49,7 @@
#include "egl_trace.h"
using namespace android;
+using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
// ----------------------------------------------------------------------------
@@ -406,7 +407,7 @@
// ----------------------------------------------------------------------------
// Translates EGL color spaces to Android data spaces.
-static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
+static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace, PixelFormat pixelFormat) {
if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
return HAL_DATASPACE_UNKNOWN;
} else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
@@ -424,7 +425,13 @@
} else if (colorspace == EGL_GL_COLORSPACE_BT2020_HLG_EXT) {
return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
} else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
- return HAL_DATASPACE_BT2020_LINEAR;
+ if (pixelFormat == PixelFormat::RGBA_FP16) {
+ return static_cast<android_dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_LINEAR |
+ HAL_DATASPACE_RANGE_EXTENDED);
+ } else {
+ return HAL_DATASPACE_BT2020_LINEAR;
+ }
} else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
return HAL_DATASPACE_BT2020_PQ;
}
@@ -573,8 +580,6 @@
newList.push_back(EGL_NONE);
}
-using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
-
// Gets the native pixel format corrsponding to the passed EGLConfig.
void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
PixelFormat* format) {
@@ -680,7 +685,7 @@
// NOTE: When using Vulkan backend, the Vulkan runtime makes all the
// native_window_* calls, so don't do them here.
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
if (result < 0) {
ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
@@ -699,14 +704,14 @@
std::vector<AttrType> strippedAttribList;
if (!processAttributes<AttrType>(dp, window, attrib_list, &colorSpace, &strippedAttribList)) {
ALOGE("error invalid colorspace: %d", colorSpace);
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
}
return EGL_NO_SURFACE;
}
attrib_list = strippedAttribList.data();
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
int err = native_window_set_buffers_format(window, static_cast<int>(format));
if (err != 0) {
ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
@@ -714,7 +719,7 @@
return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
}
- android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
+ android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace, format);
// Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN.
// HAL_DATASPACE_UNKNOWN is the default value, but it may have changed
// at this point.
@@ -738,7 +743,7 @@
}
// EGLSurface creation failed
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
native_window_set_buffers_format(window, 0);
native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
}
@@ -1349,7 +1354,7 @@
}
}
- if (!s->cnx->useAngle) {
+ if (!s->cnx->angleLoaded) {
if (!sendSurfaceMetadata(s)) {
native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
@@ -1374,7 +1379,7 @@
androidRect.bottom = y;
androidRects.push_back(androidRect);
}
- if (!s->cnx->useAngle) {
+ if (!s->cnx->angleLoaded) {
native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
androidRects.size());
}
@@ -1465,7 +1470,7 @@
int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
if (err != 0) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- } else if (!s->cnx->useAngle) {
+ } else if (!s->cnx->angleLoaded) {
return EGL_TRUE;
} // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
@@ -1479,7 +1484,7 @@
int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
if (err != 0) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- } else if (!s->cnx->useAngle) {
+ } else if (!s->cnx->angleLoaded) {
return EGL_TRUE;
} // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index fcc11f1..3bd37cb 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -41,7 +41,8 @@
libEgl(nullptr),
libGles1(nullptr),
libGles2(nullptr),
- systemDriverUnloaded(false) {
+ systemDriverUnloaded(false),
+ angleLoaded(false) {
const char* const* entries = platform_names;
EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
while (*entries) {
@@ -73,7 +74,7 @@
void* libGles2;
bool systemDriverUnloaded;
- bool useAngle; // Was ANGLE successfully loaded
+ bool angleLoaded; // Was ANGLE successfully loaded
};
extern gl_hooks_t gHooks[2];
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index fba64c7..052efb6b 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -71,6 +71,7 @@
cc_library_shared {
name: "libgpuservice",
defaults: ["libgpuservice_production_defaults"],
+ export_include_dirs: ["include"],
srcs: [
":libgpuservice_sources",
],
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 5e7b2e8..4a08c11 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -16,7 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "GpuService.h"
+#include "gpuservice/GpuService.h"
#include <android-base/stringprintf.h>
#include <android-base/properties.h>
@@ -35,6 +35,7 @@
#include <vkjson.h>
#include <thread>
+#include <memory>
namespace android {
@@ -58,18 +59,21 @@
mGpuStats(std::make_unique<GpuStats>()),
mGpuMemTracer(std::make_unique<GpuMemTracer>()) {
- std::thread gpuMemAsyncInitThread([this]() {
+ mGpuMemAsyncInitThread = std::make_unique<std::thread>([this] (){
mGpuMem->initialize();
mGpuMemTracer->initialize(mGpuMem);
});
- gpuMemAsyncInitThread.detach();
- std::thread gpuWorkAsyncInitThread([this]() {
+ mGpuWorkAsyncInitThread = std::make_unique<std::thread>([this]() {
mGpuWork->initialize();
});
- gpuWorkAsyncInitThread.detach();
};
+GpuService::~GpuService() {
+ mGpuWorkAsyncInitThread->join();
+ mGpuMemAsyncInitThread->join();
+}
+
void GpuService::setGpuStats(const std::string& driverPackageName,
const std::string& driverVersionName, uint64_t driverVersionCode,
int64_t driverBuildTime, const std::string& appPackageName,
diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS
index 0ff65bf..07c681f 100644
--- a/services/gpuservice/OWNERS
+++ b/services/gpuservice/OWNERS
@@ -4,3 +4,4 @@
lfy@google.com
paulthomson@google.com
pbaiget@google.com
+kocdemir@google.com
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h
similarity index 95%
rename from services/gpuservice/GpuService.h
rename to services/gpuservice/include/gpuservice/GpuService.h
index 0e559f2..54f8f66 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/include/gpuservice/GpuService.h
@@ -24,6 +24,7 @@
#include <serviceutils/PriorityDumper.h>
#include <mutex>
+#include <thread>
#include <vector>
namespace android {
@@ -41,6 +42,7 @@
static const char* const SERVICE_NAME ANDROID_API;
GpuService() ANDROID_API;
+ ~GpuService();
protected:
status_t shellCommand(int in, int out, int err, std::vector<String16>& args) override;
@@ -90,6 +92,8 @@
std::unique_ptr<GpuMemTracer> mGpuMemTracer;
std::mutex mLock;
std::string mDeveloperDriverPath;
+ std::unique_ptr<std::thread> mGpuMemAsyncInitThread;
+ std::unique_ptr<std::thread> mGpuWorkAsyncInitThread;
};
} // namespace android
diff --git a/services/gpuservice/main_gpuservice.cpp b/services/gpuservice/main_gpuservice.cpp
index 64aafca..2002372 100644
--- a/services/gpuservice/main_gpuservice.cpp
+++ b/services/gpuservice/main_gpuservice.cpp
@@ -18,7 +18,7 @@
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <sys/resource.h>
-#include "GpuService.h"
+#include "gpuservice/GpuService.h"
using namespace android;
diff --git a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
index c2574a3..241b864 100644
--- a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
+++ b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
@@ -16,7 +16,7 @@
#include <fuzzbinder/libbinder_driver.h>
-#include "GpuService.h"
+#include "gpuservice/GpuService.h"
using ::android::fuzzService;
using ::android::GpuService;
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 51642f9..c870b17 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -28,6 +28,7 @@
"GpuMemTest.cpp",
"GpuMemTracerTest.cpp",
"GpuStatsTest.cpp",
+ "GpuServiceTest.cpp",
],
header_libs: ["bpf_headers"],
shared_libs: [
@@ -45,6 +46,7 @@
"libstatslog",
"libstatspull",
"libutils",
+ "libgpuservice",
],
static_libs: [
"libgmock",
diff --git a/services/gpuservice/tests/unittests/GpuServiceTest.cpp b/services/gpuservice/tests/unittests/GpuServiceTest.cpp
new file mode 100644
index 0000000..62b3e53
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuServiceTest.cpp
@@ -0,0 +1,52 @@
+#undef LOG_TAG
+#define LOG_TAG "gpuservice_unittest"
+
+#include "gpuservice/GpuService.h"
+
+#include <gtest/gtest.h>
+#include <log/log_main.h>
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace {
+
+class GpuServiceTest : public testing::Test {
+public:
+ GpuServiceTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~GpuServiceTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+};
+
+
+/*
+* The behaviour before this test + fixes was UB caused by threads accessing deallocated memory.
+*
+* This test creates the service (which initializes the culprit threads),
+* deallocates it immediately and sleeps.
+*
+* GpuService's destructor gets called and joins the threads.
+* If we haven't crashed by the time the sleep time has elapsed, we're good
+* Let the test pass.
+*/
+TEST_F(GpuServiceTest, onInitializeShouldNotCauseUseAfterFree) {
+ sp<GpuService> service = new GpuService();
+ service.clear();
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+
+ // If we haven't crashed yet due to threads accessing freed up memory, let the test pass
+ EXPECT_TRUE(true);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index ffcc967..18f6dbc 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -28,6 +28,7 @@
cc_defaults {
name: "inputflinger_defaults",
+ host_supported: true,
cpp_std: "c++20",
cflags: [
"-Wall",
@@ -50,6 +51,20 @@
"-*", // Disable all checks not explicitly enabled for now
] + inputflinger_tidy_checks,
tidy_checks_as_errors: inputflinger_tidy_checks,
+ target: {
+ host: {
+ sanitize: {
+ address: true,
+ },
+ include_dirs: [
+ "bionic/libc/kernel/android/uapi/",
+ "bionic/libc/kernel/uapi",
+ ],
+ cflags: [
+ "-D__ANDROID_HOST__",
+ ],
+ },
+ },
}
/////////////////////////////////////////////////
@@ -79,6 +94,7 @@
"libcrypto",
"libcutils",
"libhidlbase",
+ "libinput",
"libkll",
"liblog",
"libprotobuf-cpp-lite",
@@ -95,14 +111,12 @@
android: {
shared_libs: [
"libgui",
- "libinput",
"libstatspull",
"libstatssocket",
],
},
host: {
static_libs: [
- "libinput",
"libstatspull",
"libstatssocket",
],
@@ -112,6 +126,7 @@
cc_library_shared {
name: "libinputflinger",
+ host_supported: true,
defaults: [
"inputflinger_defaults",
"libinputflinger_defaults",
@@ -172,25 +187,13 @@
"libbase",
"libbinder",
"libcutils",
+ "libinput",
"liblog",
"libutils",
],
header_libs: [
"libinputflinger_headers",
],
- target: {
- android: {
- shared_libs: [
- "libinput",
- ],
- },
- host: {
- static_libs: [
- "libinput",
- "libui-types",
- ],
- },
- },
}
cc_library_shared {
@@ -217,6 +220,7 @@
// native targets
"libgui_test",
"libinput",
+ "libinputreader_static",
"libinputflinger",
"inputflinger_tests",
"inputflinger_benchmarks",
@@ -230,18 +234,22 @@
"inputflinger",
"libinputflingerhost",
+ // rust targets
+ "libinput_rust_test",
+
// native fuzzers
"inputflinger_latencytracker_fuzzer",
"inputflinger_cursor_input_fuzzer",
"inputflinger_keyboard_input_fuzzer",
"inputflinger_multitouch_input_fuzzer",
"inputflinger_switch_input_fuzzer",
+ "inputflinger_touchpad_input_fuzzer",
"inputflinger_input_reader_fuzzer",
"inputflinger_blocking_queue_fuzzer",
"inputflinger_input_classifier_fuzzer",
// Java/Kotlin targets
- "CtsWindowManagerDeviceTestCases",
+ "CtsWindowManagerDeviceWindow",
"InputTests",
"CtsHardwareTestCases",
"CtsInputTestCases",
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
index 7812880..6ccd9e7 100644
--- a/services/inputflinger/InputCommonConverter.cpp
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -289,9 +289,9 @@
static void getHalPropertiesAndCoords(const NotifyMotionArgs& args,
std::vector<common::PointerProperties>& outPointerProperties,
std::vector<common::PointerCoords>& outPointerCoords) {
- outPointerProperties.reserve(args.pointerCount);
- outPointerCoords.reserve(args.pointerCount);
- for (size_t i = 0; i < args.pointerCount; i++) {
+ outPointerProperties.reserve(args.getPointerCount());
+ outPointerCoords.reserve(args.getPointerCount());
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
common::PointerProperties properties;
properties.id = args.pointerProperties[i].id;
properties.toolType = getToolType(args.pointerProperties[i].toolType);
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 3e25cc3..7c99a1c 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -27,10 +27,11 @@
using android::base::StringPrintf;
using std::chrono::nanoseconds;
+using std::chrono_literals::operator""ns;
namespace {
-constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::seconds(5);
+constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::minutes(2);
/**
* Log debug messages about metrics events logged to statsd.
@@ -38,7 +39,12 @@
*/
const bool DEBUG = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
+constexpr size_t INTERACTIONS_QUEUE_CAPACITY = 500;
+
int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus) {
+ // When adding cases to this switch, also add them to the copy of this method in
+ // TouchpadInputMapper.cpp.
+ // TODO(b/286394420): deduplicate this method with the one in TouchpadInputMapper.cpp.
switch (linuxBus) {
case BUS_USB:
return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
@@ -72,10 +78,20 @@
ftl::enum_string(src).c_str(), durMillis);
}
+ ALOGD_IF(DEBUG, " Uid breakdown:");
+
+ std::vector<int32_t> uids;
+ std::vector<int32_t> durationsPerUid;
+ for (auto& [uid, dur] : report.uidBreakdown) {
+ uids.push_back(uid.val());
+ int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
+ durationsPerUid.push_back(durMillis);
+ ALOGD_IF(DEBUG, " - uid: %s\t duration: %dms", uid.toString().c_str(),
+ durMillis);
+ }
util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
- durationMillis, sources, durationsPerSource, /*uids=*/empty,
- /*usage_durations_per_uid=*/empty);
+ durationMillis, sources, durationsPerSource, uids, durationsPerUid);
}
} sStatsdLogger;
@@ -115,10 +131,10 @@
}
std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
- LOG_ALWAYS_FATAL_IF(motionArgs.pointerCount < 1, "Received motion args without pointers");
+ LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
std::set<InputDeviceUsageSource> sources;
- for (uint32_t i = 0; i < motionArgs.pointerCount; i++) {
+ for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
const auto toolType = motionArgs.pointerProperties[i].toolType;
if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
if (toolType == ToolType::MOUSE) {
@@ -187,7 +203,10 @@
InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
InputDeviceMetricsLogger& logger,
nanoseconds usageSessionTimeout)
- : mNextListener(listener), mLogger(logger), mUsageSessionTimeout(usageSessionTimeout) {}
+ : mNextListener(listener),
+ mLogger(logger),
+ mUsageSessionTimeout(usageSessionTimeout),
+ mInteractionsQueue(INTERACTIONS_QUEUE_CAPACITY) {}
void InputDeviceMetricsCollector::notifyInputDevicesChanged(
const NotifyInputDevicesChangedArgs& args) {
@@ -246,6 +265,14 @@
mNextListener.notify(args);
}
+void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<Uid>& uids) {
+ if (isIgnoredInputDeviceId(deviceId)) {
+ return;
+ }
+ mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
+}
+
void InputDeviceMetricsCollector::dump(std::string& dump) {
dump += "InputDeviceMetricsCollector:\n";
@@ -304,17 +331,33 @@
}
}
-void InputDeviceMetricsCollector::reportCompletedSessions() {
- const auto currentTime = mLogger.getCurrentTime();
+void InputDeviceMetricsCollector::onInputDeviceInteraction(const Interaction& interaction) {
+ auto activeSessionIt = mActiveUsageSessions.find(std::get<DeviceId>(interaction));
+ if (activeSessionIt == mActiveUsageSessions.end()) {
+ return;
+ }
+ activeSessionIt->second.recordInteraction(interaction);
+}
+
+void InputDeviceMetricsCollector::reportCompletedSessions() {
+ // Process all pending interactions.
+ for (auto interaction = mInteractionsQueue.pop(); interaction;
+ interaction = mInteractionsQueue.pop()) {
+ onInputDeviceInteraction(*interaction);
+ }
+
+ const auto currentTime = mLogger.getCurrentTime();
std::vector<DeviceId> completedUsageSessions;
+ // Process usages for all active session to determine if any sessions have expired.
for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
if (activeSession.checkIfCompletedAt(currentTime)) {
completedUsageSessions.emplace_back(deviceId);
}
}
+ // Close out and log all expired usage sessions.
for (DeviceId deviceId : completedUsageSessions) {
const auto infoIt = mLoggedDeviceInfos.find(deviceId);
LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
@@ -346,6 +389,23 @@
mDeviceSession.end = eventTime;
}
+void InputDeviceMetricsCollector::ActiveSession::recordInteraction(const Interaction& interaction) {
+ const auto sessionExpiryTime = mDeviceSession.end + mUsageSessionTimeout;
+ const auto timestamp = std::get<nanoseconds>(interaction);
+ if (timestamp >= sessionExpiryTime) {
+ // This interaction occurred after the device's current active session is set to expire.
+ // Ignore it.
+ return;
+ }
+
+ for (Uid uid : std::get<std::set<Uid>>(interaction)) {
+ auto [activeUidIt, inserted] = mActiveSessionsByUid.try_emplace(uid, timestamp, timestamp);
+ if (!inserted) {
+ activeUidIt->second.end = timestamp;
+ }
+ }
+}
+
bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
@@ -360,6 +420,21 @@
mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
mActiveSessionsBySource.erase(it);
}
+
+ std::vector<Uid> completedUidSessionsForDevice;
+ for (auto& [uid, session] : mActiveSessionsByUid) {
+ if (session.end <= sessionExpiryTime) {
+ completedUidSessionsForDevice.emplace_back(uid);
+ }
+ }
+ for (Uid uid : completedUidSessionsForDevice) {
+ auto it = mActiveSessionsByUid.find(uid);
+ const auto& [_, session] = *it;
+ mUidUsageBreakdown.emplace_back(uid, session.end - session.start);
+ mActiveSessionsByUid.erase(it);
+ }
+
+ // This active session has expired if there are no more active source sessions tracked.
return mActiveSessionsBySource.empty();
}
@@ -372,7 +447,12 @@
}
mActiveSessionsBySource.clear();
- return {deviceUsageDuration, mSourceUsageBreakdown};
+ for (const auto& [uid, uidSession] : mActiveSessionsByUid) {
+ mUidUsageBreakdown.emplace_back(uid, uidSession.end - uidSession.start);
+ }
+ mActiveSessionsByUid.clear();
+
+ return {deviceUsageDuration, mSourceUsageBreakdown, mUidUsageBreakdown};
}
} // namespace android
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index e2e79e4..c70e6d4 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -18,8 +18,10 @@
#include "InputListener.h"
#include "NotifyArgs.h"
+#include "SyncQueue.h"
#include <ftl/mixins.h>
+#include <gui/WindowInfo.h>
#include <input/InputDevice.h>
#include <statslog.h>
#include <chrono>
@@ -33,11 +35,17 @@
/**
* Logs metrics about registered input devices and their usages.
*
- * Not thread safe. Must be called from a single thread.
+ * All methods in the InputListenerInterface must be called from a single thread.
*/
class InputDeviceMetricsCollectorInterface : public InputListenerInterface {
public:
/**
+ * Notify the metrics collector that there was an input device interaction with apps.
+ * Called from the InputDispatcher thread.
+ */
+ virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) = 0;
+ /**
* Dump the state of the interaction blocker.
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
@@ -92,9 +100,14 @@
using SourceUsageBreakdown =
std::vector<std::pair<InputDeviceUsageSource, std::chrono::nanoseconds /*duration*/>>;
+ // Describes the breakdown of an input device usage session by the UIDs that it interacted with.
+ using UidUsageBreakdown =
+ std::vector<std::pair<gui::Uid, std::chrono::nanoseconds /*duration*/>>;
+
struct DeviceUsageReport {
std::chrono::nanoseconds usageDuration;
SourceUsageBreakdown sourceBreakdown;
+ UidUsageBreakdown uidBreakdown;
};
virtual void logInputDeviceUsageReported(const InputDeviceIdentifier&,
@@ -121,6 +134,8 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+ void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) override;
void dump(std::string& dump) override;
private:
@@ -138,13 +153,19 @@
return std::to_string(ftl::to_underlying(id));
}
+ using Uid = gui::Uid;
+
std::map<DeviceId, InputDeviceInfo> mLoggedDeviceInfos;
+ using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>;
+ SyncQueue<Interaction> mInteractionsQueue;
+
class ActiveSession {
public:
explicit ActiveSession(std::chrono::nanoseconds usageSessionTimeout,
std::chrono::nanoseconds startTime);
void recordUsage(std::chrono::nanoseconds eventTime, InputDeviceUsageSource source);
+ void recordInteraction(const Interaction&);
bool checkIfCompletedAt(std::chrono::nanoseconds timestamp);
InputDeviceMetricsLogger::DeviceUsageReport finishSession();
@@ -159,6 +180,9 @@
std::map<InputDeviceUsageSource, UsageSession> mActiveSessionsBySource{};
InputDeviceMetricsLogger::SourceUsageBreakdown mSourceUsageBreakdown{};
+
+ std::map<Uid, UsageSession> mActiveSessionsByUid{};
+ InputDeviceMetricsLogger::UidUsageBreakdown mUidUsageBreakdown{};
};
// The input devices that currently have active usage sessions.
@@ -169,6 +193,7 @@
using SourceProvider = std::function<std::set<InputDeviceUsageSource>(const InputDeviceInfo&)>;
void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
const SourceProvider& getSources);
+ void onInputDeviceInteraction(const Interaction&);
void reportCompletedSessions();
};
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 863b483..c903564 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -34,7 +34,7 @@
namespace android {
static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
- sysprop::InputProperties::enable_input_device_usage_metrics().value_or(false);
+ sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
using gui::FocusRequest;
@@ -127,6 +127,10 @@
return *mProcessor;
}
+InputDeviceMetricsCollectorInterface& InputManager::getMetricsCollector() {
+ return *mCollector;
+}
+
InputDispatcherInterface& InputManager::getDispatcher() {
return *mDispatcher;
}
@@ -182,7 +186,7 @@
dump += " InputFlinger dump\n";
- ::write(fd, dump.c_str(), dump.size());
+ TEMP_FAILURE_RETRY(::write(fd, dump.c_str(), dump.size()));
return NO_ERROR;
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 0f0d8ea..9dc285f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -86,6 +86,9 @@
/* Gets the input processor. */
virtual InputProcessorInterface& getProcessor() = 0;
+ /* Gets the metrics collector. */
+ virtual InputDeviceMetricsCollectorInterface& getMetricsCollector() = 0;
+
/* Gets the input dispatcher. */
virtual InputDispatcherInterface& getDispatcher() = 0;
@@ -109,6 +112,7 @@
InputReaderInterface& getReader() override;
InputProcessorInterface& getProcessor() override;
+ InputDeviceMetricsCollectorInterface& getMetricsCollector() override;
InputDispatcherInterface& getDispatcher() override;
void monitor() override;
void dump(std::string& dump) override;
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index 408fbed..c34cd53 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -83,7 +83,6 @@
buttonState(buttonState),
classification(classification),
edgeFlags(edgeFlags),
- pointerCount(pointerCount),
xPrecision(xPrecision),
yPrecision(yPrecision),
xCursorPosition(xCursorPosition),
@@ -92,36 +91,8 @@
readTime(readTime),
videoFrames(videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties[i].copyFrom(pointerProperties[i]);
- this->pointerCoords[i].copyFrom(pointerCoords[i]);
- }
-}
-
-NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
- : id(other.id),
- eventTime(other.eventTime),
- deviceId(other.deviceId),
- source(other.source),
- displayId(other.displayId),
- policyFlags(other.policyFlags),
- action(other.action),
- actionButton(other.actionButton),
- flags(other.flags),
- metaState(other.metaState),
- buttonState(other.buttonState),
- classification(other.classification),
- edgeFlags(other.edgeFlags),
- pointerCount(other.pointerCount),
- xPrecision(other.xPrecision),
- yPrecision(other.yPrecision),
- xCursorPosition(other.xCursorPosition),
- yCursorPosition(other.yCursorPosition),
- downTime(other.downTime),
- readTime(other.readTime),
- videoFrames(other.videoFrames) {
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(other.pointerProperties[i]);
- pointerCoords[i].copyFrom(other.pointerCoords[i]);
+ this->pointerProperties.emplace_back(pointerProperties[i]);
+ this->pointerCoords.emplace_back(pointerCoords[i]);
}
}
@@ -130,35 +101,22 @@
}
bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
- bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+ return id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
policyFlags == rhs.policyFlags && action == rhs.action &&
actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
buttonState == rhs.buttonState && classification == rhs.classification &&
- edgeFlags == rhs.edgeFlags &&
- pointerCount == rhs.pointerCount
- // PointerProperties and PointerCoords are compared separately below
- && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
+ edgeFlags == rhs.edgeFlags && pointerProperties == rhs.pointerProperties &&
+ pointerCoords == rhs.pointerCoords && xPrecision == rhs.xPrecision &&
+ yPrecision == rhs.yPrecision &&
isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
downTime == rhs.downTime && videoFrames == rhs.videoFrames;
- if (!equal) {
- return false;
- }
-
- for (size_t i = 0; i < pointerCount; i++) {
- equal = pointerProperties[i] == rhs.pointerProperties[i] &&
- pointerCoords[i] == rhs.pointerCoords[i];
- if (!equal) {
- return false;
- }
- }
- return true;
}
std::string NotifyMotionArgs::dump() const {
std::string coords;
- for (uint32_t i = 0; i < pointerCount; i++) {
+ for (uint32_t i = 0; i < getPointerCount(); i++) {
if (!coords.empty()) {
coords += ", ";
}
@@ -181,11 +139,10 @@
coords += "}";
}
return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
- ", source=%s, action=%s, pointerCount=%" PRIu32
- " pointers=%s, flags=0x%08x)",
+ ", source=%s, action=%s, pointerCount=%zu pointers=%s, flags=0x%08x)",
id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
- MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
- flags);
+ MotionEvent::actionToString(action).c_str(), getPointerCount(),
+ coords.c_str(), flags);
}
// --- NotifySwitchArgs ---
diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS
index c88bfe9..21d208f 100644
--- a/services/inputflinger/OWNERS
+++ b/services/inputflinger/OWNERS
@@ -1 +1,2 @@
+# Bug component: 136048
include platform/frameworks/base:/INPUT_OWNERS
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
index fbd296c..ee0ab33 100644
--- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -22,7 +22,7 @@
static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) {
bool hasStylus = false;
bool hasTouch = false;
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
// Make sure we are canceling stylus pointers
const ToolType toolType = args.pointerProperties[i].toolType;
if (isStylusToolType(toolType)) {
diff --git a/services/inputflinger/SyncQueue.h b/services/inputflinger/SyncQueue.h
index 62efd55..84ccace 100644
--- a/services/inputflinger/SyncQueue.h
+++ b/services/inputflinger/SyncQueue.h
@@ -27,6 +27,10 @@
template <class T>
class SyncQueue {
public:
+ SyncQueue() = default;
+
+ SyncQueue(size_t capacity) : mCapacity(capacity) {}
+
/** Retrieve and remove the oldest object. Returns std::nullopt if the queue is empty. */
std::optional<T> pop() {
std::scoped_lock lock(mLock);
@@ -38,14 +42,23 @@
return t;
};
- /** Add a new object to the queue. */
+ /**
+ * Add a new object to the queue.
+ * Return true if an element was successfully added.
+ * Return false if the queue is full.
+ */
template <class... Args>
- void push(Args&&... args) {
+ bool push(Args&&... args) {
std::scoped_lock lock(mLock);
+ if (mCapacity && mQueue.size() == mCapacity) {
+ return false;
+ }
mQueue.emplace_back(args...);
+ return true;
};
private:
+ const std::optional<size_t> mCapacity;
std::mutex mLock;
std::list<T> mQueue GUARDED_BY(mLock);
};
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 68af9b8..c2da5ba 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -1,10 +1,10 @@
{
"presubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceWindow",
"options": [
{
- "include-filter": "android.server.wm.WindowInputTests"
+ "include-filter": "android.server.wm.window.WindowInputTests"
}
]
},
@@ -49,15 +49,38 @@
"name": "CtsViewTestCases",
"options": [
{
- "include-filter": "android.view.cts.input",
- "include-filter": "android.view.cts.HoverTest",
- "include-filter": "android.view.cts.MotionEventTest",
- "include-filter": "android.view.cts.PointerCaptureTest",
- "include-filter": "android.view.cts.TooltipTest",
- "include-filter": "android.view.cts.TouchDelegateTest",
- "include-filter": "android.view.cts.VelocityTrackerTest",
- "include-filter": "android.view.cts.VerifyInputEventTest",
- "include-filter": "android.view.cts.ViewTest",
+ "include-filter": "android.view.cts.input"
+ }
+ ]
+ },
+ {
+ "name": "CtsViewTestCases",
+ "options": [
+ {
+ "include-filter": "android.view.cts.HoverTest"
+ },
+ {
+ "include-filter": "android.view.cts.MotionEventTest"
+ },
+ {
+ "include-filter": "android.view.cts.PointerCaptureTest"
+ },
+ {
+ "include-filter": "android.view.cts.TooltipTest"
+ },
+ {
+ "include-filter": "android.view.cts.TouchDelegateTest"
+ },
+ {
+ "include-filter": "android.view.cts.VelocityTrackerTest"
+ },
+ {
+ "include-filter": "android.view.cts.VerifyInputEventTest"
+ },
+ {
+ "include-filter": "android.view.cts.ViewTest"
+ },
+ {
"include-filter": "android.view.cts.ViewUnbufferedTest"
}
]
@@ -66,7 +89,9 @@
"name": "CtsWidgetTestCases",
"options": [
{
- "include-filter": "android.widget.cts.NumberPickerTest",
+ "include-filter": "android.widget.cts.NumberPickerTest"
+ },
+ {
"include-filter": "android.widget.cts.SeekBarTest"
}
]
@@ -75,8 +100,17 @@
"name": "FrameworksCoreTests",
"options": [
{
- "include-filter": "android.hardware.input",
- "include-filter": "android.view.VerifiedKeyEventTest",
+ "include-filter": "android.hardware.input"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.VerifiedKeyEventTest"
+ },
+ {
"include-filter": "android.view.VerifiedMotionEventTest"
}
]
@@ -116,10 +150,10 @@
],
"hwasan-postsubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceWindow",
"options": [
{
- "include-filter": "android.server.wm.WindowInputTests"
+ "include-filter": "android.server.wm.window.WindowInputTests"
}
]
},
@@ -161,15 +195,38 @@
"name": "CtsViewTestCases",
"options": [
{
- "include-filter": "android.view.cts.input",
- "include-filter": "android.view.cts.HoverTest",
- "include-filter": "android.view.cts.MotionEventTest",
- "include-filter": "android.view.cts.PointerCaptureTest",
- "include-filter": "android.view.cts.TooltipTest",
- "include-filter": "android.view.cts.TouchDelegateTest",
- "include-filter": "android.view.cts.VelocityTrackerTest",
- "include-filter": "android.view.cts.VerifyInputEventTest",
- "include-filter": "android.view.cts.ViewTest",
+ "include-filter": "android.view.cts.input"
+ }
+ ]
+ },
+ {
+ "name": "CtsViewTestCases",
+ "options": [
+ {
+ "include-filter": "android.view.cts.HoverTest"
+ },
+ {
+ "include-filter": "android.view.cts.MotionEventTest"
+ },
+ {
+ "include-filter": "android.view.cts.PointerCaptureTest"
+ },
+ {
+ "include-filter": "android.view.cts.TooltipTest"
+ },
+ {
+ "include-filter": "android.view.cts.TouchDelegateTest"
+ },
+ {
+ "include-filter": "android.view.cts.VelocityTrackerTest"
+ },
+ {
+ "include-filter": "android.view.cts.VerifyInputEventTest"
+ },
+ {
+ "include-filter": "android.view.cts.ViewTest"
+ },
+ {
"include-filter": "android.view.cts.ViewUnbufferedTest"
}
]
@@ -178,7 +235,9 @@
"name": "CtsWidgetTestCases",
"options": [
{
- "include-filter": "android.widget.cts.NumberPickerTest",
+ "include-filter": "android.widget.cts.NumberPickerTest"
+ },
+ {
"include-filter": "android.widget.cts.SeekBarTest"
}
]
@@ -187,7 +246,9 @@
"name": "FrameworksCoreTests",
"options": [
{
- "include-filter": "android.view.VerifiedKeyEventTest",
+ "include-filter": "android.view.VerifiedKeyEventTest"
+ },
+ {
"include-filter": "android.view.VerifiedMotionEventTest"
}
]
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 02bc47d..f889de5 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -117,7 +117,7 @@
}
static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
if (pointerId == args.pointerProperties[i].id) {
return AMOTION_EVENT_ACTION_POINTER_UP |
(i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
@@ -156,9 +156,10 @@
actionMasked == AMOTION_EVENT_ACTION_POINTER_UP;
NotifyMotionArgs newArgs{args};
- newArgs.pointerCount = 0;
+ newArgs.pointerProperties.clear();
+ newArgs.pointerCoords.clear();
int32_t newActionIndex = 0;
- for (uint32_t i = 0; i < args.pointerCount; i++) {
+ for (uint32_t i = 0; i < args.getPointerCount(); i++) {
const int32_t pointerId = args.pointerProperties[i].id;
if (pointerIds.find(pointerId) != pointerIds.end()) {
// skip this pointer
@@ -170,19 +171,18 @@
}
continue;
}
- newArgs.pointerProperties[newArgs.pointerCount].copyFrom(args.pointerProperties[i]);
- newArgs.pointerCoords[newArgs.pointerCount].copyFrom(args.pointerCoords[i]);
+ newArgs.pointerProperties.push_back(args.pointerProperties[i]);
+ newArgs.pointerCoords.push_back(args.pointerCoords[i]);
if (i == actionIndex) {
- newActionIndex = newArgs.pointerCount;
+ newActionIndex = newArgs.getPointerCount() - 1;
}
- newArgs.pointerCount++;
}
// Update POINTER_DOWN or POINTER_UP actions
if (isPointerUpOrDownAction && newArgs.action != ACTION_UNKNOWN) {
newArgs.action =
actionMasked | (newActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
// Convert POINTER_DOWN and POINTER_UP to DOWN and UP if there's only 1 pointer remaining
- if (newArgs.pointerCount == 1) {
+ if (newArgs.getPointerCount() == 1) {
if (actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) {
newArgs.action = AMOTION_EVENT_ACTION_DOWN;
} else if (actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) {
@@ -201,13 +201,14 @@
*/
static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
std::set<int32_t> stylusPointerIds;
- for (uint32_t i = 0; i < args.pointerCount; i++) {
+ for (uint32_t i = 0; i < args.getPointerCount(); i++) {
if (isStylusToolType(args.pointerProperties[i].toolType)) {
stylusPointerIds.insert(args.pointerProperties[i].id);
}
}
NotifyMotionArgs withoutStylusPointers = removePointerIds(args, stylusPointerIds);
- if (withoutStylusPointers.pointerCount == 0 || withoutStylusPointers.action == ACTION_UNKNOWN) {
+ if (withoutStylusPointers.getPointerCount() == 0 ||
+ withoutStylusPointers.action == ACTION_UNKNOWN) {
return std::nullopt;
}
return withoutStylusPointers;
@@ -272,7 +273,7 @@
std::vector<NotifyMotionArgs> cancelSuppressedPointers(
const NotifyMotionArgs& args, const std::set<int32_t>& oldSuppressedPointerIds,
const std::set<int32_t>& newSuppressedPointerIds) {
- LOG_ALWAYS_FATAL_IF(args.pointerCount == 0, "0 pointers in %s", args.dump().c_str());
+ LOG_ALWAYS_FATAL_IF(args.getPointerCount() == 0, "0 pointers in %s", args.dump().c_str());
// First, let's remove the old suppressed pointers. They've already been canceled previously.
NotifyMotionArgs oldArgs = removePointerIds(args, oldSuppressedPointerIds);
@@ -284,7 +285,7 @@
const int32_t actionMasked = MotionEvent::getActionMasked(args.action);
// We will iteratively remove pointers from 'removedArgs'.
NotifyMotionArgs removedArgs{oldArgs};
- for (uint32_t i = 0; i < oldArgs.pointerCount; i++) {
+ for (uint32_t i = 0; i < oldArgs.getPointerCount(); i++) {
const int32_t pointerId = oldArgs.pointerProperties[i].id;
if (newSuppressedPointerIds.find(pointerId) == newSuppressedPointerIds.end()) {
// This is a pointer that should not be canceled. Move on.
@@ -296,7 +297,7 @@
continue;
}
- if (removedArgs.pointerCount == 1) {
+ if (removedArgs.getPointerCount() == 1) {
// We are about to remove the last pointer, which means there will be no more gesture
// remaining. This is identical to canceling all pointers, so just send a single CANCEL
// event, without any of the preceding POINTER_UP with FLAG_CANCELED events.
@@ -314,7 +315,7 @@
}
// Now 'removedArgs' contains only pointers that are valid.
- if (removedArgs.pointerCount <= 0 || removedArgs.action == ACTION_UNKNOWN) {
+ if (removedArgs.getPointerCount() <= 0 || removedArgs.action == ACTION_UNKNOWN) {
return out;
}
out.push_back(removedArgs);
@@ -473,7 +474,7 @@
UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {}
void SlotState::update(const NotifyMotionArgs& args) {
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
const int32_t pointerId = args.pointerProperties[i].id;
const int32_t resolvedAction = resolveActionForPointer(i, args.action);
processPointerId(pointerId, resolvedAction);
@@ -571,7 +572,7 @@
const SlotState& newSlotState) {
std::vector<::ui::InProgressTouchEvdev> touches;
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
const int32_t pointerId = args.pointerProperties[i].id;
touches.emplace_back(::ui::InProgressTouchEvdev());
touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
@@ -660,7 +661,7 @@
// Now that we know which slots should be suppressed, let's convert those to pointer id's.
std::set<int32_t> newSuppressedIds;
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
const int32_t pointerId = args.pointerProperties[i].id;
std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId);
if (!slot) {
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 4e2a6fb..e200f8b 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -19,6 +19,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcrypto",
"libcutils",
"libinputflinger_base",
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index f65533e..6dd785a 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -30,12 +30,14 @@
namespace android::inputdispatcher {
+namespace {
+
// An arbitrary device id.
constexpr int32_t DEVICE_ID = 1;
// The default pid and uid for windows created by the test.
-constexpr int32_t WINDOW_PID = 999;
-constexpr int32_t WINDOW_UID = 1001;
+constexpr gui::Pid WINDOW_PID{999};
+constexpr gui::Uid WINDOW_UID{1001};
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
@@ -59,13 +61,13 @@
ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
}
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid,
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
const std::string& reason) override {
ALOGE("Window is not responding: %s", reason.c_str());
}
void notifyWindowResponsive(const sp<IBinder>& connectionToken,
- std::optional<int32_t> pid) override {}
+ std::optional<gui::Pid> pid) override {}
void notifyInputChannelBroken(const sp<IBinder>&) override {}
@@ -80,8 +82,6 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override {}
- InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
-
bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
return true; // dispatch event normally
}
@@ -109,6 +109,9 @@
void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
+ void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) override {}
+
InputDispatcherConfiguration mConfig;
};
@@ -265,7 +268,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
- dispatcher.setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
NotifyMotionArgs motionArgs = generateMotionArgs();
@@ -300,7 +303,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
- dispatcher.setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
for (auto _ : state) {
MotionEvent event = generateMotionEvent();
@@ -348,6 +351,8 @@
dispatcher.stop();
}
+} // namespace
+
BENCHMARK(benchmarkNotifyMotion);
BENCHMARK(benchmarkInjectMotion);
BENCHMARK(benchmarkOnWindowInfosChanged);
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 492551e..8b57730 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -59,6 +59,7 @@
"libbase",
"libcrypto",
"libcutils",
+ "libinput",
"libkll",
"liblog",
"libprotobuf-cpp-lite",
@@ -74,14 +75,12 @@
android: {
shared_libs: [
"libgui",
- "libinput",
"libstatspull",
"libstatssocket",
],
},
host: {
static_libs: [
- "libinput",
"libstatspull",
"libstatssocket",
],
diff --git a/services/inputflinger/dispatcher/DebugConfig.cpp b/services/inputflinger/dispatcher/DebugConfig.cpp
index 764194d..12122fd 100644
--- a/services/inputflinger/dispatcher/DebugConfig.cpp
+++ b/services/inputflinger/dispatcher/DebugConfig.cpp
@@ -30,11 +30,10 @@
bool debugInboundEventDetails() {
if (!IS_DEBUGGABLE_BUILD) {
static const bool DEBUG_INBOUND_EVENT_DETAILS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent",
- ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "InboundEvent");
return DEBUG_INBOUND_EVENT_DETAILS;
}
- return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO);
+ return android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "InboundEvent");
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index 0e260a7..7a41d68 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -18,8 +18,7 @@
#define LOG_TAG "InputDispatcher"
-#include <log/log.h>
-#include <log/log_event_list.h>
+#include <android-base/logging.h>
namespace android::inputdispatcher {
@@ -42,14 +41,14 @@
* Enable this via "adb shell setprop log.tag.InputDispatcherOutboundEvent DEBUG" (requires restart)
*/
const bool DEBUG_OUTBOUND_EVENT_DETAILS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundEvent", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "OutboundEvent");
/**
* Log debug messages about the dispatch cycle.
* Enable this via "adb shell setprop log.tag.InputDispatcherDispatchCycle DEBUG" (requires restart)
*/
const bool DEBUG_DISPATCH_CYCLE =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "DispatchCycle", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "DispatchCycle");
/**
* Log debug messages about channel creation
@@ -57,28 +56,28 @@
* restart)
*/
const bool DEBUG_CHANNEL_CREATION =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "ChannelCreation", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "ChannelCreation");
/**
* Log debug messages about input event injection.
* Enable this via "adb shell setprop log.tag.InputDispatcherInjection DEBUG" (requires restart)
*/
const bool DEBUG_INJECTION =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Injection", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Injection");
/**
* Log debug messages about input focus tracking.
* Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart)
*/
const bool DEBUG_FOCUS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Focus", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Focus");
/**
* Log debug messages about touch mode event
* Enable this via "adb shell setprop log.tag.InputDispatcherTouchMode DEBUG" (requires restart)
*/
const bool DEBUG_TOUCH_MODE =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchMode", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "TouchMode");
/**
* Log debug messages about touch occlusion
@@ -90,13 +89,20 @@
* Enable this via "adb shell setprop log.tag.InputDispatcherAppSwitch DEBUG" (requires restart)
*/
const bool DEBUG_APP_SWITCH =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "AppSwitch", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "AppSwitch");
/**
* Log debug messages about hover events.
* Enable this via "adb shell setprop log.tag.InputDispatcherHover DEBUG" (requires restart)
*/
const bool DEBUG_HOVER =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Hover");
+
+/**
+ * Crash if a bad stream from InputListener is detected.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherVerifyEvents DEBUG" (requires restart)
+ */
+const bool DEBUG_VERIFY_EVENTS =
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "VerifyEvents");
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index b625a1b..cb369a8 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -235,8 +235,8 @@
downTime(downTime),
pointerCount(pointerCount) {
for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties[i].copyFrom(pointerProperties[i]);
- this->pointerCoords[i].copyFrom(pointerCoords[i]);
+ this->pointerProperties[i] = pointerProperties[i];
+ this->pointerCoords[i] = pointerCoords[i];
}
}
@@ -321,7 +321,28 @@
globalScaleFactor(globalScaleFactor),
deliveryTime(0),
resolvedAction(0),
- resolvedFlags(0) {}
+ resolvedFlags(0) {
+ switch (this->eventEntry->type) {
+ case EventEntry::Type::KEY: {
+ const KeyEntry& keyEntry = static_cast<KeyEntry&>(*this->eventEntry);
+ resolvedEventId = keyEntry.id;
+ resolvedAction = keyEntry.action;
+ resolvedFlags = keyEntry.flags;
+
+ break;
+ }
+ case EventEntry::Type::MOTION: {
+ const MotionEntry& motionEntry = static_cast<MotionEntry&>(*this->eventEntry);
+ resolvedEventId = motionEntry.id;
+ resolvedAction = motionEntry.action;
+ resolvedFlags = motionEntry.flags;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
uint32_t DispatchEntry::nextSeq() {
// Sequence number 0 is reserved and will never be returned.
@@ -352,7 +373,7 @@
entry.transform.dump(transform, "transform");
out << ", resolvedFlags=" << entry.resolvedFlags
<< ", targetFlags=" << entry.targetFlags.string() << ", transform=" << transform
- << "} original =" << entry.eventEntry->getDescription();
+ << "} original: " << entry.eventEntry->getDescription();
return out;
}
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index c2d3ad6..053594b 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -20,7 +20,7 @@
namespace android::inputdispatcher {
-InjectionState::InjectionState(const std::optional<int32_t>& targetUid)
+InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid)
: refCount(1),
targetUid(targetUid),
injectionResult(android::os::InputEventInjectionResult::PENDING),
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index d9e27ba..3a3f5ae 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -26,12 +26,12 @@
struct InjectionState {
mutable int32_t refCount;
- std::optional<int32_t> targetUid;
+ std::optional<gui::Uid> targetUid;
android::os::InputEventInjectionResult injectionResult; // initially PENDING
bool injectionIsAsync; // set to true if injection is not waiting for the result
int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
- explicit InjectionState(const std::optional<int32_t>& targetUid);
+ explicit InjectionState(const std::optional<gui::Uid>& targetUid);
void release();
private:
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index aa71e93..2923a3c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -26,6 +26,7 @@
#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
#include <ftl/enum.h>
+#include <log/log_event_list.h>
#if defined(__ANDROID__)
#include <gui/SurfaceComposerClient.h>
#endif
@@ -118,6 +119,10 @@
return systemTime(SYSTEM_TIME_MONOTONIC);
}
+bool isEmpty(const std::stringstream& ss) {
+ return ss.rdbuf()->in_avail() == 0;
+}
+
inline const std::string binderToString(const sp<IBinder>& binder) {
if (binder == nullptr) {
return "<null>";
@@ -125,9 +130,8 @@
return StringPrintf("%p", binder.get());
}
-inline int32_t getMotionEventActionPointerIndex(int32_t action) {
- return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
- AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+static std::string uidString(const gui::Uid& uid) {
+ return uid.toString();
}
Result<void> checkKeyAction(int32_t action) {
@@ -556,7 +560,7 @@
return !info.inputConfig.test(gui::WindowInfo::InputConfig::NOT_TOUCHABLE) && !info.isSpy();
}
-bool isWindowOwnedBy(const sp<WindowInfoHandle>& windowHandle, int32_t pid, int32_t uid) {
+bool isWindowOwnedBy(const sp<WindowInfoHandle>& windowHandle, gui::Pid pid, gui::Uid uid) {
if (windowHandle == nullptr) {
return false;
}
@@ -576,14 +580,16 @@
// The event was not injected, or the injected event does not target a window.
return {};
}
- const int32_t uid = *entry.injectionState->targetUid;
+ const auto uid = *entry.injectionState->targetUid;
if (window == nullptr) {
- return StringPrintf("No valid window target for injection into uid %d.", uid);
+ return StringPrintf("No valid window target for injection into uid %s.",
+ uid.toString().c_str());
}
if (entry.injectionState->targetUid != window->getInfo()->ownerUid) {
- return StringPrintf("Injected event targeted at uid %d would be dispatched to window '%s' "
- "owned by uid %d.",
- uid, window->getName().c_str(), window->getInfo()->ownerUid);
+ return StringPrintf("Injected event targeted at uid %s would be dispatched to window '%s' "
+ "owned by uid %s.",
+ uid.toString().c_str(), window->getName().c_str(),
+ window->getInfo()->ownerUid.toString().c_str());
}
return {};
}
@@ -595,7 +601,7 @@
return {entry.xCursorPosition, entry.yCursorPosition};
}
- const int32_t pointerIndex = getMotionEventActionPointerIndex(entry.action);
+ const int32_t pointerIndex = MotionEvent::getActionIndex(entry.action);
return {entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X),
entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)};
}
@@ -647,7 +653,6 @@
TouchedWindow touchedWindow;
touchedWindow.windowHandle = oldWindow;
touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_HOVER_EXIT;
- touchedWindow.pointerIds.set(pointerId);
out.push_back(touchedWindow);
}
}
@@ -666,7 +671,7 @@
}
touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
}
- touchedWindow.pointerIds.set(pointerId);
+ touchedWindow.addHoveringPointer(entry.deviceId, pointerId);
if (canReceiveForegroundTouches(*newWindow->getInfo())) {
touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
}
@@ -1955,7 +1960,7 @@
ALOGD("dispatchEventToCurrentInputTargets");
}
- updateInteractionTokensLocked(*eventEntry, inputTargets);
+ processInteractionsLocked(*eventEntry, inputTargets);
ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
@@ -2188,8 +2193,8 @@
/**
* In general, touch should be always split between windows. Some exceptions:
* 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's
- * from the same device, *and* the window that's receiving the current pointer does not support
- * split touch.
+ * from the same device, *and* the window that's receiving the current pointer does not support
+ * split touch.
* 2. Don't split mouse events
*/
bool InputDispatcher::shouldSplitTouch(const TouchState& touchState,
@@ -2212,9 +2217,7 @@
continue;
}
- // Eventually, touchedWindow will contain the deviceId of each pointer that's currently
- // being sent there. For now, use deviceId from touch state.
- if (entry.deviceId == touchState.deviceId && touchedWindow.pointerIds.any()) {
+ if (touchedWindow.hasTouchingPointers(entry.deviceId)) {
return false;
}
}
@@ -2247,8 +2250,13 @@
}
bool isSplit = shouldSplitTouch(tempTouchState, entry);
- const bool switchedDevice = (oldState != nullptr) &&
- (oldState->deviceId != entry.deviceId || oldState->source != entry.source);
+ bool switchedDevice = false;
+ if (oldState != nullptr) {
+ std::set<int32_t> oldActiveDevices = oldState->getActiveDeviceIds();
+ const bool anotherDeviceIsActive =
+ oldActiveDevices.count(entry.deviceId) == 0 && !oldActiveDevices.empty();
+ switchedDevice |= anotherDeviceIsActive;
+ }
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
@@ -2267,7 +2275,7 @@
// from another device. However, if the new event is a down event, let's cancel the current
// touch and let the new one take over.
if (switchedDevice && wasDown && !isDown) {
- LOG(INFO) << "Dropping event because a pointer for device " << oldState->deviceId
+ LOG(INFO) << "Dropping event because a pointer for another device "
<< " is already down in display " << displayId << ": " << entry.getDescription();
// TODO(b/211379801): test multiple simultaneous input streams.
outInjectionResult = InputEventInjectionResult::FAILED;
@@ -2277,8 +2285,6 @@
if (newGesture) {
// If a new gesture is starting, clear the touch state completely.
tempTouchState.reset();
- tempTouchState.deviceId = entry.deviceId;
- tempTouchState.source = entry.source;
isSplit = false;
} else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
ALOGI("Dropping move event because a pointer for a different device is already active "
@@ -2298,7 +2304,7 @@
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
const auto [x, y] = resolveTouchedPosition(entry);
- const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ const int32_t pointerIndex = MotionEvent::getActionIndex(action);
// Outside targets should be added upon first dispatched DOWN event. That means, this should
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2331,6 +2337,8 @@
} else if (isSplit) {
// New window does not support splitting but we have already split events.
// Ignore the new window.
+ LOG(INFO) << "Skipping " << newTouchedWindowHandle->getName()
+ << " because it doesn't support split touch";
newTouchedWindowHandle = nullptr;
}
} else {
@@ -2397,7 +2405,7 @@
// still add a window to the touch state. We should avoid doing that, but some of the
// later checks ("at least one foreground window") rely on this in order to dispatch
// the event properly, so that needs to be updated, possibly by looking at InputTargets.
- tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
+ tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId, pointerIds,
isDownOrPointerDown
? std::make_optional(entry.eventTime)
: std::nullopt);
@@ -2421,8 +2429,8 @@
if (isSplit) {
wallpaperFlags |= InputTarget::Flags::SPLIT;
}
- tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, pointerIds,
- entry.eventTime);
+ tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, entry.deviceId,
+ pointerIds, entry.eventTime);
}
}
}
@@ -2433,12 +2441,12 @@
// which is a specific behaviour that we want.
const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.pointerIds.test(pointerId) &&
- touchedWindow.pilferedPointerIds.count() > 0) {
+ if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) &&
+ touchedWindow.hasPilferingPointers(entry.deviceId)) {
// This window is already pilfering some pointers, and this new pointer is also
// going to it. Therefore, take over this pointer and don't give it to anyone
// else.
- touchedWindow.pilferedPointerIds.set(pointerId);
+ touchedWindow.addPilferingPointer(entry.deviceId, pointerId);
}
}
@@ -2510,7 +2518,7 @@
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
addWindowTargetLocked(oldTouchedWindowHandle,
InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds,
- touchedWindow.firstDownTimeInTarget, targets);
+ touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2531,27 +2539,30 @@
targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
- tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds,
- entry.eventTime);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags,
+ entry.deviceId, pointerIds, entry.eventTime);
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, pointerId, targets);
- tempTouchState.removeTouchedPointerFromWindow(pointerId, oldTouchedWindowHandle);
+ tempTouchState, entry.deviceId, pointerId, targets);
+ tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointerId,
+ oldTouchedWindowHandle);
}
}
// Update the pointerIds for non-splittable when it received pointer down.
if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
// If no split, we suppose all touched windows should receive pointer down.
- const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ const int32_t pointerIndex = MotionEvent::getActionIndex(action);
for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
TouchedWindow& touchedWindow = tempTouchState.windows[i];
// Ignore drag window for it should just track one pointer.
if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
continue;
}
- touchedWindow.pointerIds.set(entry.pointerProperties[pointerIndex].id);
+ std::bitset<MAX_POINTER_ID + 1> touchingPointers;
+ touchingPointers.set(entry.pointerProperties[pointerIndex].id);
+ touchedWindow.addTouchingPointers(entry.deviceId, touchingPointers);
}
}
}
@@ -2563,7 +2574,7 @@
for (const TouchedWindow& touchedWindow : hoveringWindows) {
std::optional<InputTarget> target =
createInputTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.firstDownTimeInTarget);
+ touchedWindow.getDownTimeInTarget(entry.deviceId));
if (!target) {
continue;
}
@@ -2579,18 +2590,13 @@
if (entry.injectionState != nullptr) {
std::string errs;
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
- // Allow ACTION_OUTSIDE events generated by targeted injection to be
- // dispatched to any uid, since the coords will be zeroed out later.
- continue;
- }
const auto err = verifyTargetedInjection(touchedWindow.windowHandle, entry);
if (err) errs += "\n - " + *err;
}
if (!errs.empty()) {
ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
- "%d:%s",
- *entry.injectionState->targetUid, errs.c_str());
+ "%s:%s",
+ entry.injectionState->targetUid->toString().c_str(), errs.c_str());
outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
return {};
}
@@ -2602,7 +2608,7 @@
sp<WindowInfoHandle> foregroundWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle) {
- const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
+ const auto foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
for (InputTarget& target : targets) {
if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
sp<WindowInfoHandle> targetWindow =
@@ -2625,17 +2631,30 @@
// Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
+ if (!touchedWindow.hasTouchingPointers(entry.deviceId) &&
+ !touchedWindow.hasHoveringPointers(entry.deviceId)) {
// Windows with hovering pointers are getting persisted inside TouchState.
// Do not send this event to those windows.
continue;
}
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
- targets);
+ touchedWindow.getTouchingPointers(entry.deviceId),
+ touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
}
+ // During targeted injection, only allow owned targets to receive events
+ std::erase_if(targets, [&](const InputTarget& target) {
+ LOG_ALWAYS_FATAL_IF(target.windowHandle == nullptr);
+ const auto err = verifyTargetedInjection(target.windowHandle, entry);
+ if (err) {
+ LOG(WARNING) << "Dropping injected event from " << target.windowHandle->getName()
+ << ": " << (*err);
+ return true;
+ }
+ return false;
+ });
+
if (targets.empty()) {
LOG(INFO) << "Dropping event because no targets were found: " << entry.getDescription();
outInjectionResult = InputEventInjectionResult::FAILED;
@@ -2673,14 +2692,9 @@
"Conflicting pointer actions: Hover received while pointer was down.");
*outConflictingPointerActions = true;
}
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- tempTouchState.deviceId = entry.deviceId;
- tempTouchState.source = entry.source;
- }
} else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
// Pointer went up.
- tempTouchState.removeTouchedPointer(entry.pointerProperties[0].id);
+ tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id);
} else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
tempTouchState.reset();
@@ -2692,18 +2706,9 @@
}
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
// One pointer went up.
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-
- for (size_t i = 0; i < tempTouchState.windows.size();) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
- touchedWindow.pointerIds.reset(pointerId);
- if (touchedWindow.pointerIds.none()) {
- tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
- continue;
- }
- i += 1;
- }
+ const int32_t pointerIndex = MotionEvent::getActionIndex(action);
+ const uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
+ tempTouchState.removeTouchingPointer(entry.deviceId, pointerId);
}
// Save changes unless the action was scroll in which case the temporary touch
@@ -2802,7 +2807,7 @@
}
case AMOTION_EVENT_ACTION_POINTER_UP:
- if (getMotionEventActionPointerIndex(entry.action) != pointerIndex) {
+ if (MotionEvent::getActionIndex(entry.action) != pointerIndex) {
break;
}
// The drag pointer is up.
@@ -2830,6 +2835,7 @@
}
InputTarget inputTarget;
inputTarget.inputChannel = inputChannel;
+ inputTarget.windowHandle = windowHandle;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
@@ -2837,7 +2843,7 @@
if (displayInfoIt != mDisplayInfos.end()) {
inputTarget.displayTransform = displayInfoIt->second.transform;
} else {
- // DisplayInfo not found for this window on display windowInfo->displayId.
+ // DisplayInfo not found for this window on display windowHandle->getInfo()->displayId.
// TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
}
return inputTarget;
@@ -2953,8 +2959,8 @@
TouchOcclusionInfo info;
info.hasBlockingOcclusion = false;
info.obscuringOpacity = 0;
- info.obscuringUid = -1;
- std::map<int32_t, float> opacityByUid;
+ info.obscuringUid = gui::Uid::INVALID;
+ std::map<gui::Uid, float> opacityByUid;
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
@@ -2964,7 +2970,7 @@
!haveSameApplicationToken(windowInfo, otherInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
info.debugInfo.push_back(
- dumpWindowForTouchOcclusion(otherInfo, /* isTouchedWindow */ false));
+ dumpWindowForTouchOcclusion(otherInfo, /*isTouchedWindow=*/false));
}
// canBeObscuredBy() has returned true above, which means this window is untrusted, so
// we perform the checks below to see if the touch can be propagated or not based on the
@@ -2976,7 +2982,7 @@
break;
}
if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) {
- uint32_t uid = otherInfo->ownerUid;
+ const auto uid = otherInfo->ownerUid;
float opacity =
(opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid];
// Given windows A and B:
@@ -2992,37 +2998,37 @@
}
}
if (DEBUG_TOUCH_OCCLUSION) {
- info.debugInfo.push_back(
- dumpWindowForTouchOcclusion(windowInfo, /* isTouchedWindow */ true));
+ info.debugInfo.push_back(dumpWindowForTouchOcclusion(windowInfo, /*isTouchedWindow=*/true));
}
return info;
}
std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info,
bool isTouchedWindow) const {
- return StringPrintf(INDENT2 "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
+ return StringPrintf(INDENT2 "* %spackage=%s/%s, id=%" PRId32 ", mode=%s, alpha=%.2f, "
"frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
"], touchableRegion=%s, window={%s}, inputConfig={%s}, "
"hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(),
- info->ownerUid, info->id, toString(info->touchOcclusionMode).c_str(),
- info->alpha, info->frameLeft, info->frameTop, info->frameRight,
- info->frameBottom, dumpRegion(info->touchableRegion).c_str(),
- info->name.c_str(), info->inputConfig.string().c_str(),
- toString(info->token != nullptr), info->applicationInfo.name.c_str(),
+ info->ownerUid.toString().c_str(), info->id,
+ toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
+ info->frameTop, info->frameRight, info->frameBottom,
+ dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
+ info->inputConfig.string().c_str(), toString(info->token != nullptr),
+ info->applicationInfo.name.c_str(),
binderToString(info->applicationInfo.token).c_str());
}
bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
if (occlusionInfo.hasBlockingOcclusion) {
- ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
- occlusionInfo.obscuringUid);
+ ALOGW("Untrusted touch due to occlusion by %s/%s", occlusionInfo.obscuringPackage.c_str(),
+ occlusionInfo.obscuringUid.toString().c_str());
return false;
}
if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) {
- ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = "
+ ALOGW("Untrusted touch due to occlusion by %s/%s (obscuring opacity = "
"%.2f, maximum allowed = %.2f)",
- occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid,
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid.toString().c_str(),
occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch);
return false;
}
@@ -3286,17 +3292,10 @@
switch (newEntry.type) {
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry);
- dispatchEntry->resolvedEventId = keyEntry.id;
- dispatchEntry->resolvedAction = keyEntry.action;
- dispatchEntry->resolvedFlags = keyEntry.flags;
-
if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key "
- "event",
- connection->getInputChannelName().c_str());
- }
+ LOG(WARNING) << "channel " << connection->getInputChannelName()
+ << "~ dropping inconsistent event: " << *dispatchEntry;
return; // skip the inconsistent event
}
break;
@@ -3320,7 +3319,6 @@
} else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
} else {
- dispatchEntry->resolvedAction = motionEntry.action;
dispatchEntry->resolvedEventId = motionEntry.id;
}
if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
@@ -3336,7 +3334,6 @@
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
- dispatchEntry->resolvedFlags = motionEntry.flags;
if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_CANCELED;
}
@@ -3349,11 +3346,8 @@
if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
- "event",
- connection->getInputChannelName().c_str());
- }
+ LOG(WARNING) << "channel " << connection->getInputChannelName()
+ << "~ dropping inconsistent event: " << *dispatchEntry;
return; // skip the inconsistent event
}
@@ -3408,38 +3402,49 @@
}
/**
- * This function is purely for debugging. It helps us understand where the user interaction
- * was taking place. For example, if user is touching launcher, we will see a log that user
- * started interacting with launcher. In that example, the event would go to the wallpaper as well.
- * We will see both launcher and wallpaper in that list.
- * Once the interaction with a particular set of connections starts, no new logs will be printed
- * until the set of interacted connections changes.
+ * This function is for debugging and metrics collection. It has two roles.
*
- * The following items are skipped, to reduce the logspam:
- * ACTION_OUTSIDE: any windows that are receiving ACTION_OUTSIDE are not logged
- * ACTION_UP: any windows that receive ACTION_UP are not logged (for both keys and motions).
- * This includes situations like the soft BACK button key. When the user releases (lifts up the
- * finger) the back button, then navigation bar will inject KEYCODE_BACK with ACTION_UP.
- * Both of those ACTION_UP events would not be logged
+ * The first role is to log input interaction with windows, which helps determine what the user was
+ * interacting with. For example, if user is touching launcher, we will see an input_interaction log
+ * that user started interacting with launcher window, as well as any other window that received
+ * that gesture, such as the wallpaper or other spy windows. A new input_interaction is only logged
+ * when the set of tokens that received the event changes. It is not logged again as long as the
+ * user is interacting with the same windows.
+ *
+ * The second role is to track input device activity for metrics collection. For each input event,
+ * we report the set of UIDs that the input device interacted with to the policy. Unlike for the
+ * input_interaction logs, the device interaction is reported even when the set of interaction
+ * tokens do not change.
+ *
+ * For these purposes, we do not count ACTION_OUTSIDE, ACTION_UP and ACTION_CANCEL actions as
+ * interaction. This includes up and cancel events for both keys and motions.
*/
-void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry,
- const std::vector<InputTarget>& targets) {
+void InputDispatcher::processInteractionsLocked(const EventEntry& entry,
+ const std::vector<InputTarget>& targets) {
+ int32_t deviceId;
+ nsecs_t eventTime;
// Skip ACTION_UP events, and all events other than keys and motions
if (entry.type == EventEntry::Type::KEY) {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
return;
}
+ deviceId = keyEntry.deviceId;
+ eventTime = keyEntry.eventTime;
} else if (entry.type == EventEntry::Type::MOTION) {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
if (motionEntry.action == AMOTION_EVENT_ACTION_UP ||
- motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
+ motionEntry.action == AMOTION_EVENT_ACTION_CANCEL ||
+ MotionEvent::getActionMasked(motionEntry.action) == AMOTION_EVENT_ACTION_POINTER_UP) {
return;
}
+ deviceId = motionEntry.deviceId;
+ eventTime = motionEntry.eventTime;
} else {
return; // Not a key or a motion
}
+ std::set<gui::Uid> interactionUids;
std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
std::vector<std::shared_ptr<Connection>> newConnections;
for (const InputTarget& target : targets) {
@@ -3454,7 +3459,18 @@
}
newConnectionTokens.insert(std::move(token));
newConnections.emplace_back(connection);
+ if (target.windowHandle) {
+ interactionUids.emplace(target.windowHandle->getInfo()->ownerUid);
+ }
}
+
+ auto command = [this, deviceId, eventTime, uids = std::move(interactionUids)]()
+ REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy.notifyDeviceInteraction(deviceId, eventTime, uids);
+ };
+ postCommandLocked(std::move(command));
+
if (newConnectionTokens == mInteractionConnectionTokens) {
return; // no change
}
@@ -3565,8 +3581,8 @@
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- LOG(DEBUG) << "Publishing " << *dispatchEntry << " to "
- << connection->getInputChannelName();
+ LOG(INFO) << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
}
// Publish the key event.
@@ -3584,8 +3600,8 @@
case EventEntry::Type::MOTION: {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- LOG(DEBUG) << "Publishing " << *dispatchEntry << " to "
- << connection->getInputChannelName();
+ LOG(INFO) << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
}
status = publishMotionEvent(*connection, *dispatchEntry);
break;
@@ -3744,8 +3760,8 @@
const std::shared_ptr<Connection>& connection,
bool notify) {
if (DEBUG_DISPATCH_CYCLE) {
- LOG(DEBUG) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
- << " - notify=" << toString(notify);
+ LOG(INFO) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
+ << " - notify=" << toString(notify);
}
// Clear the dispatch queues.
@@ -4061,9 +4077,9 @@
uint32_t pointerId = uint32_t(pointerProperties.id);
if (pointerIds.test(pointerId)) {
splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
- splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
- splitPointerCoords[splitPointerCount].copyFrom(
- originalMotionEntry.pointerCoords[originalPointerIndex]);
+ splitPointerProperties[splitPointerCount] = pointerProperties;
+ splitPointerCoords[splitPointerCount] =
+ originalMotionEntry.pointerCoords[originalPointerIndex];
splitPointerCount += 1;
}
}
@@ -4085,7 +4101,7 @@
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
- int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
+ int32_t originalPointerIndex = MotionEvent::getActionIndex(action);
const PointerProperties& pointerProperties =
originalMotionEntry.pointerProperties[originalPointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
@@ -4112,11 +4128,11 @@
}
}
- if (action == AMOTION_EVENT_ACTION_DOWN) {
- LOG_ALWAYS_FATAL_IF(splitDownTime != originalMotionEntry.eventTime,
- "Split motion event has mismatching downTime and eventTime for "
- "ACTION_DOWN, motionEntry=%s, splitDownTime=%" PRId64,
- originalMotionEntry.getDescription().c_str(), splitDownTime);
+ if (action == AMOTION_EVENT_ACTION_DOWN && splitDownTime != originalMotionEntry.eventTime) {
+ logDispatchStateLocked();
+ LOG_ALWAYS_FATAL("Split motion event has mismatching downTime and eventTime for "
+ "ACTION_DOWN, motionEntry=%s, splitDownTime=%" PRId64,
+ originalMotionEntry.getDescription().c_str(), splitDownTime);
}
int32_t newId = mIdGenerator.nextId();
@@ -4300,7 +4316,7 @@
args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,
args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,
args.downTime);
- for (uint32_t i = 0; i < args.pointerCount; i++) {
+ for (uint32_t i = 0; i < args.getPointerCount(); i++) {
ALOGD(" Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
i, args.pointerProperties[i].id,
@@ -4317,13 +4333,27 @@
}
}
- Result<void> motionCheck = validateMotionEvent(args.action, args.actionButton,
- args.pointerCount, args.pointerProperties);
+ Result<void> motionCheck =
+ validateMotionEvent(args.action, args.actionButton, args.getPointerCount(),
+ args.pointerProperties.data());
if (!motionCheck.ok()) {
LOG(FATAL) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
return;
}
+ if (DEBUG_VERIFY_EVENTS) {
+ auto [it, _] =
+ mVerifiersByDisplay.try_emplace(args.displayId,
+ StringPrintf("display %" PRId32, args.displayId));
+ Result<void> result =
+ it->second.processMovement(args.deviceId, args.action, args.getPointerCount(),
+ args.pointerProperties.data(), args.pointerCoords.data(),
+ args.flags);
+ if (!result.ok()) {
+ LOG(FATAL) << "Bad stream: " << result.error() << " caused by " << args.dump();
+ }
+ }
+
uint32_t policyFlags = args.policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4343,7 +4373,7 @@
const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);
if (touchStateIt != mTouchStatesByDisplay.end()) {
const TouchState& touchState = touchStateIt->second;
- if (touchState.deviceId == args.deviceId && touchState.isDown()) {
+ if (touchState.hasTouchingPointers(args.deviceId)) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
@@ -4363,8 +4393,8 @@
args.metaState, args.buttonState, args.classification,
displayTransform, args.xPrecision, args.yPrecision,
args.xCursorPosition, args.yCursorPosition, displayTransform,
- args.downTime, args.eventTime, args.pointerCount,
- args.pointerProperties, args.pointerCoords);
+ args.downTime, args.eventTime, args.getPointerCount(),
+ args.pointerProperties.data(), args.pointerCoords.data());
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy.filterInputEvent(event, policyFlags)) {
@@ -4382,8 +4412,9 @@
args.buttonState, args.classification, args.edgeFlags,
args.xPrecision, args.yPrecision,
args.xCursorPosition, args.yCursorPosition,
- args.downTime, args.pointerCount,
- args.pointerProperties, args.pointerCoords);
+ args.downTime, args.getPointerCount(),
+ args.pointerProperties.data(),
+ args.pointerCoords.data());
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
@@ -4465,6 +4496,10 @@
std::unique_ptr<DeviceResetEntry> newEntry =
std::make_unique<DeviceResetEntry>(args.id, args.eventTime, args.deviceId);
needWake = enqueueInboundEventLocked(std::move(newEntry));
+
+ for (auto& [_, verifier] : mVerifiersByDisplay) {
+ verifier.resetDevice(args.deviceId);
+ }
} // release lock
if (needWake) {
@@ -4492,7 +4527,7 @@
}
InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event,
- std::optional<int32_t> targetUid,
+ std::optional<gui::Uid> targetUid,
InputEventInjectionSync syncMode,
std::chrono::milliseconds timeout,
uint32_t policyFlags) {
@@ -4503,10 +4538,10 @@
}
if (debugInboundEventDetails()) {
- LOG(DEBUG) << __func__ << ": targetUid=" << toString(targetUid)
- << ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
- << "ms, policyFlags=0x" << std::hex << policyFlags << std::dec
- << ", event=" << *event;
+ LOG(INFO) << __func__ << ": targetUid=" << toString(targetUid, &uidString)
+ << ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
+ << "ms, policyFlags=0x" << std::hex << policyFlags << std::dec
+ << ", event=" << *event;
}
nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
@@ -4657,7 +4692,7 @@
bool needWake = false;
while (!injectedEntries.empty()) {
if (DEBUG_INJECTION) {
- LOG(DEBUG) << "Injecting " << injectedEntries.front()->getDescription();
+ LOG(INFO) << "Injecting " << injectedEntries.front()->getDescription();
}
needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
injectedEntries.pop();
@@ -4721,8 +4756,8 @@
} // release lock
if (DEBUG_INJECTION) {
- LOG(DEBUG) << "injectInputEvent - Finished with result "
- << ftl::enum_string(injectionResult);
+ LOG(INFO) << "injectInputEvent - Finished with result "
+ << ftl::enum_string(injectionResult);
}
return injectionResult;
@@ -4766,8 +4801,8 @@
InjectionState* injectionState = entry.injectionState;
if (injectionState) {
if (DEBUG_INJECTION) {
- LOG(DEBUG) << "Setting input event injection result to "
- << ftl::enum_string(injectionResult);
+ LOG(INFO) << "Setting input event injection result to "
+ << ftl::enum_string(injectionResult);
}
if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
@@ -4948,8 +4983,8 @@
ALOGD("%s", log.c_str());
}
}
- ALOGW("Dropping untrusted touch event due to %s/%d", occlusionInfo.obscuringPackage.c_str(),
- occlusionInfo.obscuringUid);
+ ALOGW("Dropping untrusted touch event due to %s/%s", occlusionInfo.obscuringPackage.c_str(),
+ occlusionInfo.obscuringUid.toString().c_str());
return false;
}
@@ -5022,19 +5057,6 @@
mWindowHandlesByDisplay[displayId] = newHandles;
}
-void InputDispatcher::setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>>& handlesPerDisplay) {
- // TODO(b/198444055): Remove setInputWindows from InputDispatcher.
- { // acquire lock
- std::scoped_lock _l(mLock);
- for (const auto& [displayId, handles] : handlesPerDisplay) {
- setInputWindowsLocked(handles, displayId);
- }
- }
- // Wake up poll loop since it may need to make new input dispatching choices.
- mLooper->wake();
-}
-
/**
* Called from InputManagerService, update window handle list by displayId that can receive input.
* A window handle contains information about InputChannel, Touch Region, Types, Focused,...
@@ -5082,13 +5104,6 @@
// Copy old handles for release if they are no longer present.
const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
- // Save the old windows' orientation by ID before it gets updated.
- std::unordered_map<int32_t, uint32_t> oldWindowOrientations;
- for (const sp<WindowInfoHandle>& handle : oldWindowHandles) {
- oldWindowOrientations.emplace(handle->getId(),
- handle->getInfo()->transform.getOrientation());
- }
-
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
@@ -5142,23 +5157,6 @@
}
}
- // Determine if the orientation of any of the input windows have changed, and cancel all
- // pointer events if necessary.
- for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
- const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
- if (newWindowHandle != nullptr &&
- newWindowHandle->getInfo()->transform.getOrientation() !=
- oldWindowOrientations[oldWindowHandle->getId()]) {
- std::shared_ptr<InputChannel> inputChannel =
- getInputChannelLocked(newWindowHandle->getToken());
- if (inputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "touched window's orientation changed");
- synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
- }
- }
- }
-
// Release information for windows that are no longer present.
// This ensures that unused input channels are released promptly.
// Otherwise, they might stick around until the window handle is destroyed
@@ -5311,15 +5309,16 @@
mLooper->wake();
}
-bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
- int32_t displayId) {
+bool InputDispatcher::setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid,
+ bool hasPermission, int32_t displayId) {
bool needWake = false;
{
std::scoped_lock lock(mLock);
ALOGD_IF(DEBUG_TOUCH_MODE,
- "Request to change touch mode to %s (calling pid=%d, uid=%d, "
+ "Request to change touch mode to %s (calling pid=%s, uid=%s, "
"hasPermission=%s, target displayId=%d, mTouchModePerDisplay[displayId]=%s)",
- toString(inTouchMode), pid, uid, toString(hasPermission), displayId,
+ toString(inTouchMode), pid.toString().c_str(), uid.toString().c_str(),
+ toString(hasPermission), displayId,
mTouchModePerDisplay.count(displayId) == 0
? "not set"
: std::to_string(mTouchModePerDisplay[displayId]).c_str());
@@ -5331,9 +5330,9 @@
if (!hasPermission) {
if (!focusedWindowIsOwnedByLocked(pid, uid) &&
!recentWindowsAreOwnedByLocked(pid, uid)) {
- ALOGD("Touch mode switch rejected, caller (pid=%d, uid=%d) doesn't own the focused "
+ ALOGD("Touch mode switch rejected, caller (pid=%s, uid=%s) doesn't own the focused "
"window nor none of the previously interacted window",
- pid, uid);
+ pid.toString().c_str(), uid.toString().c_str());
return false;
}
}
@@ -5349,7 +5348,7 @@
return true;
}
-bool InputDispatcher::focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) {
+bool InputDispatcher::focusedWindowIsOwnedByLocked(gui::Pid pid, gui::Uid uid) {
const sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (focusedToken == nullptr) {
return false;
@@ -5358,7 +5357,7 @@
return isWindowOwnedBy(windowHandle, pid, uid);
}
-bool InputDispatcher::recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) {
+bool InputDispatcher::recentWindowsAreOwnedByLocked(gui::Pid pid, gui::Uid uid) {
return std::find_if(mInteractionConnectionTokens.begin(), mInteractionConnectionTokens.end(),
[&](const sp<IBinder>& connectionToken) REQUIRES(mLock) {
const sp<WindowInfoHandle> windowHandle =
@@ -5403,14 +5402,22 @@
// Find the target touch state and touched window by fromToken.
auto [state, touchedWindow, displayId] = findTouchStateWindowAndDisplayLocked(fromToken);
+
if (state == nullptr || touchedWindow == nullptr) {
- ALOGD("Focus transfer failed because from window is not being touched.");
+ ALOGD("Touch transfer failed because from window is not being touched.");
return false;
}
+ std::set<int32_t> deviceIds = touchedWindow->getTouchingDeviceIds();
+ if (deviceIds.size() != 1) {
+ LOG(INFO) << "Can't transfer touch. Currently touching devices: " << dumpSet(deviceIds)
+ << " for window: " << touchedWindow->dump();
+ return false;
+ }
+ const int32_t deviceId = *deviceIds.begin();
sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
if (toWindowHandle == nullptr) {
- ALOGW("Cannot transfer focus because to window not found.");
+ ALOGW("Cannot transfer touch because to window not found.");
return false;
}
@@ -5422,7 +5429,7 @@
// Erase old window.
ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags;
- std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->getTouchingPointers(deviceId);
sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
state->removeWindowByToken(fromToken);
@@ -5433,7 +5440,8 @@
if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) {
newTargetFlags |= InputTarget::Flags::FOREGROUND;
}
- state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds, downTimeInTarget);
+ state->addOrUpdateWindow(toWindowHandle, newTargetFlags, deviceId, pointerIds,
+ downTimeInTarget);
// Store the dragging window.
if (isDragDrop) {
@@ -5452,16 +5460,15 @@
std::shared_ptr<Connection> toConnection = getConnectionLocked(toToken);
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
- CancelationOptions
- options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "transferring touch focus from this window to another window");
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "transferring touch from this window to another window");
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection,
newTargetFlags);
// Check if the wallpaper window should deliver the corresponding event.
transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
- *state, pointerIds);
+ *state, deviceId, pointerIds);
}
} // release lock
@@ -5643,10 +5650,11 @@
windowInfo->applicationInfo.name.c_str(),
binderToString(windowInfo->applicationInfo.token).c_str());
dump += dumpRegion(windowInfo->touchableRegion);
- dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
+ dump += StringPrintf(", ownerPid=%s, ownerUid=%s, dispatchingTimeout=%" PRId64
"ms, hasToken=%s, "
"touchOcclusionMode=%s\n",
- windowInfo->ownerPid, windowInfo->ownerUid,
+ windowInfo->ownerPid.toString().c_str(),
+ windowInfo->ownerUid.toString().c_str(),
millis(windowInfo->dispatchingTimeout),
binderToString(windowInfo->token).c_str(),
toString(windowInfo->touchOcclusionMode).c_str());
@@ -5749,6 +5757,12 @@
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
}
+ std::stringstream inputStateDump;
+ inputStateDump << connection->inputState;
+ if (!isEmpty(inputStateDump)) {
+ dump += INDENT3 "InputState: ";
+ dump += inputStateDump.str() + "\n";
+ }
}
} else {
dump += INDENT "Connections: <none>\n";
@@ -5838,7 +5852,7 @@
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId,
const std::string& name,
- int32_t pid) {
+ gui::Pid pid) {
std::shared_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
status_t result = openInputChannelPair(name, serverChannel, clientChannel);
@@ -5944,20 +5958,28 @@
}
auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
- if (statePtr == nullptr || windowPtr == nullptr || windowPtr->pointerIds.none()) {
+ if (statePtr == nullptr || windowPtr == nullptr) {
ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
" Ignoring.");
return BAD_VALUE;
}
+ std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds();
+ if (deviceIds.size() != 1) {
+ LOG(WARNING) << "Can't pilfer. Currently touching devices: " << dumpSet(deviceIds)
+ << " in window: " << windowPtr->dump();
+ return BAD_VALUE;
+ }
+ const int32_t deviceId = *deviceIds.begin();
TouchState& state = *statePtr;
TouchedWindow& window = *windowPtr;
// Send cancel events to all the input channels we're stealing from.
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"input channel stole pointer stream");
- options.deviceId = state.deviceId;
+ options.deviceId = deviceId;
options.displayId = displayId;
- options.pointerIds = window.pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId);
+ options.pointerIds = pointerIds;
std::string canceledWindows;
for (const TouchedWindow& w : state.windows) {
const std::shared_ptr<InputChannel> channel =
@@ -5974,9 +5996,9 @@
// Prevent the gesture from being sent to any other windows.
// This only blocks relevant pointers to be sent to other windows
- window.pilferedPointerIds |= window.pointerIds;
+ window.addPilferingPointers(deviceId, pointerIds);
- state.cancelPointersForWindowsExcept(window.pointerIds, token);
+ state.cancelPointersForWindowsExcept(deviceId, pointerIds, token);
return OK;
}
@@ -6030,7 +6052,7 @@
} // release lock
}
-std::optional<int32_t> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) {
+std::optional<gui::Pid> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) {
for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) {
for (const Monitor& monitor : monitors) {
if (monitor.inputChannel->getConnectionToken() == token) {
@@ -6190,9 +6212,9 @@
StringPrintf("%s does not have a focused window", application->getName().c_str());
updateLastAnrStateLocked(*application, reason);
- auto command = [this, application = std::move(application)]() REQUIRES(mLock) {
+ auto command = [this, app = std::move(application)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy.notifyNoFocusedWindowAnr(application);
+ mPolicy.notifyNoFocusedWindowAnr(app);
};
postCommandLocked(std::move(command));
}
@@ -6250,17 +6272,17 @@
}
void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
- std::optional<int32_t> pid,
+ std::optional<gui::Pid> pid,
std::string reason) {
- auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {
+ auto command = [this, token, pid, r = std::move(reason)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy.notifyWindowUnresponsive(token, pid, reason);
+ mPolicy.notifyWindowUnresponsive(token, pid, r);
};
postCommandLocked(std::move(command));
}
void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& token,
- std::optional<int32_t> pid) {
+ std::optional<gui::Pid> pid) {
auto command = [this, token, pid]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
mPolicy.notifyWindowResponsive(token, pid);
@@ -6276,7 +6298,7 @@
void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,
std::string reason) {
const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
- std::optional<int32_t> pid;
+ std::optional<gui::Pid> pid;
if (connection.monitor) {
ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
reason.c_str());
@@ -6298,7 +6320,7 @@
*/
void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) {
const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
- std::optional<int32_t> pid;
+ std::optional<gui::Pid> pid;
if (connection.monitor) {
pid = findMonitorPidByTokenLocked(connectionToken);
} else {
@@ -6524,7 +6546,7 @@
}
}
-void InputDispatcher::dump(std::string& dump) {
+void InputDispatcher::dump(std::string& dump) const {
std::scoped_lock _l(mLock);
dump += "Input Dispatcher State:\n";
@@ -6549,7 +6571,7 @@
* this method can be safely called from any thread, as long as you've ensured that
* the work you are interested in completing has already been queued.
*/
-bool InputDispatcher::waitForIdle() {
+bool InputDispatcher::waitForIdle() const {
/**
* Timeout should represent the longest possible time that a device might spend processing
* events and commands.
@@ -6659,7 +6681,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
// Set an empty list to remove all handles from the specific display.
- setInputWindowsLocked(/* window handles */ {}, displayId);
+ setInputWindowsLocked(/*windowInfoHandles=*/{}, displayId);
setFocusedApplicationLocked(displayId, nullptr);
// Call focus resolver to clean up stale requests. This must be called after input windows
// have been removed for the removed display.
@@ -6668,6 +6690,7 @@
std::erase(mIneligibleDisplaysForPointerCapture, displayId);
// Remove the associated touch mode state.
mTouchModePerDisplay.erase(displayId);
+ mVerifiersByDisplay.erase(displayId);
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -6700,6 +6723,13 @@
for (const auto& [displayId, handles] : handlesPerDisplay) {
setInputWindowsLocked(handles, displayId);
}
+
+ if (update.vsyncId < mWindowInfosVsyncId) {
+ ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64
+ ", current update vsync id: %" PRId64,
+ mWindowInfosVsyncId, update.vsyncId);
+ }
+ mWindowInfosVsyncId = update.vsyncId;
}
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
@@ -6740,13 +6770,6 @@
mLooper->wake();
}
-void InputDispatcher::requestRefreshConfiguration() {
- InputDispatcherConfiguration config = mPolicy.getDispatcherConfiguration();
-
- std::scoped_lock _l(mLock);
- mConfig = config;
-}
-
void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout) {
std::scoped_lock _l(mLock);
mMonitorDispatchingTimeout = timeout;
@@ -6755,7 +6778,7 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t pointerId,
+ TouchState& state, int32_t deviceId, int32_t pointerId,
std::vector<InputTarget>& targets) const {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
@@ -6777,8 +6800,8 @@
addWindowTargetLocked(oldWallpaper,
oldTouchedWindow.targetFlags |
InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
- pointerIds, oldTouchedWindow.firstDownTimeInTarget, targets);
- state.removeTouchedPointerFromWindow(pointerId, oldWallpaper);
+ pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId), targets);
+ state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper);
}
if (newWallpaper != nullptr) {
@@ -6786,7 +6809,7 @@
InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER |
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- pointerIds);
+ deviceId, pointerIds);
}
}
@@ -6794,7 +6817,7 @@
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<WindowInfoHandle> fromWindowHandle,
const sp<WindowInfoHandle> toWindowHandle,
- TouchState& state,
+ TouchState& state, int32_t deviceId,
std::bitset<MAX_POINTER_ID + 1> pointerIds) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
fromWindowHandle->getInfo()->inputConfig.test(
@@ -6824,7 +6847,8 @@
oldTargetFlags & (InputTarget::Flags::SPLIT | InputTarget::Flags::DISPATCH_AS_IS);
wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
- state.addOrUpdateWindow(newWallpaper, wallpaperFlags, pointerIds, downTimeInTarget);
+ state.addOrUpdateWindow(newWallpaper, wallpaperFlags, deviceId, pointerIds,
+ downTimeInTarget);
std::shared_ptr<Connection> wallpaperConnection =
getConnectionLocked(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
@@ -6858,4 +6882,11 @@
return nullptr;
}
+void InputDispatcher::setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
+ std::scoped_lock _l(mLock);
+
+ mConfig.keyRepeatTimeout = timeout;
+ mConfig.keyRepeatDelay = delay;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 8ca01b7..fef726f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -25,7 +25,6 @@
#include "InputDispatcherConfiguration.h"
#include "InputDispatcherInterface.h"
#include "InputDispatcherPolicyInterface.h"
-#include "InputState.h"
#include "InputTarget.h"
#include "InputThread.h"
#include "LatencyAggregator.h"
@@ -87,9 +86,9 @@
std::chrono::nanoseconds staleEventTimeout);
~InputDispatcher() override;
- void dump(std::string& dump) override;
+ void dump(std::string& dump) const override;
void monitor() override;
- bool waitForIdle() override;
+ bool waitForIdle() const override;
status_t start() override;
status_t stop() override;
@@ -104,22 +103,19 @@
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
android::os::InputEventInjectionResult injectInputEvent(
- const InputEvent* event, std::optional<int32_t> targetUid,
+ const InputEvent* event, std::optional<gui::Uid> targetUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) override;
std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
- void setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>>&
- handlesPerDisplay) override;
void setFocusedApplication(
int32_t displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
void setFocusedDisplay(int32_t displayId) override;
void setInputDispatchMode(bool enabled, bool frozen) override;
void setInputFilterEnabled(bool enabled) override;
- bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
+ bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission,
int32_t displayId) override;
void setMaximumObscuringOpacityForTouch(float opacity) override;
@@ -132,7 +128,7 @@
void setFocusedWindow(const android::gui::FocusRequest&) override;
base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
const std::string& name,
- int32_t pid) override;
+ gui::Pid pid) override;
status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
status_t pilferPointers(const sp<IBinder>& token) override;
void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
@@ -148,11 +144,11 @@
void cancelCurrentTouch() override;
- void requestRefreshConfiguration() override;
-
// Public to allow tests to verify that a Monitor can get ANR.
void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
+ void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) override;
+
private:
enum class DropReason {
NOT_DROPPED,
@@ -169,10 +165,10 @@
InputDispatcherPolicyInterface& mPolicy;
android::InputDispatcherConfiguration mConfig GUARDED_BY(mLock);
- std::mutex mLock;
+ mutable std::mutex mLock;
std::condition_variable mDispatcherIsAlive;
- std::condition_variable mDispatcherEnteredIdle;
+ mutable std::condition_variable mDispatcherEnteredIdle;
sp<Looper> mLooper;
@@ -202,7 +198,9 @@
DropReason mLastDropReason GUARDED_BY(mLock);
- const IdGenerator mIdGenerator;
+ const IdGenerator mIdGenerator GUARDED_BY(mLock);
+
+ int64_t mWindowInfosVsyncId GUARDED_BY(mLock);
// With each iteration, InputDispatcher nominally processes one queued event,
// a timeout, or a response from an input consumer.
@@ -269,7 +267,7 @@
mConnectionsByToken GUARDED_BY(mLock);
// Find a monitor pid by the provided token.
- std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
+ std::optional<gui::Pid> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Input channels that will receive a copy of all input events sent to the provided display.
std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -445,8 +443,8 @@
// when switching touch mode state).
std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> mInteractionConnectionTokens
GUARDED_BY(mLock);
- void updateInteractionTokensLocked(const EventEntry& entry,
- const std::vector<InputTarget>& targets) REQUIRES(mLock);
+ void processInteractionsLocked(const EventEntry& entry, const std::vector<InputTarget>& targets)
+ REQUIRES(mLock);
// Dispatch inbound events.
bool dispatchConfigurationChangedLocked(nsecs_t currentTime,
@@ -520,10 +518,10 @@
void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken,
- std::optional<int32_t> pid, std::string reason)
+ std::optional<gui::Pid> pid, std::string reason)
REQUIRES(mLock);
void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken,
- std::optional<int32_t> pid) REQUIRES(mLock);
+ std::optional<gui::Pid> pid) REQUIRES(mLock);
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -572,7 +570,7 @@
bool hasBlockingOcclusion;
float obscuringOpacity;
std::string obscuringPackage;
- int32_t obscuringUid;
+ gui::Uid obscuringUid = gui::Uid::INVALID;
std::vector<std::string> debugInfo;
};
@@ -647,7 +645,7 @@
// splitDownTime refers to the time of first 'down' event on that particular target
std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
- nsecs_t splitDownTime);
+ nsecs_t splitDownTime) REQUIRES(mLock);
// Reset and drop everything the dispatcher is doing.
void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
@@ -681,6 +679,7 @@
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
+ std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay;
bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
bool handled) REQUIRES(mLock);
@@ -700,22 +699,22 @@
void traceWaitQueueLength(const Connection& connection);
// Check window ownership
- bool focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
- bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
+ bool focusedWindowIsOwnedByLocked(gui::Pid pid, gui::Uid uid) REQUIRES(mLock);
+ bool recentWindowsAreOwnedByLocked(gui::Pid pid, gui::Uid uid) REQUIRES(mLock);
sp<InputReporterInterface> mReporter;
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t pointerId,
+ TouchState& state, int32_t deviceId, int32_t pointerId,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
const sp<android::gui::WindowInfoHandle> toWindowHandle,
- TouchState& state, std::bitset<MAX_POINTER_ID + 1> pointerIds)
- REQUIRES(mLock);
+ TouchState& state, int32_t deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds) REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 4652c2d..ccffe26 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -28,7 +28,7 @@
InputState::~InputState() {}
-bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const {
+bool InputState::isHovering(DeviceId deviceId, uint32_t source, int32_t displayId) const {
for (const MotionMemento& memento : mMotionMementos) {
if (memento.deviceId == deviceId && memento.source == source &&
memento.displayId == displayId && memento.hovering) {
@@ -93,11 +93,7 @@
mMotionMementos.erase(mMotionMementos.begin() + index);
return true;
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
- "displayId=%" PRId32 ", actionMasked=%d",
- entry.deviceId, entry.source, entry.displayId, actionMasked);
- }
+
return false;
}
@@ -150,11 +146,7 @@
return true;
}
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Dropping inconsistent motion pointer up/down or move event: "
- "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
- entry.deviceId, entry.source, entry.displayId, actionMasked);
- }
+
return false;
}
@@ -164,11 +156,7 @@
mMotionMementos.erase(mMotionMementos.begin() + index);
return true;
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
- "displayId=%" PRId32,
- entry.deviceId, entry.source, entry.displayId);
- }
+
return false;
}
@@ -252,8 +240,8 @@
continue;
}
}
- pointerProperties[pointerCount].copyFrom(entry.pointerProperties[i]);
- pointerCoords[pointerCount].copyFrom(entry.pointerCoords[i]);
+ pointerProperties[pointerCount] = entry.pointerProperties[i];
+ pointerCoords[pointerCount] = entry.pointerCoords[i];
pointerCount++;
}
}
@@ -263,8 +251,8 @@
if (other.firstNewPointerIdx < 0) {
other.firstNewPointerIdx = other.pointerCount;
}
- other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]);
- other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]);
+ other.pointerProperties[other.pointerCount] = pointerProperties[i];
+ other.pointerCoords[other.pointerCount] = pointerCoords[i];
other.pointerCount++;
}
}
@@ -336,17 +324,16 @@
// We will deliver all pointers the target already knows about
for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
- pointerProperties[i].copyFrom(memento.pointerProperties[i]);
- pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+ pointerProperties[i] = memento.pointerProperties[i];
+ pointerCoords[i] = memento.pointerCoords[i];
pointerCount++;
}
// We will send explicit events for all pointers the target doesn't know about
for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
i < memento.pointerCount; i++) {
-
- pointerProperties[i].copyFrom(memento.pointerProperties[i]);
- pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+ pointerProperties[i] = memento.pointerProperties[i];
+ pointerCoords[i] = memento.pointerCoords[i];
pointerCount++;
// Down only if the first pointer, pointer down otherwise
@@ -382,8 +369,8 @@
std::vector<PointerCoords> pointerCoords(MAX_POINTERS);
for (uint32_t pointerIdx = 0; pointerIdx < memento.pointerCount; pointerIdx++) {
uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id);
- pointerProperties[pointerIdx].copyFrom(memento.pointerProperties[pointerIdx]);
- pointerCoords[pointerIdx].copyFrom(memento.pointerCoords[pointerIdx]);
+ pointerProperties[pointerIdx] = memento.pointerProperties[pointerIdx];
+ pointerCoords[pointerIdx] = memento.pointerCoords[pointerIdx];
if (pointerIds.test(pointerId)) {
canceledPointerIndices.push_back(pointerIdx);
}
@@ -538,4 +525,14 @@
}
}
+std::ostream& operator<<(std::ostream& out, const InputState& state) {
+ if (!state.mMotionMementos.empty()) {
+ out << "mMotionMementos: ";
+ for (const InputState::MotionMemento& memento : state.mMotionMementos) {
+ out << "{deviceId= " << memento.deviceId << ", hovering=" << memento.hovering << "}, ";
+ }
+ }
+ return out;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index af2a3cb..32df034 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -36,7 +36,7 @@
// Returns true if the specified source is known to have received a hover enter
// motion event.
- bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
+ bool isHovering(DeviceId deviceId, uint32_t source, int32_t displayId) const;
// Records tracking information for a key event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
@@ -76,7 +76,7 @@
private:
struct KeyMemento {
- int32_t deviceId;
+ DeviceId deviceId;
uint32_t source;
int32_t displayId;
int32_t keyCode;
@@ -88,7 +88,7 @@
};
struct MotionMemento {
- int32_t deviceId;
+ DeviceId deviceId;
uint32_t source;
int32_t displayId;
int32_t flags;
@@ -128,7 +128,10 @@
std::vector<std::unique_ptr<MotionEntry>> synthesizeCancelationEventsForPointers(
const MotionMemento& memento, std::bitset<MAX_POINTER_ID + 1> pointerIds,
nsecs_t currentTime);
+ friend std::ostream& operator<<(std::ostream& out, const InputState& state);
};
+std::ostream& operator<<(std::ostream& out, const InputState& state);
+
} // namespace inputdispatcher
} // namespace android
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index fc8b785..11f3413 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -76,4 +76,24 @@
}
return out;
}
+
+std::ostream& operator<<(std::ostream& out, const InputTarget& target) {
+ out << "{inputChannel=";
+ if (target.inputChannel != nullptr) {
+ out << target.inputChannel->getName();
+ } else {
+ out << "<null>";
+ }
+ out << ", windowHandle=";
+ if (target.windowHandle != nullptr) {
+ out << target.windowHandle->getName();
+ } else {
+ out << "<null>";
+ }
+ out << ", targetFlags=" << target.flags.string();
+ out << ", pointers=" << target.getPointerInfoString();
+ out << "}";
+ return out;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 7b12f81..8b8a35a 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -17,6 +17,7 @@
#pragma once
#include <ftl/flags.h>
+#include <gui/WindowInfo.h>
#include <gui/constants.h>
#include <input/InputTransport.h>
#include <ui/Transform.h>
@@ -114,6 +115,10 @@
// Transform per pointerId.
ui::Transform pointerTransforms[MAX_POINTERS];
+ // The window that this input target is being dispatched to. It is possible for this to be
+ // null for cases like global monitors.
+ sp<gui::WindowInfoHandle> windowHandle;
+
void addPointers(std::bitset<MAX_POINTER_ID + 1> pointerIds, const ui::Transform& transform);
void setDefaultPointerTransform(const ui::Transform& transform);
@@ -134,6 +139,6 @@
std::string getPointerInfoString() const;
};
-std::string dispatchModeToString(int32_t dispatchMode);
+std::ostream& operator<<(std::ostream& out, const InputTarget& target);
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index 43a82d5..204791e 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -19,7 +19,7 @@
namespace android::inputdispatcher {
// --- Monitor ---
-Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid)
+Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid)
: inputChannel(inputChannel), pid(pid) {}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index 7b51191..1b1eb3a 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -16,6 +16,7 @@
#pragma once
+#include <gui/PidUid.h>
#include <input/InputTransport.h>
namespace android::inputdispatcher {
@@ -23,9 +24,9 @@
struct Monitor {
std::shared_ptr<InputChannel> inputChannel; // never null
- int32_t pid;
+ gui::Pid pid;
- explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid);
+ explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid);
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 0a61d48..4221e42 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -31,18 +31,34 @@
*this = TouchState();
}
-void TouchState::removeTouchedPointer(int32_t pointerId) {
+std::set<int32_t> TouchState::getActiveDeviceIds() const {
+ std::set<int32_t> out;
+ for (const TouchedWindow& w : windows) {
+ std::set<int32_t> deviceIds = w.getActiveDeviceIds();
+ out.insert(deviceIds.begin(), deviceIds.end());
+ }
+ return out;
+}
+
+bool TouchState::hasTouchingPointers(DeviceId deviceId) const {
+ return std::any_of(windows.begin(), windows.end(), [&](const TouchedWindow& window) {
+ return window.hasTouchingPointers(deviceId);
+ });
+}
+
+void TouchState::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
for (TouchedWindow& touchedWindow : windows) {
- touchedWindow.removeTouchingPointer(pointerId);
+ touchedWindow.removeTouchingPointer(deviceId, pointerId);
}
clearWindowsWithoutPointers();
}
-void TouchState::removeTouchedPointerFromWindow(
- int32_t pointerId, const sp<android::gui::WindowInfoHandle>& windowHandle) {
+void TouchState::removeTouchingPointerFromWindow(
+ DeviceId deviceId, int32_t pointerId,
+ const sp<android::gui::WindowInfoHandle>& windowHandle) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.removeTouchingPointer(pointerId);
+ touchedWindow.removeTouchingPointer(deviceId, pointerId);
clearWindowsWithoutPointers();
return;
}
@@ -58,13 +74,13 @@
void TouchState::clearWindowsWithoutPointers() {
std::erase_if(windows, [](const TouchedWindow& w) {
- return w.pointerIds.none() && !w.hasHoveringPointers();
+ return !w.hasTouchingPointers() && !w.hasHoveringPointers();
});
}
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
+ std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
std::optional<nsecs_t> firstDownTimeInTarget) {
for (TouchedWindow& touchedWindow : windows) {
// We do not compare windows by token here because two windows that share the same token
@@ -75,11 +91,11 @@
touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_IS);
}
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
- // downTime set initially. Need to update existing window when an pointer is down for
- // the window.
- touchedWindow.pointerIds |= pointerIds;
- if (!touchedWindow.firstDownTimeInTarget.has_value()) {
- touchedWindow.firstDownTimeInTarget = firstDownTimeInTarget;
+ // downTime set initially. Need to update existing window when a pointer is down for the
+ // window.
+ touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
+ if (firstDownTimeInTarget) {
+ touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
return;
}
@@ -87,23 +103,25 @@
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
touchedWindow.targetFlags = targetFlags;
- touchedWindow.pointerIds = pointerIds;
- touchedWindow.firstDownTimeInTarget = firstDownTimeInTarget;
+ touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
+ if (firstDownTimeInTarget) {
+ touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
+ }
windows.push_back(touchedWindow);
}
void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
- int32_t hoveringDeviceId, int32_t hoveringPointerId) {
+ DeviceId deviceId, int32_t hoveringPointerId) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
+ touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
return;
}
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
- touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
+ touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
windows.push_back(touchedWindow);
}
@@ -130,12 +148,12 @@
}
}
-void TouchState::cancelPointersForWindowsExcept(std::bitset<MAX_POINTER_ID + 1> pointerIds,
+void TouchState::cancelPointersForWindowsExcept(DeviceId deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token) {
- if (pointerIds.none()) return;
- std::for_each(windows.begin(), windows.end(), [&pointerIds, &token](TouchedWindow& w) {
+ std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
if (w.windowHandle->getToken() != token) {
- w.pointerIds &= ~pointerIds;
+ w.removeTouchingPointers(deviceId, pointerIds);
}
});
clearWindowsWithoutPointers();
@@ -149,24 +167,29 @@
*/
void TouchState::cancelPointersForNonPilferingWindows() {
// First, find all pointers that are being pilfered, across all windows
- std::bitset<MAX_POINTER_ID + 1> allPilferedPointerIds;
- std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](const TouchedWindow& w) {
- allPilferedPointerIds |= w.pilferedPointerIds;
- });
+ std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
+ for (const TouchedWindow& w : windows) {
+ for (const auto& [deviceId, pilferedPointerIds] : w.getPilferingPointers()) {
+ allPilferedPointerIdsByDevice[deviceId] |= pilferedPointerIds;
+ }
+ };
// Optimization: most of the time, pilfering does not occur
- if (allPilferedPointerIds.none()) return;
+ if (allPilferedPointerIdsByDevice.empty()) return;
// Now, remove all pointers from every window that's being pilfered by other windows.
// For example, if window A is pilfering pointer 1 (only), and window B is pilfering pointer 2
// (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
// pilfered pointers will be disjoint across all windows, but there's no reason to cause that
// limitation here.
- std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](TouchedWindow& w) {
- std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
- w.pilferedPointerIds ^ allPilferedPointerIds;
- w.pointerIds &= ~pilferedByOtherWindows;
- });
+ for (const auto& [deviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
+ std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
+ std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
+ w.getPilferingPointers(deviceId) ^ allPilferedPointerIds;
+ // Remove all pointers pilfered by other windows
+ w.removeTouchingPointers(deviceId, pilferedByOtherWindows);
+ });
+ }
clearWindowsWithoutPointers();
}
@@ -216,7 +239,7 @@
bool TouchState::isDown() const {
return std::any_of(windows.begin(), windows.end(),
- [](const TouchedWindow& window) { return window.pointerIds.any(); });
+ [](const TouchedWindow& window) { return window.hasTouchingPointers(); });
}
bool TouchState::hasHoveringPointers() const {
@@ -224,11 +247,11 @@
[](const TouchedWindow& window) { return window.hasHoveringPointers(); });
}
-std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId,
+std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
int32_t pointerId) const {
std::set<sp<WindowInfoHandle>> out;
for (const TouchedWindow& window : windows) {
- if (window.hasHoveringPointer(hoveringDeviceId, pointerId)) {
+ if (window.hasHoveringPointer(deviceId, pointerId)) {
out.insert(window.windowHandle);
}
}
@@ -242,22 +265,17 @@
clearWindowsWithoutPointers();
}
-void TouchState::removeAllPointersForDevice(int32_t removedDeviceId) {
+void TouchState::removeAllPointersForDevice(DeviceId deviceId) {
for (TouchedWindow& window : windows) {
- window.removeAllHoveringPointersForDevice(removedDeviceId);
+ window.removeAllHoveringPointersForDevice(deviceId);
+ window.removeAllTouchingPointersForDevice(deviceId);
}
- if (deviceId == removedDeviceId) {
- for (TouchedWindow& window : windows) {
- window.removeAllTouchingPointers();
- }
- }
+
clearWindowsWithoutPointers();
}
std::string TouchState::dump() const {
std::string out;
- out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
- inputEventSourceToString(source).c_str());
if (!windows.empty()) {
out += " Windows:\n";
for (size_t i = 0; i < windows.size(); i++) {
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 15b840f..39e63e5 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -29,11 +29,6 @@
namespace inputdispatcher {
struct TouchState {
- // id of the device that is currently down, others are rejected
- int32_t deviceId = -1;
- // source of the device that is current down, others are rejected
- uint32_t source = 0;
-
std::vector<TouchedWindow> windows;
TouchState() = default;
@@ -43,24 +38,28 @@
void reset();
void clearWindowsWithoutPointers();
- void removeTouchedPointer(int32_t pointerId);
- void removeTouchedPointerFromWindow(int32_t pointerId,
- const sp<android::gui::WindowInfoHandle>& windowHandle);
+ std::set<DeviceId> getActiveDeviceIds() const;
+
+ bool hasTouchingPointers(DeviceId deviceId) const;
+ void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
+ void removeTouchingPointerFromWindow(DeviceId deviceId, int32_t pointerId,
+ const sp<android::gui::WindowInfoHandle>& windowHandle);
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
+ std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- int32_t deviceId, int32_t hoveringPointerId);
- void removeHoveringPointer(int32_t deviceId, int32_t hoveringPointerId);
+ DeviceId deviceId, int32_t hoveringPointerId);
+ void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId);
void clearHoveringPointers();
- void removeAllPointersForDevice(int32_t removedDeviceId);
+ void removeAllPointersForDevice(DeviceId deviceId);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
// Cancel pointers for current set of windows except the window with particular binder token.
- void cancelPointersForWindowsExcept(std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ void cancelPointersForWindowsExcept(DeviceId deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token);
// Cancel pointers for current set of non-pilfering windows i.e. windows with isPilferingWindow
// set to false.
@@ -76,7 +75,7 @@
bool hasHoveringPointers() const;
std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
- int32_t deviceId, int32_t pointerId) const;
+ DeviceId deviceId, int32_t pointerId) const;
std::string dump() const;
};
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index d55d657..9807a6d 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -16,6 +16,7 @@
#include "TouchedWindow.h"
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <input/PrintTools.h>
@@ -26,67 +27,232 @@
namespace inputdispatcher {
bool TouchedWindow::hasHoveringPointers() const {
- return !mHoveringPointerIdsByDevice.empty();
+ for (const auto& [_, state] : mDeviceStates) {
+ if (state.hoveringPointerIds.any()) {
+ return true;
+ }
+ }
+ return false;
}
-bool TouchedWindow::hasHoveringPointers(int32_t deviceId) const {
- return mHoveringPointerIdsByDevice.find(deviceId) != mHoveringPointerIdsByDevice.end();
+bool TouchedWindow::hasHoveringPointers(DeviceId deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return false;
+ }
+ const DeviceState& state = stateIt->second;
+
+ return state.hoveringPointerIds.any();
}
void TouchedWindow::clearHoveringPointers() {
- mHoveringPointerIdsByDevice.clear();
+ for (auto& [_, state] : mDeviceStates) {
+ state.hoveringPointerIds.reset();
+ }
+
+ std::erase_if(mDeviceStates, [](const auto& pair) { return !pair.second.hasPointers(); });
}
-bool TouchedWindow::hasHoveringPointer(int32_t deviceId, int32_t pointerId) const {
- auto it = mHoveringPointerIdsByDevice.find(deviceId);
- if (it == mHoveringPointerIdsByDevice.end()) {
+bool TouchedWindow::hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
return false;
}
- return it->second.test(pointerId);
+ const DeviceState& state = stateIt->second;
+
+ return state.hoveringPointerIds.test(pointerId);
}
-void TouchedWindow::addHoveringPointer(int32_t deviceId, int32_t pointerId) {
- const auto [it, _] = mHoveringPointerIdsByDevice.insert({deviceId, {}});
- it->second.set(pointerId);
+void TouchedWindow::addHoveringPointer(DeviceId deviceId, int32_t pointerId) {
+ mDeviceStates[deviceId].hoveringPointerIds.set(pointerId);
}
-void TouchedWindow::removeTouchingPointer(int32_t pointerId) {
- pointerIds.reset(pointerId);
- pilferedPointerIds.reset(pointerId);
- if (pointerIds.none()) {
- firstDownTimeInTarget.reset();
+void TouchedWindow::addTouchingPointers(DeviceId deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointers) {
+ mDeviceStates[deviceId].touchingPointerIds |= pointers;
+}
+
+bool TouchedWindow::hasTouchingPointers() const {
+ for (const auto& [_, state] : mDeviceStates) {
+ if (state.touchingPointerIds.any()) {
+ return true;
+ }
}
+ return false;
}
-void TouchedWindow::removeAllTouchingPointers() {
- pointerIds.reset();
+bool TouchedWindow::hasTouchingPointers(DeviceId deviceId) const {
+ return getTouchingPointers(deviceId).any();
}
-void TouchedWindow::removeHoveringPointer(int32_t deviceId, int32_t pointerId) {
- const auto it = mHoveringPointerIdsByDevice.find(deviceId);
- if (it == mHoveringPointerIdsByDevice.end()) {
+bool TouchedWindow::hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const {
+ return getTouchingPointers(deviceId).test(pointerId);
+}
+
+std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getTouchingPointers(DeviceId deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return {};
+ }
+ const DeviceState& state = stateIt->second;
+
+ return state.touchingPointerIds;
+}
+
+void TouchedWindow::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ pointerIds.set(pointerId, true);
+
+ removeTouchingPointers(deviceId, pointerIds);
+}
+
+void TouchedWindow::removeTouchingPointers(DeviceId deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointers) {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
return;
}
- it->second.set(pointerId, false);
+ DeviceState& state = stateIt->second;
- if (it->second.none()) {
- mHoveringPointerIdsByDevice.erase(deviceId);
+ state.touchingPointerIds &= ~pointers;
+ state.pilferingPointerIds &= ~pointers;
+
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
}
}
-void TouchedWindow::removeAllHoveringPointersForDevice(int32_t deviceId) {
- mHoveringPointerIdsByDevice.erase(deviceId);
+std::set<DeviceId> TouchedWindow::getTouchingDeviceIds() const {
+ std::set<DeviceId> deviceIds;
+ for (const auto& [deviceId, _] : mDeviceStates) {
+ deviceIds.insert(deviceId);
+ }
+ return deviceIds;
+}
+
+std::set<DeviceId> TouchedWindow::getActiveDeviceIds() const {
+ std::set<DeviceId> out;
+ for (const auto& [deviceId, _] : mDeviceStates) {
+ out.emplace(deviceId);
+ }
+ return out;
+}
+
+bool TouchedWindow::hasPilferingPointers(DeviceId deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return false;
+ }
+ const DeviceState& state = stateIt->second;
+
+ return state.pilferingPointerIds.any();
+}
+
+void TouchedWindow::addPilferingPointers(DeviceId deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds) {
+ mDeviceStates[deviceId].pilferingPointerIds |= pointerIds;
+}
+
+void TouchedWindow::addPilferingPointer(DeviceId deviceId, int32_t pointerId) {
+ mDeviceStates[deviceId].pilferingPointerIds.set(pointerId);
+}
+
+std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getPilferingPointers(DeviceId deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return {};
+ }
+ const DeviceState& state = stateIt->second;
+
+ return state.pilferingPointerIds;
+}
+
+std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> TouchedWindow::getPilferingPointers() const {
+ std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> out;
+ for (const auto& [deviceId, state] : mDeviceStates) {
+ out.emplace(deviceId, state.pilferingPointerIds);
+ }
+ return out;
+}
+
+std::optional<nsecs_t> TouchedWindow::getDownTimeInTarget(DeviceId deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return {};
+ }
+ const DeviceState& state = stateIt->second;
+ return state.downTimeInTarget;
+}
+
+void TouchedWindow::trySetDownTimeInTarget(DeviceId deviceId, nsecs_t downTime) {
+ auto [stateIt, _] = mDeviceStates.try_emplace(deviceId);
+ DeviceState& state = stateIt->second;
+
+ if (!state.downTimeInTarget) {
+ state.downTimeInTarget = downTime;
+ }
+}
+
+void TouchedWindow::removeAllTouchingPointersForDevice(DeviceId deviceId) {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return;
+ }
+ DeviceState& state = stateIt->second;
+
+ state.touchingPointerIds.reset();
+ state.pilferingPointerIds.reset();
+ state.downTimeInTarget.reset();
+
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
+ }
+}
+
+void TouchedWindow::removeHoveringPointer(DeviceId deviceId, int32_t pointerId) {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return;
+ }
+ DeviceState& state = stateIt->second;
+
+ state.hoveringPointerIds.set(pointerId, false);
+
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
+ }
+}
+
+void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return;
+ }
+ DeviceState& state = stateIt->second;
+
+ state.hoveringPointerIds.reset();
+
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
+ }
+}
+
+std::string TouchedWindow::deviceStateToString(const TouchedWindow::DeviceState& state) {
+ return StringPrintf("[touchingPointerIds=%s, "
+ "downTimeInTarget=%s, hoveringPointerIds=%s, pilferingPointerIds=%s]",
+ bitsetToString(state.touchingPointerIds).c_str(),
+ toString(state.downTimeInTarget).c_str(),
+ bitsetToString(state.hoveringPointerIds).c_str(),
+ bitsetToString(state.pilferingPointerIds).c_str());
}
std::string TouchedWindow::dump() const {
std::string out;
- std::string hoveringPointers =
- dumpMap(mHoveringPointerIdsByDevice, constToString, bitsetToString);
- out += StringPrintf("name='%s', pointerIds=%s, targetFlags=%s, firstDownTimeInTarget=%s, "
- "mHoveringPointerIdsByDevice=%s, pilferedPointerIds=%s\n",
- windowHandle->getName().c_str(), bitsetToString(pointerIds).c_str(),
- targetFlags.string().c_str(), toString(firstDownTimeInTarget).c_str(),
- hoveringPointers.c_str(), bitsetToString(pilferedPointerIds).c_str());
+ std::string deviceStates =
+ dumpMap(mDeviceStates, constToString, TouchedWindow::deviceStateToString);
+ out += StringPrintf("name='%s', targetFlags=%s, mDeviceStates=%s\n",
+ windowHandle->getName().c_str(), targetFlags.string().c_str(),
+ deviceStates.c_str());
return out;
}
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 43e7169..0a38f9f 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -20,6 +20,7 @@
#include <input/Input.h>
#include <utils/BitSet.h>
#include <bitset>
+#include <set>
#include "InputTarget.h"
namespace android {
@@ -30,28 +31,65 @@
struct TouchedWindow {
sp<gui::WindowInfoHandle> windowHandle;
ftl::Flags<InputTarget::Flags> targetFlags;
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- // The pointer ids of the pointers that this window is currently pilfering
- std::bitset<MAX_POINTER_ID + 1> pilferedPointerIds;
- // Time at which the first action down occurred on this window.
- // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
- std::optional<nsecs_t> firstDownTimeInTarget;
+ // Hovering
bool hasHoveringPointers() const;
- bool hasHoveringPointers(int32_t deviceId) const;
+ bool hasHoveringPointers(DeviceId deviceId) const;
+ bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const;
+ void addHoveringPointer(DeviceId deviceId, int32_t pointerId);
+ void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
- bool hasHoveringPointer(int32_t deviceId, int32_t pointerId) const;
- void addHoveringPointer(int32_t deviceId, int32_t pointerId);
- void removeHoveringPointer(int32_t deviceId, int32_t pointerId);
- void removeTouchingPointer(int32_t pointerId);
+ // Touching
+ bool hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const;
+ bool hasTouchingPointers() const;
+ bool hasTouchingPointers(DeviceId deviceId) const;
+ std::bitset<MAX_POINTER_ID + 1> getTouchingPointers(DeviceId deviceId) const;
+ void addTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
+ void removeTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ /**
+ * Get the currently active touching device id. If there isn't exactly 1 touching device, return
+ * nullopt.
+ */
+ std::set<DeviceId> getTouchingDeviceIds() const;
+ /**
+ * The ids of devices that are currently touching or hovering.
+ */
+ std::set<DeviceId> getActiveDeviceIds() const;
- void removeAllTouchingPointers();
- void removeAllHoveringPointersForDevice(int32_t deviceId);
+ // Pilfering pointers
+ bool hasPilferingPointers(DeviceId deviceId) const;
+ void addPilferingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointerIds);
+ void addPilferingPointer(DeviceId deviceId, int32_t pointerId);
+ std::bitset<MAX_POINTER_ID + 1> getPilferingPointers(DeviceId deviceId) const;
+ std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> getPilferingPointers() const;
+
+ // Down time
+ std::optional<nsecs_t> getDownTimeInTarget(DeviceId deviceId) const;
+ void trySetDownTimeInTarget(DeviceId deviceId, nsecs_t downTime);
+
+ void removeAllTouchingPointersForDevice(DeviceId deviceId);
+ void removeAllHoveringPointersForDevice(DeviceId deviceId);
void clearHoveringPointers();
std::string dump() const;
private:
- std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> mHoveringPointerIdsByDevice;
+ struct DeviceState {
+ std::bitset<MAX_POINTER_ID + 1> touchingPointerIds;
+ // The pointer ids of the pointers that this window is currently pilfering, by device
+ std::bitset<MAX_POINTER_ID + 1> pilferingPointerIds;
+ // Time at which the first action down occurred on this window, for each device
+ // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE
+ // scenario.
+ std::optional<nsecs_t> downTimeInTarget;
+ std::bitset<MAX_POINTER_ID + 1> hoveringPointerIds;
+
+ bool hasPointers() const { return touchingPointerIds.any() || hoveringPointerIds.any(); };
+ };
+
+ std::map<DeviceId, DeviceState> mDeviceStates;
+
+ static std::string deviceStateToString(const TouchedWindow::DeviceState& state);
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index c752ddd..d099b44 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -39,7 +39,7 @@
/* Dumps the state of the input dispatcher.
*
* This method may be called on any thread (usually by the input manager). */
- virtual void dump(std::string& dump) = 0;
+ virtual void dump(std::string& dump) const = 0;
/* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
virtual void monitor() = 0;
@@ -50,7 +50,7 @@
* Return true if the dispatcher is idle.
* Return false if the timeout waiting for the dispatcher to become idle has expired.
*/
- virtual bool waitForIdle() = 0;
+ virtual bool waitForIdle() const = 0;
/* Make the dispatcher start processing events.
*
@@ -76,7 +76,7 @@
* perform all necessary permission checks prior to injecting events.
*/
virtual android::os::InputEventInjectionResult injectInputEvent(
- const InputEvent* event, std::optional<int32_t> targetUid,
+ const InputEvent* event, std::optional<gui::Uid> targetUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) = 0;
@@ -87,14 +87,6 @@
*/
virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) = 0;
- /* Sets the list of input windows per display.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<gui::WindowInfoHandle>>>&
- handlesPerDisplay) = 0;
-
/* Sets the focused application on the given display.
*
* This method may be called on any thread (usually by the input manager).
@@ -134,7 +126,7 @@
*
* Returns true when changing touch mode state.
*/
- virtual bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
+ virtual bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission,
int32_t displayId) = 0;
/**
@@ -182,7 +174,7 @@
*/
virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
const std::string& name,
- int32_t pid) = 0;
+ gui::Pid pid) = 0;
/* Removes input channels that will no longer receive input events.
*
@@ -226,11 +218,10 @@
*/
virtual void cancelCurrentTouch() = 0;
- /**
- * Request that the InputDispatcher's configuration, which can be obtained through the policy,
- * be updated.
+ /*
+ * Updates key repeat configuration timeout and delay.
*/
- virtual void requestRefreshConfiguration() = 0;
+ virtual void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) = 0;
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 5539915..af28e48 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -22,14 +22,14 @@
#include <gui/InputApplication.h>
#include <input/Input.h>
#include <utils/RefBase.h>
+#include <set>
namespace android {
-
/*
* Input dispatcher policy interface.
*
- * The input reader policy is used by the input reader to interact with the Window Manager
+ * The input dispatcher policy is used by the input dispatcher to interact with the Window Manager
* and other system components.
*
* The actual implementation is partially supported by callbacks into the DVM
@@ -53,7 +53,7 @@
* pid of the owner. The string reason contains information about the input event that we
* haven't received a response for.
*/
- virtual void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<int32_t> pid,
+ virtual void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid,
const std::string& reason) = 0;
/* Notifies the system that a window just became responsive. This is only called after the
@@ -61,7 +61,7 @@
* no longer should be shown to the user. The window is eligible to cause a new ANR in the
* future.
*/
- virtual void notifyWindowResponsive(const sp<IBinder>& token, std::optional<int32_t> pid) = 0;
+ virtual void notifyWindowResponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
@@ -73,9 +73,6 @@
InputDeviceSensorAccuracy accuracy) = 0;
virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0;
- /* Gets the input dispatcher configuration. */
- virtual InputDispatcherConfiguration getDispatcherConfiguration() = 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
@@ -136,6 +133,10 @@
/* Notifies the policy that the drag window has moved over to another window */
virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
+
+ /* Notifies the policy that there was an input device interaction with apps. */
+ virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index a93a2ea..25e1d21 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -437,7 +437,8 @@
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) = 0;
+ const InputDeviceIdentifier& identifier,
+ const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) = 0;
/* Gets a user-supplied alias for a particular input device, or an empty string if none. */
virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
@@ -447,6 +448,9 @@
const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0;
/* Notifies the input reader policy that a stylus gesture has started. */
virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
+
+ /* Returns true if any InputConnection is currently active. */
+ virtual bool isInputMethodConnectionActive() = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index 7d29dd9..736b1e0 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -104,9 +104,9 @@
MotionClassification classification;
int32_t edgeFlags;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
+ // Vectors 'pointerProperties' and 'pointerCoords' must always have the same number of elements
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
float xPrecision;
float yPrecision;
/**
@@ -131,11 +131,13 @@
float yCursorPosition, nsecs_t downTime,
const std::vector<TouchVideoFrame>& videoFrames);
- NotifyMotionArgs(const NotifyMotionArgs& other);
+ NotifyMotionArgs(const NotifyMotionArgs& other) = default;
NotifyMotionArgs& operator=(const android::NotifyMotionArgs&) = default;
bool operator==(const NotifyMotionArgs& rhs) const;
+ inline size_t getPointerCount() const { return pointerProperties.size(); }
+
std::string dump() const;
};
diff --git a/services/inputflinger/tests/EventBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h
similarity index 60%
rename from services/inputflinger/tests/EventBuilders.h
rename to services/inputflinger/include/NotifyArgsBuilders.h
index 606a57d..e4363a4 100644
--- a/services/inputflinger/tests/EventBuilders.h
+++ b/services/inputflinger/include/NotifyArgsBuilders.h
@@ -19,145 +19,15 @@
#include <NotifyArgs.h>
#include <android/input.h>
#include <attestation/HmacKeyManager.h>
+#include <gui/constants.h>
#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <utils/Timers.h> // for nsecs_t, systemTime
+
#include <vector>
namespace android {
-// An arbitrary device id.
-static constexpr uint32_t DEFAULT_DEVICE_ID = 1;
-
-// The default policy flags to use for event injection by tests.
-static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
-
-class PointerBuilder {
-public:
- PointerBuilder(int32_t id, ToolType toolType) {
- mProperties.clear();
- mProperties.id = id;
- mProperties.toolType = toolType;
- mCoords.clear();
- }
-
- PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
-
- PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
-
- PointerBuilder& axis(int32_t axis, float value) {
- mCoords.setAxisValue(axis, value);
- return *this;
- }
-
- PointerProperties buildProperties() const { return mProperties; }
-
- PointerCoords buildCoords() const { return mCoords; }
-
-private:
- PointerProperties mProperties;
- PointerCoords mCoords;
-};
-
-class MotionEventBuilder {
-public:
- MotionEventBuilder(int32_t action, int32_t source) {
- mAction = action;
- mSource = source;
- mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
- mDownTime = mEventTime;
- }
-
- MotionEventBuilder& deviceId(int32_t deviceId) {
- mDeviceId = deviceId;
- return *this;
- }
-
- MotionEventBuilder& downTime(nsecs_t downTime) {
- mDownTime = downTime;
- return *this;
- }
-
- MotionEventBuilder& eventTime(nsecs_t eventTime) {
- mEventTime = eventTime;
- return *this;
- }
-
- MotionEventBuilder& displayId(int32_t displayId) {
- mDisplayId = displayId;
- return *this;
- }
-
- MotionEventBuilder& actionButton(int32_t actionButton) {
- mActionButton = actionButton;
- return *this;
- }
-
- MotionEventBuilder& buttonState(int32_t buttonState) {
- mButtonState = buttonState;
- return *this;
- }
-
- MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
- mRawXCursorPosition = rawXCursorPosition;
- return *this;
- }
-
- MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
- mRawYCursorPosition = rawYCursorPosition;
- return *this;
- }
-
- MotionEventBuilder& pointer(PointerBuilder pointer) {
- mPointers.push_back(pointer);
- return *this;
- }
-
- MotionEventBuilder& addFlag(uint32_t flags) {
- mFlags |= flags;
- return *this;
- }
-
- MotionEvent build() {
- std::vector<PointerProperties> pointerProperties;
- std::vector<PointerCoords> pointerCoords;
- for (const PointerBuilder& pointer : mPointers) {
- pointerProperties.push_back(pointer.buildProperties());
- pointerCoords.push_back(pointer.buildCoords());
- }
-
- // Set mouse cursor position for the most common cases to avoid boilerplate.
- if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- mRawXCursorPosition = pointerCoords[0].getX();
- mRawYCursorPosition = pointerCoords[0].getY();
- }
-
- MotionEvent event;
- static const ui::Transform kIdentityTransform;
- event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
- mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
- MotionClassification::NONE, kIdentityTransform,
- /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
- mRawYCursorPosition, kIdentityTransform, mDownTime, mEventTime,
- mPointers.size(), pointerProperties.data(), pointerCoords.data());
- return event;
- }
-
-private:
- int32_t mAction;
- int32_t mDeviceId{DEFAULT_DEVICE_ID};
- int32_t mSource;
- nsecs_t mDownTime;
- nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
- int32_t mActionButton{0};
- int32_t mButtonState{0};
- int32_t mFlags{0};
- float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
- float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
-
- std::vector<PointerBuilder> mPointers;
-};
-
class MotionArgsBuilder {
public:
MotionArgsBuilder(int32_t action, int32_t source) {
@@ -242,6 +112,10 @@
mRawYCursorPosition = pointerCoords[0].getY();
}
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ addFlag(AMOTION_EVENT_FLAG_CANCELED);
+ }
+
return {InputEvent::nextId(),
mEventTime,
/*readTime=*/mEventTime,
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index b0edb57..c0c6d1f 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -52,6 +52,7 @@
"mapper/RotaryEncoderInputMapper.cpp",
"mapper/SensorInputMapper.cpp",
"mapper/SingleTouchInputMapper.cpp",
+ "mapper/SlopController.cpp",
"mapper/SwitchInputMapper.cpp",
"mapper/TouchCursorInputMapperCommon.cpp",
"mapper/TouchInputMapper.cpp",
@@ -79,6 +80,7 @@
"libcrypto",
"libcutils",
"libjsoncpp",
+ "libinput",
"liblog",
"libPlatformProperties",
"libstatslog",
@@ -97,13 +99,13 @@
target: {
android: {
shared_libs: [
- "libinput",
+ "libstatspull",
],
},
host: {
static_libs: [
- "libinput",
"libbinder",
+ "libstatspull",
],
},
},
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 04747cc..e69c99e 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -538,7 +538,8 @@
associatedDevice(std::move(assocDev)),
controllerNumber(0),
enabled(true),
- isVirtual(fd < 0) {}
+ isVirtual(fd < 0),
+ currentFrameDropped(false) {}
EventHub::Device::~Device() {
close();
@@ -612,6 +613,45 @@
}
bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
+
+ // Query the initial state of keys and switches, which is tracked by EventHub.
+ readDeviceState();
+}
+
+void EventHub::Device::readDeviceState() {
+ if (readDeviceBitMask(EVIOCGKEY(0), keyState) < 0) {
+ ALOGD("Unable to query the global key state for %s: %s", path.c_str(), strerror(errno));
+ }
+ if (readDeviceBitMask(EVIOCGSW(0), swState) < 0) {
+ ALOGD("Unable to query the global switch state for %s: %s", path.c_str(), strerror(errno));
+ }
+
+ // Read absolute axis info and values for all available axes for the device.
+ populateAbsoluteAxisStates();
+}
+
+void EventHub::Device::populateAbsoluteAxisStates() {
+ absState.clear();
+
+ for (int axis = 0; axis <= ABS_MAX; axis++) {
+ if (!absBitmask.test(axis)) {
+ continue;
+ }
+ struct input_absinfo info {};
+ if (ioctl(fd, EVIOCGABS(axis), &info)) {
+ ALOGE("Error reading absolute controller %d for device %s fd %d: %s", axis,
+ identifier.name.c_str(), fd, strerror(errno));
+ continue;
+ }
+ auto& [axisInfo, value] = absState[axis];
+ axisInfo.valid = true;
+ axisInfo.minValue = info.minimum;
+ axisInfo.maxValue = info.maximum;
+ axisInfo.flat = info.flat;
+ axisInfo.fuzz = info.fuzz;
+ axisInfo.resolution = info.resolution;
+ value = info.value;
+ }
}
bool EventHub::Device::hasKeycodeLocked(int keycode) const {
@@ -729,6 +769,66 @@
return NAME_NOT_FOUND;
}
+void EventHub::Device::trackInputEvent(const struct input_event& event) {
+ switch (event.type) {
+ case EV_KEY: {
+ LOG_ALWAYS_FATAL_IF(!currentFrameDropped &&
+ !keyState.set(static_cast<size_t>(event.code),
+ event.value != 0),
+ "%s: device '%s' received invalid EV_KEY event code: %s value: %d",
+ __func__, identifier.name.c_str(),
+ InputEventLookup::getLinuxEvdevLabel(EV_KEY, event.code, 1)
+ .code.c_str(),
+ event.value);
+ break;
+ }
+ case EV_SW: {
+ LOG_ALWAYS_FATAL_IF(!currentFrameDropped &&
+ !swState.set(static_cast<size_t>(event.code),
+ event.value != 0),
+ "%s: device '%s' received invalid EV_SW event code: %s value: %d",
+ __func__, identifier.name.c_str(),
+ InputEventLookup::getLinuxEvdevLabel(EV_SW, event.code, 1)
+ .code.c_str(),
+ event.value);
+ break;
+ }
+ case EV_ABS: {
+ if (currentFrameDropped) {
+ break;
+ }
+ auto it = absState.find(event.code);
+ LOG_ALWAYS_FATAL_IF(it == absState.end(),
+ "%s: device '%s' received invalid EV_ABS event code: %s value: %d",
+ __func__, identifier.name.c_str(),
+ InputEventLookup::getLinuxEvdevLabel(EV_ABS, event.code, 0)
+ .code.c_str(),
+ event.value);
+ it->second.value = event.value;
+ break;
+ }
+ case EV_SYN: {
+ switch (event.code) {
+ case SYN_REPORT:
+ currentFrameDropped = false;
+ break;
+ case SYN_DROPPED:
+ // When we receive SYN_DROPPED, all events in the current frame should be
+ // dropped. We query the state of the device to synchronize our device state
+ // with the kernel's to account for the dropped events.
+ currentFrameDropped = true;
+ readDeviceState();
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
/**
* Get the capabilities for the current process.
* Crashes the system if unable to create / check / destroy the capabilities object.
@@ -894,31 +994,23 @@
status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const {
outAxisInfo->clear();
-
- if (axis >= 0 && axis <= ABS_MAX) {
- std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
- struct input_absinfo info;
- if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
- ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
- device->identifier.name.c_str(), device->fd, errno);
- return -errno;
- }
-
- if (info.minimum != info.maximum) {
- outAxisInfo->valid = true;
- outAxisInfo->minValue = info.minimum;
- outAxisInfo->maxValue = info.maximum;
- outAxisInfo->flat = info.flat;
- outAxisInfo->fuzz = info.fuzz;
- outAxisInfo->resolution = info.resolution;
- }
- return OK;
- }
+ if (axis < 0 || axis > ABS_MAX) {
+ return NAME_NOT_FOUND;
}
- return -1;
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+ // 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;
+ }
+ *outAxisInfo = it->second.info;
+ return OK;
}
bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
@@ -949,38 +1041,34 @@
}
int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
- if (scanCode >= 0 && scanCode <= KEY_MAX) {
- std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->keyBitmask.test(scanCode)) {
- if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
- return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
- }
- }
+ if (scanCode < 0 || scanCode > KEY_MAX) {
+ return AKEY_STATE_UNKNOWN;
}
- return AKEY_STATE_UNKNOWN;
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->keyBitmask.test(scanCode)) {
+ return AKEY_STATE_UNKNOWN;
+ }
+ return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
- std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode);
- if (scanCodes.size() != 0) {
- if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
- for (size_t i = 0; i < scanCodes.size(); i++) {
- int32_t sc = scanCodes[i];
- if (sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc)) {
- return AKEY_STATE_DOWN;
- }
- }
- return AKEY_STATE_UP;
- }
- }
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->keyMap.haveKeyLayout()) {
+ return AKEY_STATE_UNKNOWN;
}
- return AKEY_STATE_UNKNOWN;
+ const std::vector<int32_t> scanCodes =
+ device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode);
+ if (scanCodes.empty()) {
+ return AKEY_STATE_UNKNOWN;
+ }
+ return std::any_of(scanCodes.begin(), scanCodes.end(),
+ [&device](const int32_t sc) {
+ return sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc);
+ })
+ ? AKEY_STATE_DOWN
+ : AKEY_STATE_UP;
}
int32_t EventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
@@ -1024,39 +1112,33 @@
}
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
- if (sw >= 0 && sw <= SW_MAX) {
- std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->swBitmask.test(sw)) {
- if (device->readDeviceBitMask(EVIOCGSW(0), device->swState) >= 0) {
- return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
- }
- }
+ if (sw < 0 || sw > SW_MAX) {
+ return AKEY_STATE_UNKNOWN;
}
- return AKEY_STATE_UNKNOWN;
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->swBitmask.test(sw)) {
+ return AKEY_STATE_UNKNOWN;
+ }
+ 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;
-
- if (axis >= 0 && axis <= ABS_MAX) {
- std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
- struct input_absinfo info;
- if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
- ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
- device->identifier.name.c_str(), device->fd, errno);
- return -errno;
- }
-
- *outValue = info.value;
- return OK;
- }
+ if (axis < 0 || axis > ABS_MAX) {
+ return NAME_NOT_FOUND;
}
- return -1;
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd()) {
+ return NAME_NOT_FOUND;
+ }
+ const auto it = device->absState.find(axis);
+ if (it == device->absState.end()) {
+ return NAME_NOT_FOUND;
+ }
+ *outValue = it->second.value;
+ return OK;
}
bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
@@ -1909,6 +1991,7 @@
const size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
+ device->trackInputEvent(iev);
events.push_back({
.when = processEventTimestamp(iev),
.readTime = systemTime(SYSTEM_TIME_MONOTONIC),
@@ -2786,6 +2869,31 @@
device->associatedDevice
? device->associatedDevice->sysfsRootPath.c_str()
: "<none>");
+ if (device->keyBitmask.any(0, KEY_MAX + 1)) {
+ const auto pressedKeys = device->keyState.dumpSetIndices(", ", [](int i) {
+ return InputEventLookup::getLinuxEvdevLabel(EV_KEY, i, 1).code;
+ });
+ dump += StringPrintf(INDENT3 "KeyState (pressed): %s\n", pressedKeys.c_str());
+ }
+ if (device->swBitmask.any(0, SW_MAX + 1)) {
+ const auto pressedSwitches = device->swState.dumpSetIndices(", ", [](int i) {
+ return InputEventLookup::getLinuxEvdevLabel(EV_SW, i, 1).code;
+ });
+ dump += StringPrintf(INDENT3 "SwState (pressed): %s\n", pressedSwitches.c_str());
+ }
+ if (!device->absState.empty()) {
+ std::string axisValues;
+ for (const auto& [axis, state] : device->absState) {
+ if (!axisValues.empty()) {
+ axisValues += ", ";
+ }
+ axisValues += StringPrintf("%s=%d",
+ InputEventLookup::getLinuxEvdevLabel(EV_ABS, axis, 0)
+ .code.c_str(),
+ state.value);
+ }
+ dump += INDENT3 "AbsState: " + axisValues + "\n";
+ }
}
dump += INDENT "Unattached video devices:\n";
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 0a64a1c..bacc720 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -48,6 +48,7 @@
mIdentifier(identifier),
mClasses(0),
mSources(0),
+ mIsWaking(false),
mIsExternal(false),
mHasMic(false),
mDropUntilNextSync(false) {}
@@ -101,6 +102,7 @@
dump += StringPrintf(INDENT "%s", eventHubDevStr.c_str());
dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+ dump += StringPrintf(INDENT2 "IsWaking: %s\n", toString(mIsWaking));
dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
if (mAssociatedDisplayPort) {
dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
@@ -220,23 +222,7 @@
mAssociatedDeviceType =
getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location);
- }
-
- if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
- if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
- std::shared_ptr<KeyCharacterMap> keyboardLayout =
- mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
- bool shouldBumpGeneration = false;
- for_each_subdevice(
- [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
- if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
- shouldBumpGeneration = true;
- }
- });
- if (shouldBumpGeneration) {
- bumpGeneration();
- }
- }
+ mIsWaking = mConfiguration.getBool("device.wake").value_or(false);
}
if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) {
@@ -376,9 +362,25 @@
}
--count;
}
+ postProcess(out);
return out;
}
+void InputDevice::postProcess(std::list<NotifyArgs>& args) const {
+ if (mIsWaking) {
+ // Update policy flags to request wake for the `NotifyArgs` that come from waking devices.
+ for (auto& arg : args) {
+ if (const auto notifyMotionArgs = std::get_if<NotifyMotionArgs>(&arg)) {
+ notifyMotionArgs->policyFlags |= POLICY_FLAG_WAKE;
+ } else if (const auto notifySwitchArgs = std::get_if<NotifySwitchArgs>(&arg)) {
+ notifySwitchArgs->policyFlags |= POLICY_FLAG_WAKE;
+ } else if (const auto notifyKeyArgs = std::get_if<NotifyKeyArgs>(&arg)) {
+ notifyKeyArgs->policyFlags |= POLICY_FLAG_WAKE;
+ }
+ }
+ }
+}
+
std::list<NotifyArgs> InputDevice::timeoutExpired(nsecs_t when) {
std::list<NotifyArgs> out;
for_each_mapper([&](InputMapper& mapper) { out += mapper.timeoutExpired(when); });
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index ea95f78..08600b2 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -1040,6 +1040,16 @@
return mReader->getLedMetaStateLocked();
}
+void InputReader::ContextImpl::setPreventingTouchpadTaps(bool prevent) {
+ // lock is already held by the input loop
+ mReader->mPreventingTouchpadTaps = prevent;
+}
+
+bool InputReader::ContextImpl::isPreventingTouchpadTaps() {
+ // lock is already held by the input loop
+ return mReader->mPreventingTouchpadTaps;
+}
+
void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
// lock is already held by the input loop
mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 20612c7..0bcab42 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -19,6 +19,8 @@
#include <bitset>
#include <climits>
#include <filesystem>
+#include <functional>
+#include <map>
#include <ostream>
#include <string>
#include <unordered_map>
@@ -411,7 +413,17 @@
* Note the parameter "bit" is an index to the bit, 0 <= bit < BITS.
*/
inline bool test(size_t bit) const {
- return (bit < BITS) ? mData[bit / WIDTH].test(bit % WIDTH) : false;
+ return (bit < BITS) && mData[bit / WIDTH].test(bit % WIDTH);
+ }
+ /* Sets the given bit in the bit array to given value.
+ * Returns true if the given bit is a valid index and thus was set successfully.
+ */
+ inline bool set(size_t bit, bool value) {
+ if (bit >= BITS) {
+ return false;
+ }
+ mData[bit / WIDTH].set(bit % WIDTH, value);
+ return true;
}
/* Returns total number of bytes needed for the array */
inline size_t bytes() { return (BITS + CHAR_BIT - 1) / CHAR_BIT; }
@@ -459,6 +471,20 @@
mData[i] = std::bitset<WIDTH>(buffer[i]);
}
}
+ /* Dump the indices in the bit array that are set. */
+ inline std::string dumpSetIndices(std::string separator,
+ std::function<std::string(size_t /*index*/)> format) {
+ std::string dmp;
+ for (size_t i = 0; i < BITS; i++) {
+ if (test(i)) {
+ if (!dmp.empty()) {
+ dmp += separator;
+ }
+ dmp += format(i);
+ }
+ }
+ return dmp.empty() ? "<none>" : dmp;
+ }
private:
std::array<std::bitset<WIDTH>, COUNT> mData;
@@ -600,16 +626,21 @@
ftl::Flags<InputDeviceClass> classes;
- BitArray<KEY_MAX> keyBitmask;
- BitArray<KEY_MAX> keyState;
- BitArray<ABS_MAX> absBitmask;
- BitArray<REL_MAX> relBitmask;
- BitArray<SW_MAX> swBitmask;
- BitArray<SW_MAX> swState;
- BitArray<LED_MAX> ledBitmask;
- BitArray<FF_MAX> ffBitmask;
- BitArray<INPUT_PROP_MAX> propBitmask;
- BitArray<MSC_MAX> mscBitmask;
+ BitArray<KEY_CNT> keyBitmask;
+ BitArray<KEY_CNT> keyState;
+ BitArray<REL_CNT> relBitmask;
+ BitArray<SW_CNT> swBitmask;
+ BitArray<SW_CNT> swState;
+ BitArray<LED_CNT> ledBitmask;
+ BitArray<FF_CNT> ffBitmask;
+ BitArray<INPUT_PROP_CNT> propBitmask;
+ BitArray<MSC_CNT> mscBitmask;
+ BitArray<ABS_CNT> absBitmask;
+ struct AxisState {
+ RawAbsoluteAxisInfo info;
+ int value;
+ };
+ std::map<int /*axis*/, AxisState> absState;
std::string configurationFile;
std::unique_ptr<PropertyMap> configuration;
@@ -643,6 +674,7 @@
status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray);
void configureFd();
+ void populateAbsoluteAxisStates();
bool hasKeycodeLocked(int keycode) const;
void loadConfigurationLocked();
bool loadVirtualKeyMapLocked();
@@ -652,6 +684,10 @@
void setLedForControllerLocked();
status_t mapLed(int32_t led, int32_t* outScanCode) const;
void setLedStateLocked(int32_t led, bool on);
+
+ bool currentFrameDropped;
+ void trackInputEvent(const struct input_event& event);
+ void readDeviceState();
};
/**
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 2f8e5bd..1cbcbf4 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -191,6 +191,7 @@
std::unique_ptr<PeripheralControllerInterface> mController;
uint32_t mSources;
+ bool mIsWaking;
bool mIsExternal;
std::optional<uint8_t> mAssociatedDisplayPort;
std::optional<std::string> mAssociatedDisplayUniqueId;
@@ -207,6 +208,10 @@
PropertyMap mConfiguration;
+ // Runs logic post a `process` call. This can be used to update the generated `NotifyArgs` as
+ // per the properties of the InputDevice.
+ void postProcess(std::list<NotifyArgs>& args) const;
+
// helpers to interate over the devices collection
// run a function against every mapper on every subdevice
inline void for_each_mapper(std::function<void(InputMapper&)> f) {
@@ -284,7 +289,18 @@
return mEventHub->getDeviceControllerNumber(mId);
}
inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
- return mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo);
+ if (const auto status = mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo); status != OK) {
+ return status;
+ }
+
+ // Validate axis info for InputDevice.
+ if (axisInfo->valid && axisInfo->minValue == axisInfo->maxValue) {
+ // Historically, we deem axes with the same min and max values as invalid to avoid
+ // dividing by zero when scaling by max - min.
+ // TODO(b/291772515): Perform axis info validation on a per-axis basis when it is used.
+ axisInfo->valid = false;
+ }
+ return OK;
}
inline bool hasRelativeAxis(int32_t code) const {
return mEventHub->hasRelativeAxis(mId, code);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9112913..01ec7c1 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -155,6 +155,9 @@
int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
int32_t getLedMetaState() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
+ void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock)
+ REQUIRES(mLock) override;
+ bool isPreventingTouchpadTaps() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
} mContext;
friend class ContextImpl;
@@ -185,6 +188,9 @@
std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
mDeviceToEventHubIdsMap GUARDED_BY(mLock);
+ // true if tap-to-click on touchpad currently disabled
+ bool mPreventingTouchpadTaps GUARDED_BY(mLock){false};
+
// low-level input event decoding and device management
[[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 0beace1..aed7563 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -62,6 +62,9 @@
virtual void updateLedMetaState(int32_t metaState) = 0;
virtual int32_t getLedMetaState() = 0;
+
+ virtual void setPreventingTouchpadTaps(bool prevent) = 0;
+ virtual bool isPreventingTouchpadTaps() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index c684ed4..79f07a5 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -347,7 +347,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
}
@@ -357,7 +357,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
if (buttonsPressed) {
BitSet32 pressed(buttonsPressed);
@@ -371,7 +371,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
}
@@ -386,7 +386,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
// Send scroll events.
@@ -401,7 +401,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
}
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 099a955..8a9ea75 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -352,7 +352,7 @@
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
&pointerProperties, &pointerCoords, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /*videoFrames=*/{}));
return out;
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index e03a773..58b29b8 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -86,20 +86,26 @@
return ADISPLAY_ID_NONE;
}
+std::optional<KeyboardLayoutInfo> KeyboardInputMapper::getKeyboardLayoutInfo() const {
+ if (mKeyboardLayoutInfo) {
+ return mKeyboardLayoutInfo;
+ }
+ std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
+ if (!layoutInfo) {
+ return std::nullopt;
+ }
+ return KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType);
+}
+
void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
info.setKeyboardType(mKeyboardType);
info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
- if (mKeyboardLayoutInfo) {
- info.setKeyboardLayoutInfo(*mKeyboardLayoutInfo);
- } else {
- std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
- if (layoutInfo) {
- info.setKeyboardLayoutInfo(
- KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType));
- }
+ std::optional keyboardLayoutInfo = getKeyboardLayoutInfo();
+ if (keyboardLayoutInfo) {
+ info.setKeyboardLayoutInfo(*keyboardLayoutInfo);
}
}
@@ -152,13 +158,31 @@
getValueByKey(config.keyboardLayoutAssociations, getDeviceContext().getLocation());
if (mKeyboardLayoutInfo != newKeyboardLayoutInfo) {
mKeyboardLayoutInfo = newKeyboardLayoutInfo;
+ // Also update keyboard layout overlay as soon as we find the new layout info
+ updateKeyboardLayoutOverlay();
bumpGeneration();
}
}
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEYBOARD_LAYOUTS)) {
+ if (!getDeviceContext().getDeviceClasses().test(InputDeviceClass::VIRTUAL) &&
+ updateKeyboardLayoutOverlay()) {
+ bumpGeneration();
+ }
+ }
return out;
}
+bool KeyboardInputMapper::updateKeyboardLayoutOverlay() {
+ std::shared_ptr<KeyCharacterMap> keyboardLayout =
+ getDeviceContext()
+ .getContext()
+ ->getPolicy()
+ ->getKeyboardLayoutOverlay(getDeviceContext().getDeviceIdentifier(),
+ getKeyboardLayoutInfo());
+ return getDeviceContext().setKeyboardLayoutOverlay(keyboardLayout);
+}
+
void KeyboardInputMapper::configureParameters() {
const PropertyMap& config = getDeviceContext().getConfiguration();
mParameters.orientationAware = config.getBool("keyboard.orientationAware").value_or(false);
@@ -246,6 +270,7 @@
keyDown.flags = flags;
mKeyDowns.push_back(keyDown);
}
+ onKeyDownProcessed();
} else {
// Remove key down.
if (keyDownIndex) {
@@ -423,4 +448,19 @@
return out;
}
+void KeyboardInputMapper::onKeyDownProcessed() {
+ InputReaderContext& context = *getContext();
+ if (context.isPreventingTouchpadTaps()) {
+ // avoid pinging java service unnecessarily
+ return;
+ }
+ // 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);
+ if (shouldHideCursor && context.getPolicy()->isInputMethodConnectionActive()) {
+ context.fadePointer();
+ context.setPreventingTouchpadTaps(true);
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 45fd68b..09808df 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -99,12 +99,15 @@
bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
std::optional<size_t> findKeyDownIndex(int32_t scanCode);
+ std::optional<KeyboardLayoutInfo> getKeyboardLayoutInfo() const;
+ bool updateKeyboardLayoutOverlay();
void resetLedState();
void initializeLedState(LedState& ledState, int32_t led);
void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
+ void onKeyDownProcessed();
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 13f2e59..07ae5b1 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -20,6 +20,7 @@
#include "RotaryEncoderInputMapper.h"
+#include <utils/Timers.h>
#include <optional>
#include "CursorScrollAccumulator.h"
@@ -62,6 +63,7 @@
dump += INDENT2 "Rotary Encoder Input Mapper:\n";
dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
+ dump += StringPrintf(INDENT3 "HaveSlopController: %s\n", toString(mSlopController != nullptr));
}
std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when,
@@ -70,6 +72,16 @@
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
if (!changes.any()) {
mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
+
+ const PropertyMap& propertyMap = getDeviceContext().getConfiguration();
+ float slopThreshold = propertyMap.getInt("rotary_encoder.slop_threshold").value_or(0);
+ int32_t slopDurationNs = milliseconds_to_nanoseconds(
+ propertyMap.getInt("rotary_encoder.slop_duration_ms").value_or(0));
+ if (slopThreshold > 0 && slopDurationNs > 0) {
+ mSlopController = std::make_unique<SlopController>(slopThreshold, slopDurationNs);
+ } else {
+ mSlopController = nullptr;
+ }
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
std::optional<DisplayViewport> internalViewport =
@@ -103,6 +115,10 @@
std::list<NotifyArgs> out;
float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
+ if (mSlopController) {
+ scroll = mSlopController->consumeEvent(when, scroll);
+ }
+
bool scrolled = scroll != 0;
// Send motion event.
@@ -132,10 +148,10 @@
out.push_back(
NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, /* buttonState */ 0, MotionClassification::NONE,
+ metaState, /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /*videoFrames=*/{}));
}
mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 9e2e8c4..fe5d152 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -20,6 +20,7 @@
#include "CursorScrollAccumulator.h"
#include "InputMapper.h"
+#include "SlopController.h"
namespace android {
@@ -46,6 +47,7 @@
int32_t mSource;
float mScalingFactor;
ui::Rotation mOrientation;
+ std::unique_ptr<SlopController> mSlopController;
explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp
new file mode 100644
index 0000000..f79219f
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SlopController.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+#include "../Macros.h"
+// clang-format on
+
+#include "SlopController.h"
+
+namespace {
+int signOf(float value) {
+ if (value == 0) return 0;
+ if (value > 0) return 1;
+ return -1;
+}
+} // namespace
+
+namespace android {
+
+SlopController::SlopController(float slopThreshold, nsecs_t slopDurationNanos)
+ : mSlopThreshold(slopThreshold), mSlopDurationNanos(slopDurationNanos) {}
+
+float SlopController::consumeEvent(nsecs_t eventTimeNanos, float value) {
+ if (mSlopDurationNanos == 0) {
+ return value;
+ }
+
+ if (shouldResetSlopTracking(eventTimeNanos, value)) {
+ mCumulativeValue = 0;
+ mHasSlopBeenMet = false;
+ }
+
+ mLastEventTimeNanos = eventTimeNanos;
+
+ if (mHasSlopBeenMet) {
+ // Since slop has already been met, we know that all of the current value would pass the
+ // slop threshold. So return that, without any further processing.
+ return value;
+ }
+
+ mCumulativeValue += value;
+
+ if (abs(mCumulativeValue) >= mSlopThreshold) {
+ mHasSlopBeenMet = true;
+ // Return the amount of value that exceeds the slop.
+ return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold);
+ }
+
+ return 0;
+}
+
+bool SlopController::shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) const {
+ const nsecs_t ageNanos = eventTimeNanos - mLastEventTimeNanos;
+ if (ageNanos >= mSlopDurationNanos) {
+ return true;
+ }
+ if (value == 0) {
+ return false;
+ }
+ if (signOf(mCumulativeValue) != signOf(value)) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SlopController.h b/services/inputflinger/reader/mapper/SlopController.h
new file mode 100644
index 0000000..c106410
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SlopController.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Controls a slop logic. Slop here refers to an approach to try and drop insignificant input
+ * events. This is helpful in cases where unintentional input events may cause unintended outcomes,
+ * like scrolling a screen or keeping the screen awake.
+ *
+ * Current slop logic:
+ * "If time since last event > Xns, then discard the next N values."
+ */
+class SlopController final {
+public:
+ SlopController(float slopThreshold, nsecs_t slopDurationNanos);
+
+ /**
+ * Consumes an event with a given time and value for slop processing.
+ * Returns an amount <=value that should be consumed.
+ */
+ float consumeEvent(nsecs_t eventTime, float value);
+
+private:
+ bool shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) const;
+
+ /** The amount of event values ignored after an inactivity of the slop duration. */
+ const float mSlopThreshold;
+ /** The duration of inactivity that resets slop controlling. */
+ const nsecs_t mSlopDurationNanos;
+
+ nsecs_t mLastEventTimeNanos = 0;
+ float mCumulativeValue = 0;
+ bool mHasSlopBeenMet = false;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f4d50b8..b565454 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -271,7 +271,7 @@
toString(mFusedStylusPointerId).c_str());
dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
mExternalStylusFusionTimeout);
- dump += StringPrintf(INDENT4 " External Stylus Buttons Applied: 0x%08x",
+ dump += StringPrintf(INDENT4 "External Stylus Buttons Applied: 0x%08x\n",
mExternalStylusButtonsApplied);
dump += INDENT3 "External Stylus State:\n";
dumpStylusState(dump, mExternalStylusState);
@@ -972,7 +972,18 @@
(rawXResolution > 0 && rawYResolution > 0) ? (rawXResolution + rawYResolution) / 2 : 0;
const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport);
- const bool viewportChanged = mViewport != newViewport;
+ bool viewportChanged;
+ if (mParameters.enableForInactiveViewport) {
+ // When touch is enabled for an inactive viewport, ignore the
+ // viewport active status when checking whether the viewport has
+ // changed.
+ DisplayViewport tempViewport = mViewport;
+ tempViewport.isActive = newViewport.isActive;
+ viewportChanged = tempViewport != newViewport;
+ } else {
+ viewportChanged = mViewport != newViewport;
+ }
+
bool skipViewportUpdate = false;
if (viewportChanged) {
const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation;
@@ -1889,9 +1900,9 @@
uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
// Skip checking whether the pointer is inside the physical frame if the device is in
- // unscaled mode.
+ // unscaled or pointer mode.
if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) &&
- mDeviceMode != DeviceMode::UNSCALED) {
+ mDeviceMode != DeviceMode::UNSCALED && mDeviceMode != DeviceMode::POINTER) {
// If exactly one pointer went down, check for virtual key hit.
// Otherwise, we will drop the entire stroke.
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1998,12 +2009,12 @@
PointerCoords& curOutCoords = outCoords[outIndex];
if (curInProperties != curOutProperties) {
- curOutProperties.copyFrom(curInProperties);
+ curOutProperties = curInProperties;
changed = true;
}
if (curInCoords != curOutCoords) {
- curOutCoords.copyFrom(curInCoords);
+ curOutCoords = curInCoords;
changed = true;
}
}
@@ -2733,7 +2744,7 @@
buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, 0, 0, x, y, mPointerGesture.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
// Update state.
@@ -2745,10 +2756,9 @@
for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
- mPointerGesture.lastGestureProperties[index].copyFrom(
- mPointerGesture.currentGestureProperties[index]);
- mPointerGesture.lastGestureCoords[index].copyFrom(
- mPointerGesture.currentGestureCoords[index]);
+ mPointerGesture.lastGestureProperties[index] =
+ mPointerGesture.currentGestureProperties[index];
+ mPointerGesture.lastGestureCoords[index] = mPointerGesture.currentGestureCoords[index];
mPointerGesture.lastGestureIdToIndex[id] = index;
}
}
@@ -3532,8 +3542,7 @@
std::tie(x, y) = mPointerController->getPosition();
}
- mPointerSimple.currentCoords.copyFrom(
- mCurrentCookedState.cookedPointerData.pointerCoords[index]);
+ mPointerSimple.currentCoords = mCurrentCookedState.cookedPointerData.pointerCoords[index];
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerSimple.currentProperties.id = 0;
@@ -3571,8 +3580,8 @@
const auto [x, y] = mPointerController->getPosition();
const uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
- mPointerSimple.currentCoords.copyFrom(
- mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
+ mPointerSimple.currentCoords =
+ mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex];
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
@@ -3632,7 +3641,7 @@
mOrientedXPrecision, mOrientedYPrecision,
mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
mPointerSimple.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
if (mPointerSimple.hovering && !hovering) {
@@ -3647,7 +3656,7 @@
&mPointerSimple.lastCoords, mOrientedXPrecision,
mOrientedYPrecision, mPointerSimple.lastCursorX,
mPointerSimple.lastCursorY, mPointerSimple.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
if (down) {
@@ -3664,7 +3673,7 @@
&mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
- mPointerSimple.downTime, /* videoFrames */ {}));
+ mPointerSimple.downTime, /*videoFrames=*/{}));
}
// Send move.
@@ -3675,7 +3684,7 @@
&mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
- mPointerSimple.downTime, /* videoFrames */ {}));
+ mPointerSimple.downTime, /*videoFrames=*/{}));
}
if (hovering) {
@@ -3691,7 +3700,7 @@
&mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
- mPointerSimple.downTime, /* videoFrames */ {}));
+ mPointerSimple.downTime, /*videoFrames=*/{}));
}
// Send hover move.
@@ -3702,7 +3711,7 @@
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
&mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
mOrientedXPrecision, mOrientedYPrecision, cursorPosition.x,
- cursorPosition.y, mPointerSimple.downTime, /* videoFrames */ {}));
+ cursorPosition.y, mPointerSimple.downTime, /*videoFrames=*/{}));
}
if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3712,8 +3721,7 @@
mWheelXVelocityControl.move(when, &hscroll, nullptr);
// Send scroll.
- PointerCoords pointerCoords;
- pointerCoords.copyFrom(mPointerSimple.currentCoords);
+ PointerCoords pointerCoords = mPointerSimple.currentCoords;
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
@@ -3724,13 +3732,13 @@
&mPointerSimple.currentProperties, &pointerCoords,
mOrientedXPrecision, mOrientedYPrecision, cursorPosition.x,
cursorPosition.y, mPointerSimple.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
// Save state.
if (down || hovering) {
- mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
- mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+ mPointerSimple.lastCoords = mPointerSimple.currentCoords;
+ mPointerSimple.lastProperties = mPointerSimple.currentProperties;
mPointerSimple.displayId = displayId;
mPointerSimple.source = mSource;
mPointerSimple.lastCursorX = cursorPosition.x;
@@ -3755,7 +3763,7 @@
mOrientedXPrecision, mOrientedYPrecision,
mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
mPointerSimple.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
}
@@ -3784,8 +3792,8 @@
while (!idBits.isEmpty()) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = idToIndex[id];
- pointerProperties[pointerCount].copyFrom(properties[index]);
- pointerCoords[pointerCount].copyFrom(coords[index]);
+ pointerProperties[pointerCount] = properties[index];
+ pointerCoords[pointerCount] = coords[index];
if (changedId >= 0 && id == uint32_t(changedId)) {
action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index d8b59ca..c5dfb00 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -195,9 +195,9 @@
enum class DeviceMode {
DISABLED, // input is disabled
DIRECT, // direct mapping (touchscreen)
- UNSCALED, // unscaled mapping (touchpad)
+ UNSCALED, // unscaled mapping (e.g. captured touchpad)
NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
- POINTER, // pointer mapping (pointer)
+ POINTER, // pointer mapping (e.g. uncaptured touchpad, drawing tablet)
ftl_last = POINTER
};
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index c72425a..eca0f86 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -16,8 +16,11 @@
#include "../Macros.h"
+#include <algorithm>
#include <chrono>
+#include <iterator>
#include <limits>
+#include <map>
#include <optional>
#include <android-base/stringprintf.h>
@@ -26,6 +29,8 @@
#include <input/PrintTools.h>
#include <linux/input-event-codes.h>
#include <log/log_main.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
#include "ui/Rotation.h"
@@ -53,13 +58,14 @@
};
const std::vector<CurveSegment> segments = {
- {10.922, 3.19, 0},
- {31.750, 4.79, -17.526},
- {98.044, 7.28, -96.52},
- {std::numeric_limits<double>::infinity(), 15.04, -857.758},
+ {32.002, 3.19, 0},
+ {52.83, 4.79, -51.254},
+ {119.124, 7.28, -182.737},
+ {std::numeric_limits<double>::infinity(), 15.04, -1107.556},
};
-const std::vector<double> sensitivityFactors = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18};
+const std::vector<double> sensitivityFactors = {1, 2, 4, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 16, 18, 20};
std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity,
size_t propertySize) {
@@ -169,6 +175,106 @@
mapper->consumeGesture(gesture);
}
+int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus) {
+ // When adding cases to this switch, also add them to the copy of this method in
+ // InputDeviceMetricsCollector.cpp.
+ // TODO(b/286394420): deduplicate this method with the one in InputDeviceMetricsCollector.cpp.
+ switch (linuxBus) {
+ case BUS_USB:
+ return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
+ case BUS_BLUETOOTH:
+ return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
+ default:
+ return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
+ }
+}
+
+class MetricsAccumulator {
+public:
+ static MetricsAccumulator& getInstance() {
+ static MetricsAccumulator sAccumulator;
+ return sAccumulator;
+ }
+
+ void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].fingers++; }
+
+ void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].palms++; }
+
+ // Checks whether a Gesture struct is for the end of a gesture that we log metrics for, and
+ // records it if so.
+ void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) {
+ switch (gesture.type) {
+ case kGestureTypeFling:
+ if (gesture.details.fling.fling_state == GESTURES_FLING_START) {
+ // Indicates the end of a two-finger scroll gesture.
+ mCounters[id].twoFingerSwipeGestures++;
+ }
+ break;
+ case kGestureTypeSwipeLift:
+ mCounters[id].threeFingerSwipeGestures++;
+ break;
+ case kGestureTypeFourFingerSwipeLift:
+ mCounters[id].fourFingerSwipeGestures++;
+ break;
+ case kGestureTypePinch:
+ if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
+ mCounters[id].pinchGestures++;
+ }
+ break;
+ default:
+ // We're not interested in any other gestures.
+ break;
+ }
+ }
+
+private:
+ MetricsAccumulator() {
+ AStatsManager_setPullAtomCallback(android::util::TOUCHPAD_USAGE, /*metadata=*/nullptr,
+ MetricsAccumulator::pullAtomCallback, /*cookie=*/nullptr);
+ }
+
+ ~MetricsAccumulator() { AStatsManager_clearPullAtomCallback(android::util::TOUCHPAD_USAGE); }
+
+ static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
+ AStatsEventList* outEventList,
+ void* cookie) {
+ LOG_ALWAYS_FATAL_IF(atomTag != android::util::TOUCHPAD_USAGE);
+ MetricsAccumulator& accumulator = MetricsAccumulator::getInstance();
+ accumulator.produceAtoms(outEventList);
+ accumulator.resetCounters();
+ return AStatsManager_PULL_SUCCESS;
+ }
+
+ void produceAtoms(AStatsEventList* outEventList) const {
+ for (auto& [id, counters] : mCounters) {
+ auto [busId, vendorId, productId, versionId] = id;
+ addAStatsEvent(outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId,
+ versionId, linuxBusToInputDeviceBusEnum(busId), counters.fingers,
+ counters.palms, counters.twoFingerSwipeGestures,
+ counters.threeFingerSwipeGestures, counters.fourFingerSwipeGestures,
+ counters.pinchGestures);
+ }
+ }
+
+ void resetCounters() { mCounters.clear(); }
+
+ // Stores the counters for a specific touchpad model. Fields have the same meanings as those of
+ // the TouchpadUsage atom; see that definition for detailed documentation.
+ struct Counters {
+ int32_t fingers = 0;
+ int32_t palms = 0;
+
+ int32_t twoFingerSwipeGestures = 0;
+ int32_t threeFingerSwipeGestures = 0;
+ int32_t fourFingerSwipeGestures = 0;
+ int32_t pinchGestures = 0;
+ };
+
+ // Metrics are aggregated by device model and version, so if two devices of the same model and
+ // version are connected at once, they will have the same counters.
+ std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters;
+};
+
} // namespace
TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext,
@@ -178,7 +284,8 @@
mPointerController(getContext()->getPointerController(getDeviceId())),
mStateConverter(deviceContext, mMotionAccumulator),
mGestureConverter(*getContext(), deviceContext, getDeviceId()),
- mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()) {
+ mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
+ mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
RawAbsoluteAxisInfo slotAxisInfo;
deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
@@ -243,6 +350,7 @@
dump += addLinePrefix(mPropertyProvider.dump(), INDENT4);
dump += INDENT3 "Captured event converter:\n";
dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
+ dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
}
std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when,
@@ -254,13 +362,31 @@
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
- std::optional<int32_t> displayId = mPointerController->getDisplayId();
+ mDisplayId = ADISPLAY_ID_NONE;
+ if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+ // This InputDevice is associated with a viewport.
+ // Only generate events for the associated display.
+ const bool mismatchedPointerDisplay =
+ (viewport->displayId != mPointerController->getDisplayId());
+ if (mismatchedPointerDisplay) {
+ ALOGW("Touchpad \"%s\" associated viewport display does not match pointer "
+ "controller",
+ mDeviceContext.getName().c_str());
+ }
+ mDisplayId = mismatchedPointerDisplay ? std::nullopt
+ : std::make_optional(viewport->displayId);
+ } else {
+ // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+ mDisplayId = mPointerController->getDisplayId();
+ }
+
ui::Rotation orientation = ui::ROTATION_0;
- if (displayId.has_value()) {
- if (auto viewport = config.getDisplayViewportById(*displayId); viewport) {
+ if (mDisplayId.has_value()) {
+ if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
orientation = getInverseRotation(viewport->orientation);
}
}
+ mGestureConverter.setDisplayId(mDisplayId);
mGestureConverter.setOrientation(orientation);
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
@@ -331,12 +457,39 @@
}
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
+ updatePalmDetectionMetrics();
return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
} else {
return {};
}
}
+void TouchpadInputMapper::updatePalmDetectionMetrics() {
+ std::set<int32_t> currentTrackingIds;
+ for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+ const MultiTouchMotionAccumulator::Slot& slot = mMotionAccumulator.getSlot(i);
+ if (!slot.isInUse()) {
+ continue;
+ }
+ currentTrackingIds.insert(slot.getTrackingId());
+ if (slot.getToolType() == ToolType::PALM) {
+ mPalmTrackingIds.insert(slot.getTrackingId());
+ }
+ }
+ std::vector<int32_t> liftedTouches;
+ std::set_difference(mLastFrameTrackingIds.begin(), mLastFrameTrackingIds.end(),
+ currentTrackingIds.begin(), currentTrackingIds.end(),
+ std::inserter(liftedTouches, liftedTouches.begin()));
+ for (int32_t trackingId : liftedTouches) {
+ if (mPalmTrackingIds.erase(trackingId) > 0) {
+ MetricsAccumulator::getInstance().recordPalm(mMetricsId);
+ } else {
+ MetricsAccumulator::getInstance().recordFinger(mMetricsId);
+ }
+ }
+ mLastFrameTrackingIds = currentTrackingIds;
+}
+
std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs) {
ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str());
@@ -363,11 +516,19 @@
std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out = {};
- for (Gesture& gesture : mGesturesToProcess) {
- out += mGestureConverter.handleGesture(when, readTime, gesture);
+ if (mDisplayId) {
+ MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance();
+ for (Gesture& gesture : mGesturesToProcess) {
+ out += mGestureConverter.handleGesture(when, readTime, gesture);
+ metricsAccumulator.processGesture(mMetricsId, gesture);
+ }
}
mGesturesToProcess.clear();
return out;
}
+std::optional<int32_t> TouchpadInputMapper::getAssociatedDisplayId() {
+ return mDisplayId;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 23d0fd3..47d712e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -18,6 +18,7 @@
#include <list>
#include <memory>
+#include <set>
#include <vector>
#include <PointerControllerInterface.h>
@@ -58,10 +59,18 @@
void consumeGesture(const Gesture* gesture);
+ // A subset of InputDeviceIdentifier used for logging metrics, to avoid storing a copy of the
+ // strings in that bigger struct.
+ using MetricsIdentifier = std::tuple<uint16_t /*busId*/, uint16_t /*vendorId*/,
+ uint16_t /*productId*/, uint16_t /*version*/>;
+
+ std::optional<int32_t> getAssociatedDisplayId() override;
+
private:
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
+ void updatePalmDetectionMetrics();
[[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs);
[[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
@@ -86,6 +95,20 @@
bool mProcessing = false;
bool mResettingInterpreter = false;
std::vector<Gesture> mGesturesToProcess;
+
+ static MetricsIdentifier metricsIdFromInputDeviceIdentifier(const InputDeviceIdentifier& id) {
+ return std::make_tuple(id.bus, id.vendor, id.product, id.version);
+ }
+ const MetricsIdentifier mMetricsId;
+ // Tracking IDs for touches on the pad in the last evdev frame.
+ std::set<int32_t> mLastFrameTrackingIds;
+ // Tracking IDs for touches that have at some point been reported as palms by the touchpad.
+ std::set<int32_t> mPalmTrackingIds;
+
+ // The display that events generated by this mapper should target. This can be set to
+ // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
+ // std::nullopt), all events will be ignored.
+ std::optional<int32_t> mDisplayId;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 1088821..7006e9e 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -124,6 +124,11 @@
std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
+ if (!mDisplayId) {
+ // Ignore gestures when there is no target display configured.
+ return {};
+ }
+
switch (gesture.type) {
case kGestureTypeMove:
return {handleMove(when, readTime, gesture)};
@@ -153,6 +158,9 @@
const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
+ if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
+ enableTapToClick();
+ }
rotateDelta(mOrientation, &deltaX, &deltaY);
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -191,6 +199,15 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+
+ if (mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick();
+ if (gesture.details.buttons.is_tap) {
+ // return early to prevent this tap
+ return out;
+ }
+ }
+
const uint32_t buttonsPressed = gesture.details.buttons.down;
bool pointerDown = isPointerDown(mButtonState) ||
buttonsPressed &
@@ -239,6 +256,11 @@
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
newButtonState, /* pointerCount= */ 1, mFingerProps.data(),
&coords, xCursorPosition, yCursorPosition));
+ // Send a HOVER_MOVE to tell the application that the mouse is hovering again.
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ /*actionButton=*/0, newButtonState, /*pointerCount=*/1,
+ mFingerProps.data(), &coords, xCursorPosition,
+ yCursorPosition));
}
mButtonState = newButtonState;
return out;
@@ -332,6 +354,9 @@
// magnitude, which will also result in the pointer icon being updated.
// TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
// initiated with a touchpad.
+ if (!mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick();
+ }
return {handleMove(when, readTime,
Gesture(kGestureMove, gesture.start_time, gesture.end_time,
/*dx=*/0.f,
@@ -536,11 +561,11 @@
readTime,
mDeviceId,
SOURCE,
- mPointerController->getDisplayId(),
+ *mDisplayId,
/* policyFlags= */ POLICY_FLAG_WAKE,
action,
/* actionButton= */ actionButton,
- /* flags= */ 0,
+ /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0,
mReaderContext.getGlobalMetaState(),
buttonState,
mCurrentClassification,
@@ -556,4 +581,8 @@
/* videoFrames= */ {}};
}
+void GestureConverter::enableTapToClick() {
+ mReaderContext.setPreventingTouchpadTaps(false);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index b613b88..e6cf617 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -46,6 +46,8 @@
void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
+ void setDisplayId(std::optional<int32_t> displayId) { mDisplayId = displayId; }
+
void populateMotionRanges(InputDeviceInfo& info) const;
[[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -78,10 +80,13 @@
const PointerCoords* pointerCoords, float xCursorPosition,
float yCursorPosition);
+ void enableTapToClick();
+
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
std::shared_ptr<PointerControllerInterface> mPointerController;
+ std::optional<int32_t> mDisplayId;
ui::Rotation mOrientation = ui::ROTATION_0;
RawAbsoluteAxisInfo mXAxisInfo;
RawAbsoluteAxisInfo mYAxisInfo;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 6438a14..3d6df30 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -59,9 +59,12 @@
"NotifyArgs_test.cpp",
"PreferStylusOverTouch_test.cpp",
"PropertyProvider_test.cpp",
+ "SlopController_test.cpp",
"SyncQueue_test.cpp",
"TestInputListener.cpp",
+ "TestInputListenerMatchers.cpp",
"TouchpadInputMapper_test.cpp",
+ "KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
"UnwantedInteractionBlocker_test.cpp",
],
@@ -74,24 +77,12 @@
target: {
android: {
shared_libs: [
- "libinput",
"libvintf",
],
},
- host: {
- include_dirs: [
- "bionic/libc/kernel/android/uapi/",
- "bionic/libc/kernel/uapi",
- ],
- cflags: [
- "-D__ANDROID_HOST__",
- ],
- static_libs: [
- "libinput",
- ],
- },
},
sanitize: {
+ hwaddress: true,
undefined: true,
all_undefined: true,
diag: {
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 3486d0f..78420c0 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -209,6 +209,14 @@
mConfig.stylusPointerIconEnabled = enabled;
}
+void FakeInputReaderPolicy::setIsInputMethodConnectionActive(bool active) {
+ mIsInputMethodConnectionActive = active;
+}
+
+bool FakeInputReaderPolicy::isInputMethodConnectionActive() {
+ return mIsInputMethodConnectionActive;
+}
+
void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
}
@@ -227,7 +235,7 @@
}
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) {
+ const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 85ff01a..e03d28d 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -77,6 +77,8 @@
void setVelocityControlParams(const VelocityControlParameters& params);
void setStylusButtonMotionEventsEnabled(bool enabled);
void setStylusPointerIconEnabled(bool enabled);
+ void setIsInputMethodConnectionActive(bool active);
+ bool isInputMethodConnectionActive() override;
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
@@ -84,7 +86,7 @@
int32_t /*deviceId*/) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) override;
+ const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
@@ -99,6 +101,7 @@
std::vector<DisplayViewport> mViewports;
TouchAffineTransformation transform;
std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
+ bool mIsInputMethodConnectionActive{false};
uint32_t mNextPointerCaptureSequenceNumber{0};
};
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index 5440a98..2ff9c3c 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -31,6 +31,8 @@
namespace android::inputdispatcher {
+namespace {
+
class FakeWindowHandle : public WindowInfoHandle {
public:
FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
@@ -49,6 +51,8 @@
}
};
+} // namespace
+
TEST(FocusResolverTest, SetFocusedWindow) {
sp<IBinder> focusableWindowToken = sp<BBinder>::make();
sp<IBinder> invisibleWindowToken = sp<BBinder>::make();
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index a3994f0..74ce359 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -19,6 +19,7 @@
#include <EventHub.h>
#include <gestures/GestureConverter.h>
#include <gtest/gtest.h>
+#include <gui/constants.h>
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
@@ -85,6 +86,7 @@
TEST_F(GestureConverterTest, Move) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
@@ -93,8 +95,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
}
@@ -103,6 +105,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
@@ -111,8 +114,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5));
}
@@ -120,6 +123,7 @@
TEST_F(GestureConverterTest, ButtonsChange) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
// Press left and right buttons at once
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -132,23 +136,23 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Then release the left button
Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -161,31 +165,37 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Finally release the right button
Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT,
/* is_tap= */ false);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture);
- ASSERT_EQ(2u, args.size());
+ ASSERT_EQ(3u, args.size());
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, DragWithButton) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
// Press the button
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -197,15 +207,15 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Move
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
@@ -215,8 +225,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
@@ -225,24 +235,30 @@
/* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
/* is_tap= */ false);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture);
- ASSERT_EQ(2u, args.size());
+ ASSERT_EQ(3u, args.size());
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll) {
const nsecs_t downTime = 12345;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
@@ -253,7 +269,8 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER), WithDownTime(downTime),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
@@ -261,7 +278,8 @@
WithGestureScrollDistance(0, 10, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
@@ -272,7 +290,8 @@
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -284,7 +303,8 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_Rotated) {
@@ -292,6 +312,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
@@ -301,14 +322,15 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER), WithDownTime(downTime)));
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithCoords(POINTER_X - 10, POINTER_Y),
WithGestureScrollDistance(0, 10, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
@@ -318,7 +340,7 @@
WithCoords(POINTER_X - 15, POINTER_Y),
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -329,12 +351,13 @@
WithCoords(POINTER_X - 15, POINTER_Y),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -350,12 +373,14 @@
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
ASSERT_EQ(1u, args.size());
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionClassification(MotionClassification::NONE));
+ AllOf(WithMotionClassification(MotionClassification::NONE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -379,6 +404,7 @@
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/0);
@@ -398,6 +424,7 @@
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
/*dy=*/5);
@@ -423,6 +450,7 @@
// only checks movement in one dimension.
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
@@ -436,7 +464,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -445,7 +474,8 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -454,7 +484,8 @@
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
@@ -463,7 +494,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0, -0.01, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
@@ -480,7 +512,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
@@ -496,26 +529,30 @@
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
@@ -527,28 +564,31 @@
NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10);
@@ -563,7 +603,8 @@
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15);
@@ -577,21 +618,24 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 10, /* dy= */ 0);
@@ -605,7 +649,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -614,7 +659,8 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -623,7 +669,8 @@
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -632,7 +679,8 @@
3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger3Start = arg.pointerCoords[3];
args.pop_front();
@@ -641,7 +689,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0.01, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10);
@@ -660,7 +709,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15);
@@ -678,32 +728,37 @@
3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_Inwards) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -714,7 +769,7 @@
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -722,7 +777,7 @@
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
@@ -734,7 +789,7 @@
WithGesturePinchScaleFactor(0.8f, EPSILON),
WithPointerCoords(0, POINTER_X - 80, POINTER_Y),
WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -745,18 +800,19 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_Outwards) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -767,7 +823,7 @@
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -775,7 +831,7 @@
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 1.2, GESTURES_ZOOM_UPDATE);
@@ -787,7 +843,7 @@
WithGesturePinchScaleFactor(1.2f, EPSILON),
WithPointerCoords(0, POINTER_X - 120, POINTER_Y),
WithPointerCoords(1, POINTER_X + 120, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -798,18 +854,19 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -833,6 +890,7 @@
TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -858,6 +916,7 @@
TEST_F(GestureConverterTest, ResetWithButtonPressed) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
@@ -871,24 +930,25 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringScroll) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
(void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -901,12 +961,14 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/10);
@@ -919,24 +981,28 @@
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringPinch) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -949,18 +1015,19 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, FlingTapDown) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
@@ -969,10 +1036,252 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X, POINTER_Y));
ASSERT_TRUE(mFakePointerController->isPointerShown());
}
+TEST_F(GestureConverterTest, Tap) {
+ // Tap should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ ASSERT_EQ(5u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTest, Click) {
+ // Click should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTest, TapWithTapToClickDisabled) {
+ // Tap should be ignored when disabled
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
+ // Click should still produce button press/release events
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
+ // initially disable tap-to-click
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index e38f88c..2ff64c8 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -16,13 +16,15 @@
#include "../InputDeviceMetricsCollector.h"
+#include <NotifyArgsBuilders.h>
#include <gtest/gtest.h>
#include <gui/constants.h>
+#include <input/InputEventBuilders.h>
#include <linux/input.h>
+
#include <array>
#include <tuple>
-#include "EventBuilders.h"
#include "TestInputListener.h"
namespace android {
@@ -80,6 +82,14 @@
const InputDeviceInfo NON_ALPHABETIC_KEYBOARD_INFO =
generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/false);
+std::set<gui::Uid> uids(std::initializer_list<int32_t> vals) {
+ std::set<gui::Uid> set;
+ for (const auto val : vals) {
+ set.emplace(val);
+ }
+ return set;
+}
+
} // namespace
// --- InputDeviceMetricsCollectorDeviceClassificationTest ---
@@ -341,8 +351,9 @@
TestInputListener mTestListener;
InputDeviceMetricsCollector mMetricsCollector{mTestListener, *this, USAGE_TIMEOUT};
- void assertUsageLogged(const InputDeviceIdentifier& identifier, nanoseconds duration,
- std::optional<SourceUsageBreakdown> sourceBreakdown = {}) {
+ void assertUsageLogged(InputDeviceIdentifier identifier, nanoseconds duration,
+ std::optional<SourceUsageBreakdown> sourceBreakdown = {},
+ std::optional<UidUsageBreakdown> uidBreakdown = {}) {
ASSERT_GE(mLoggedUsageSessions.size(), 1u);
const auto& [loggedIdentifier, report] = *mLoggedUsageSessions.begin();
ASSERT_EQ(identifier, loggedIdentifier);
@@ -350,6 +361,9 @@
if (sourceBreakdown) {
ASSERT_EQ(sourceBreakdown, report.sourceBreakdown);
}
+ if (uidBreakdown) {
+ ASSERT_EQ(uidBreakdown, report.uidBreakdown);
+ }
mLoggedUsageSessions.erase(mLoggedUsageSessions.begin());
}
@@ -357,6 +371,8 @@
void setCurrentTime(nanoseconds time) { mCurrentTime = time; }
+ nsecs_t currentTime() const { return mCurrentTime.count(); }
+
NotifyMotionArgs generateMotionArgs(int32_t deviceId,
uint32_t source = AINPUT_SOURCE_TOUCHSCREEN,
std::vector<ToolType> toolTypes = {ToolType::FINGER}) {
@@ -406,6 +422,7 @@
// Device was used.
mMetricsCollector.notifyMotion(generateMotionArgs(ignoredDeviceId));
mTestListener.assertNotifyMotionWasCalled();
+ mMetricsCollector.notifyDeviceInteraction(ignoredDeviceId, TIME.count(), uids({0, 1, 2}));
ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
// Device was used again after the usage timeout expired, but we still don't log usage.
@@ -622,4 +639,146 @@
ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
}
+TEST_F(InputDeviceMetricsCollectorTest, UidsNotTrackedWhenThereIsNoActiveSession) {
+ mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+
+ // Notify interaction with UIDs before the device is used.
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1}));
+
+ // Use the device.
+ setCurrentTime(TIME + 100ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ setCurrentTime(TIME + 200ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+
+ // Notify interaction for the wrong device.
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({42}));
+
+ // Notify interaction after usage session would have expired.
+ // This interaction should not be tracked.
+ setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({2, 3}));
+
+ // Use the device again, by starting a new usage session.
+ setCurrentTime(TIME + 300ns + USAGE_TIMEOUT);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+
+ // The first usage session is logged.
+ static const UidUsageBreakdown emptyBreakdown;
+ ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 100ns, /*sourceBreakdown=*/{},
+ /*uidBreakdown=*/emptyBreakdown));
+
+ ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
+}
+
+TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid) {
+ mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+ UidUsageBreakdown expectedUidBreakdown;
+
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1}));
+
+ setCurrentTime(TIME + 100ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
+ setCurrentTime(TIME + 200ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2, 3}));
+
+ expectedUidBreakdown.emplace_back(1, 200ns);
+ expectedUidBreakdown.emplace_back(2, 100ns);
+ expectedUidBreakdown.emplace_back(3, 0ns);
+
+ // Remove the device to force the usage session to be logged.
+ mMetricsCollector.notifyInputDevicesChanged({});
+ ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 200ns, /*sourceBreakdown=*/{},
+ expectedUidBreakdown));
+
+ ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
+}
+
+TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid_TracksMultipleSessionsForUid) {
+ mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+ UidUsageBreakdown expectedUidBreakdown;
+
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
+ setCurrentTime(TIME + 100ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
+
+ setCurrentTime(TIME + 200ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1}));
+
+ setCurrentTime(TIME + 300ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 3}));
+ setCurrentTime(TIME + 400ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 3}));
+
+ setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
+ expectedUidBreakdown.emplace_back(2, 100ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({4}));
+
+ setCurrentTime(TIME + 300ns + USAGE_TIMEOUT);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 4}));
+
+ setCurrentTime(TIME + 400ns + USAGE_TIMEOUT);
+ expectedUidBreakdown.emplace_back(3, 100ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({2, 3}));
+
+ setCurrentTime(TIME + 500ns + USAGE_TIMEOUT);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({3}));
+
+ // Remove the device to force the usage session to be logged.
+ mMetricsCollector.notifyInputDevicesChanged({});
+ expectedUidBreakdown.emplace_back(1, 300ns + USAGE_TIMEOUT);
+ expectedUidBreakdown.emplace_back(2, 0ns);
+ expectedUidBreakdown.emplace_back(3, 100ns);
+ expectedUidBreakdown.emplace_back(4, 100ns);
+ ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 500ns + USAGE_TIMEOUT,
+ /*sourceBreakdown=*/{}, expectedUidBreakdown));
+
+ ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
+}
+
+TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid_TracksUidsByDevice) {
+ mMetricsCollector.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID), generateTestDeviceInfo(DEVICE_ID_2)}});
+ UidUsageBreakdown expectedUidBreakdown1;
+ UidUsageBreakdown expectedUidBreakdown2;
+
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
+
+ setCurrentTime(TIME + 100ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({1, 3}));
+
+ setCurrentTime(TIME + 200ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2));
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({1, 3}));
+
+ setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
+ expectedUidBreakdown1.emplace_back(1, 200ns);
+ expectedUidBreakdown1.emplace_back(2, 200ns);
+ expectedUidBreakdown2.emplace_back(1, 100ns);
+ expectedUidBreakdown2.emplace_back(3, 100ns);
+ mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
+ ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(DEVICE_ID), 200ns,
+ /*sourceBreakdown=*/{}, expectedUidBreakdown1));
+ ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(DEVICE_ID_2), 100ns,
+ /*sourceBreakdown=*/{}, expectedUidBreakdown2));
+
+ ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e6b73af..6d9cd87 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -15,8 +15,10 @@
*/
#include "../dispatcher/InputDispatcher.h"
-#include "EventBuilders.h"
+#include "../BlockingQueue.h"
+#include "TestInputListenerMatchers.h"
+#include <NotifyArgsBuilders.h>
#include <android-base/properties.h>
#include <android-base/silent_death_test.h>
#include <android-base/stringprintf.h>
@@ -59,6 +61,9 @@
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr int32_t SECOND_DISPLAY_ID = 1;
+// Ensure common actions are interchangeable between keys and motions for convenience.
+static_assert(AMOTION_EVENT_ACTION_DOWN == AKEY_EVENT_ACTION_DOWN);
+static_assert(AMOTION_EVENT_ACTION_UP == AKEY_EVENT_ACTION_UP);
static constexpr int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
static constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
static constexpr int32_t ACTION_UP = AMOTION_EVENT_ACTION_UP;
@@ -91,18 +96,35 @@
AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
// The default pid and uid for windows created on the primary display by the test.
-static constexpr int32_t WINDOW_PID = 999;
-static constexpr int32_t WINDOW_UID = 1001;
+static constexpr gui::Pid WINDOW_PID{999};
+static constexpr gui::Uid WINDOW_UID{1001};
// The default pid and uid for the windows created on the secondary display by the test.
-static constexpr int32_t SECONDARY_WINDOW_PID = 1010;
-static constexpr int32_t SECONDARY_WINDOW_UID = 1012;
+static constexpr gui::Pid SECONDARY_WINDOW_PID{1010};
+static constexpr gui::Uid SECONDARY_WINDOW_UID{1012};
// An arbitrary pid of the gesture monitor window
-static constexpr int32_t MONITOR_PID = 2001;
+static constexpr gui::Pid MONITOR_PID{2001};
static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+/**
+ * If we expect to receive the event, the timeout can be made very long. When the test are running
+ * correctly, we will actually never wait until the end of the timeout because the wait will end
+ * when the event comes in. Still, this value shouldn't be infinite. During development, a local
+ * change may cause the test to fail. This timeout should be short enough to not annoy so that the
+ * developer can see the failure quickly (on human scale).
+ */
+static constexpr std::chrono::duration CONSUME_TIMEOUT_EVENT_EXPECTED = 1000ms;
+/**
+ * When no event is expected, we can have a very short timeout. A large value here would slow down
+ * the tests. In the unlikely event of system being too slow, the event may still be present but the
+ * timeout would complete before it is consumed. This would result in test flakiness. If this
+ * occurs, the flakiness rate would be high. Since the flakes are treated with high priority, this
+ * would get noticed and addressed quickly.
+ */
+static constexpr std::chrono::duration CONSUME_TIMEOUT_NO_EVENT_EXPECTED = 10ms;
+
static constexpr int expectedWallpaperFlags =
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
@@ -132,41 +154,10 @@
<< MotionEvent::actionToString(receivedAction);
}
-MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
- bool matches = action == arg.getAction();
- if (!matches) {
- *result_listener << "expected action " << MotionEvent::actionToString(action)
- << ", but got " << MotionEvent::actionToString(arg.getAction());
- }
- if (action == AMOTION_EVENT_ACTION_DOWN) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "downTime should match eventTime for ACTION_DOWN events";
- matches &= arg.getDownTime() == arg.getEventTime();
- }
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
- matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
- }
- return matches;
-}
-
MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
return arg.getDownTime() == downTime;
}
-MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
- return arg.getDisplayId() == displayId;
-}
-
-MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") {
- return arg.getDeviceId() == deviceId;
-}
-
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
<< inputEventSourceToString(arg.getSource());
@@ -202,9 +193,10 @@
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
- InputDispatcherConfiguration mConfig;
-
- using AnrResult = std::pair<sp<IBinder>, int32_t /*pid*/>;
+ struct AnrResult {
+ sp<IBinder> token{};
+ gui::Pid pid{gui::Pid::INVALID};
+ };
public:
FakeInputDispatcherPolicy() = default;
@@ -294,15 +286,14 @@
void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
const sp<IBinder>& expectedToken,
- int32_t expectedPid) {
+ gui::Pid expectedPid) {
std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLocked(mLock);
AnrResult result;
ASSERT_NO_FATAL_FAILURE(result =
getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock));
- const auto& [token, pid] = result;
- ASSERT_EQ(expectedToken, token);
- ASSERT_EQ(expectedPid, pid);
+ ASSERT_EQ(expectedToken, result.token);
+ ASSERT_EQ(expectedPid, result.pid);
}
/** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
@@ -315,15 +306,14 @@
}
void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken,
- int32_t expectedPid) {
+ gui::Pid expectedPid) {
std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLocked(mLock);
AnrResult result;
ASSERT_NO_FATAL_FAILURE(
result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock));
- const auto& [token, pid] = result;
- ASSERT_EQ(expectedToken, token);
- ASSERT_EQ(expectedPid, pid);
+ ASSERT_EQ(expectedToken, result.token);
+ ASSERT_EQ(expectedPid, result.pid);
}
/** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
@@ -344,11 +334,6 @@
"signal";
}
- void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
- mConfig.keyRepeatTimeout = timeout;
- mConfig.keyRepeatDelay = delay;
- }
-
PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) {
std::unique_lock lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
@@ -379,7 +364,9 @@
mPointerCaptureRequest.reset();
}
- void assertDropTargetEquals(const sp<IBinder>& targetToken) {
+ void assertDropTargetEquals(const InputDispatcherInterface& dispatcher,
+ const sp<IBinder>& targetToken) {
+ dispatcher.waitForIdle();
std::scoped_lock lock(mLock);
ASSERT_TRUE(mNotifyDropWindowWasCalled);
ASSERT_EQ(targetToken, mDropTargetWindowToken);
@@ -413,6 +400,14 @@
ASSERT_FALSE(mPokedUserActivity) << "Expected user activity not to have been poked";
}
+ void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids) {
+ ASSERT_EQ(std::make_pair(deviceId, uids), mNotifiedInteractions.popWithTimeout(100ms));
+ }
+
+ void assertNotifyDeviceInteractionWasNotCalled() {
+ ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms));
+ }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -438,6 +433,8 @@
std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
+ BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
+
// All three ANR-related callbacks behave the same way, so we use this generic function to wait
// for a specific container to become non-empty. When the container is non-empty, return the
// first entry from the container and erase it.
@@ -493,7 +490,7 @@
mConfigurationChangedTime = when;
}
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid,
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
const std::string&) override {
std::scoped_lock lock(mLock);
ASSERT_TRUE(pid.has_value());
@@ -502,7 +499,7 @@
}
void notifyWindowResponsive(const sp<IBinder>& connectionToken,
- std::optional<int32_t> pid) override {
+ std::optional<gui::Pid> pid) override {
std::scoped_lock lock(mLock);
ASSERT_TRUE(pid.has_value());
mResponsiveWindows.push({connectionToken, *pid});
@@ -533,8 +530,6 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override {}
- InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
-
bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
switch (inputEvent.getType()) {
@@ -609,6 +604,11 @@
mDropTargetWindowToken = token;
}
+ void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) override {
+ ASSERT_TRUE(mNotifiedInteractions.emplace(deviceId, uids));
+ }
+
void assertFilterInputEventWasCalledInternal(
const std::function<void(const InputEvent&)>& verify) {
std::scoped_lock lock(mLock);
@@ -628,7 +628,7 @@
void SetUp() override {
mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, STALE_EVENT_TIMEOUT);
- mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
// Start InputDispatcher thread
ASSERT_EQ(OK, mDispatcher->start());
}
@@ -669,7 +669,7 @@
// Rejects undefined key actions.
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
INVALID_HMAC,
- /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+ /*action=*/-1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
ARBITRARY_TIME);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
@@ -705,11 +705,11 @@
ui::Transform identityTransform;
// Rejects undefined motion actions.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
- /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
+ /*action=*/-1, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -721,7 +721,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -733,7 +733,7 @@
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -745,7 +745,7 @@
0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -757,7 +757,7 @@
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -769,7 +769,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 0, pointerProperties, pointerCoords);
+ /*pointerCount=*/0, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -780,7 +780,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -793,7 +793,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -805,7 +805,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -819,7 +819,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 2, pointerProperties, pointerCoords);
+ /*pointerCount=*/2, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -846,7 +846,8 @@
mFakePolicy->assertNotifySwitchWasCalled(args);
}
-// --- InputDispatcherTest SetInputWindowTest ---
+namespace {
+
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
@@ -878,9 +879,9 @@
mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
}
- InputEvent* consume() {
+ InputEvent* consume(std::chrono::milliseconds timeout) {
InputEvent* event;
- std::optional<uint32_t> consumeSeq = receiveEvent(&event);
+ std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event);
if (!consumeSeq) {
return nullptr;
}
@@ -892,7 +893,8 @@
* Receive an event without acknowledging it.
* Return the sequence number that could later be used to send finished signal.
*/
- std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
+ std::optional<uint32_t> receiveEvent(std::chrono::milliseconds timeout,
+ InputEvent** outEvent = nullptr) {
uint32_t consumeSeq;
InputEvent* event;
@@ -902,7 +904,7 @@
status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
&event);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
- if (elapsed > 100ms) {
+ if (elapsed > timeout) {
break;
}
}
@@ -942,7 +944,7 @@
void consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
@@ -988,7 +990,7 @@
}
MotionEvent* consumeMotion() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
if (event == nullptr) {
ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one.";
@@ -1009,7 +1011,7 @@
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode) {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::FOCUS, event->getType())
@@ -1023,7 +1025,7 @@
}
void consumeCaptureEvent(bool hasCapture) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::CAPTURE, event->getType())
@@ -1037,7 +1039,7 @@
}
void consumeDragEvent(bool isExiting, float x, float y) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event;
@@ -1052,7 +1054,7 @@
}
void consumeTouchModeEvent(bool inTouchMode) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType())
@@ -1065,7 +1067,7 @@
}
void assertNoEvents() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
if (event == nullptr) {
return;
}
@@ -1143,12 +1145,12 @@
mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
}
- sp<FakeWindowHandle> clone(
- const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId) {
- sp<FakeWindowHandle> handle =
- sp<FakeWindowHandle>::make(inputApplicationHandle, dispatcher,
- mInfo.name + "(Mirror)", displayId, mInfo.token);
+ sp<FakeWindowHandle> clone(int32_t displayId) {
+ sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)");
+ handle->mInfo = mInfo;
+ handle->mInfo.displayId = displayId;
+ handle->mInfo.id = sId++;
+ handle->mInputReceiver = mInputReceiver;
return handle;
}
@@ -1248,6 +1250,25 @@
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
+ KeyEvent* consumeKey() {
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consume failed : no event";
+ return nullptr;
+ }
+ if (event->getType() != InputEventType::KEY) {
+ ADD_FAILURE() << "Instead of key event, got " << *event;
+ return nullptr;
+ }
+ return static_cast<KeyEvent*>(event);
+ }
+
+ void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) {
+ KeyEvent* keyEvent = consumeKey();
+ ASSERT_NE(nullptr, keyEvent) << "Did not get a key event, but expected " << matcher;
+ ASSERT_THAT(*keyEvent, matcher);
+ }
+
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags);
}
@@ -1308,13 +1329,11 @@
void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- InputEvent* event = consume();
- ASSERT_NE(nullptr, event);
- ASSERT_EQ(InputEventType::MOTION, event->getType());
- const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked());
- EXPECT_EQ(0.f, motionEvent.getRawPointerCoords(0)->getX());
- EXPECT_EQ(0.f, motionEvent.getRawPointerCoords(0)->getY());
+ MotionEvent* motionEvent = consumeMotion();
+ ASSERT_NE(nullptr, motionEvent);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent->getActionMasked());
+ EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getX());
+ EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getY());
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
@@ -1358,7 +1377,7 @@
ADD_FAILURE() << "Invalid receive event on window with no receiver";
return std::nullopt;
}
- return mInputReceiver->receiveEvent(outEvent);
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED, outEvent);
}
void finishEvent(uint32_t sequenceNum) {
@@ -1371,15 +1390,15 @@
mInputReceiver->sendTimeline(inputEventId, timeline);
}
- InputEvent* consume() {
+ InputEvent* consume(std::chrono::milliseconds timeout) {
if (mInputReceiver == nullptr) {
return nullptr;
}
- return mInputReceiver->consume();
+ return mInputReceiver->consume(timeout);
}
MotionEvent* consumeMotion() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
if (event == nullptr) {
ADD_FAILURE() << "Consume failed : no event";
return nullptr;
@@ -1405,31 +1424,33 @@
const std::string& getName() { return mName; }
- void setOwnerInfo(int32_t ownerPid, int32_t ownerUid) {
+ void setOwnerInfo(gui::Pid ownerPid, gui::Uid ownerUid) {
mInfo.ownerPid = ownerPid;
mInfo.ownerUid = ownerUid;
}
- int32_t getPid() const { return mInfo.ownerPid; }
+ gui::Pid getPid() const { return mInfo.ownerPid; }
void destroyReceiver() { mInputReceiver = nullptr; }
int getChannelFd() { return mInputReceiver->getChannelFd(); }
private:
+ FakeWindowHandle(std::string name) : mName(name){};
const std::string mName;
- std::unique_ptr<FakeInputReceiver> mInputReceiver;
+ std::shared_ptr<FakeInputReceiver> mInputReceiver;
static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
+ friend class sp<FakeWindowHandle>;
};
std::atomic<int32_t> FakeWindowHandle::sId{1};
static InputEventInjectionResult injectKey(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+ InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
int32_t displayId = ADISPLAY_ID_NONE,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
- bool allowKeyRepeat = true, std::optional<int32_t> targetUid = {},
+ bool allowKeyRepeat = true, std::optional<gui::Uid> targetUid = {},
uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -1443,10 +1464,19 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(&event, targetUid, syncMode, injectionTimeout, policyFlags);
+ return dispatcher.injectInputEvent(&event, targetUid, syncMode, injectionTimeout, policyFlags);
}
-static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispatcher>& dispatcher,
+static void assertInjectedKeyTimesOut(InputDispatcher& dispatcher) {
+ InputEventInjectionResult result =
+ injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_NONE,
+ InputEventInjectionSync::WAIT_FOR_RESULT, CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
+ if (result != InputEventInjectionResult::TIMED_OUT) {
+ FAIL() << "Injection should have timed out, but got " << ftl::enum_string(result);
+ }
+}
+
+static InputEventInjectionResult injectKeyDown(InputDispatcher& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, displayId);
}
@@ -1454,36 +1484,36 @@
// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
// has to be woken up to process the repeating key.
-static InputEventInjectionResult injectKeyDownNoRepeat(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDownNoRepeat(InputDispatcher& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, displayId,
InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
/*allowKeyRepeat=*/false);
}
-static InputEventInjectionResult injectKeyUp(const std::unique_ptr<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectKeyUp(InputDispatcher& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, displayId);
}
static InputEventInjectionResult injectMotionEvent(
- const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
+ InputDispatcher& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
- std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
- return dispatcher->injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
- policyFlags);
+ std::optional<gui::Uid> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
+ return dispatcher.injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
+ policyFlags);
}
static InputEventInjectionResult injectMotionEvent(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t source,
- int32_t displayId, const PointF& position = {100, 200},
+ InputDispatcher& dispatcher, int32_t action, int32_t source, int32_t displayId,
+ const PointF& position = {100, 200},
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC),
- std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
+ std::optional<gui::Uid> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
MotionEventBuilder motionBuilder =
MotionEventBuilder(action, source)
.displayId(displayId)
@@ -1501,14 +1531,14 @@
targetUid, policyFlags);
}
-static InputEventInjectionResult injectMotionDown(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t source, int32_t displayId,
- const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionDown(InputDispatcher& dispatcher, int32_t source,
+ int32_t displayId,
+ const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
-static InputEventInjectionResult injectMotionUp(const std::unique_ptr<InputDispatcher>& dispatcher,
- int32_t source, int32_t displayId,
+static InputEventInjectionResult injectMotionUp(InputDispatcher& dispatcher, int32_t source,
+ int32_t displayId,
const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
}
@@ -1517,8 +1547,8 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A,
- KEY_A, AMETA_NONE, currentTime);
+ displayId, POLICY_FLAG_PASS_TO_USER, action, /*flags=*/0, AKEYCODE_A, KEY_A,
+ AMETA_NONE, currentTime);
return args;
}
@@ -1528,7 +1558,7 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, 0, action, /* flags */ 0, AKEYCODE_C, KEY_C, AMETA_META_ON,
+ displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, AMETA_META_ON,
currentTime);
return args;
@@ -1539,7 +1569,7 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, 0, action, /* flags */ 0, AKEYCODE_ASSIST, KEY_ASSISTANT,
+ displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, KEY_ASSISTANT,
AMETA_NONE, currentTime);
return args;
@@ -1569,12 +1599,12 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion event.
NotifyMotionArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, source, displayId,
- POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
- AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, /*flags=*/0,
+ AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
- pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+ pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /*videoFrames=*/{});
return args;
}
@@ -1592,6 +1622,8 @@
return NotifyPointerCaptureChangedArgs(/*id=*/0, systemTime(SYSTEM_TIME_MONOTONIC), request);
}
+} // namespace
+
/**
* When a window unexpectedly disposes of its input channel, policy should be notified about the
* broken channel.
@@ -1602,7 +1634,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher,
"Window that breaks its input channel", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Window closes its channel, but the window remains.
window->destroyReceiver();
@@ -1614,9 +1646,9 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -1628,10 +1660,10 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Inject a MotionEvent to an unknown display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -1639,8 +1671,8 @@
}
/**
- * Calling setInputWindows once should not cause any issues.
- * This test serves as a sanity check for the next test, where setInputWindows is
+ * Calling onWindowInfosChanged once should not cause any issues.
+ * This test serves as a sanity check for the next test, where onWindowInfosChanged is
* called twice.
*/
TEST_F(InputDispatcherTest, SetInputWindowOnceWithSingleTouchWindow) {
@@ -1649,9 +1681,9 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1660,7 +1692,7 @@
}
/**
- * Calling setInputWindows twice, with the same info, should not cause any issues.
+ * Calling onWindowInfosChanged twice, with the same info, should not cause any issues.
*/
TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -1668,10 +1700,10 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1687,9 +1719,10 @@
sp<FakeWindowHandle> windowSecond =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Top window should receive the touch down event. Second window should not receive anything.
@@ -1713,9 +1746,10 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1724,7 +1758,7 @@
wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1732,7 +1766,7 @@
wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
// Now the foreground window goes away, but the wallpaper stays
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged({{*wallpaperWindow->getInfo()}, {}, 0, 0});
foregroundWindow->consumeMotionCancel();
// Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
@@ -1748,7 +1782,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// First touch pointer down on right window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
@@ -1768,7 +1802,7 @@
window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));
// Remove the window. The gesture should be canceled
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
const std::map<int32_t, PointF> expectedPointers{{1, PointF{110, 100}}};
window->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedPointers)));
@@ -1791,9 +1825,10 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1802,7 +1837,7 @@
wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1815,7 +1850,7 @@
// Now the foreground window goes away, but the wallpaper stays, even though its channel
// is no longer valid.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged({{*wallpaperWindow->getInfo()}, {}, 0, 0});
foregroundWindow->consumeMotionCancel();
}
@@ -1839,11 +1874,12 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
// Touch down on top window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1859,7 +1895,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1875,20 +1911,19 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionPointerUp(0);
wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.displayId(ADISPLAY_ID_DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
- .pointer(PointerBuilder(/* id */ 1,
- ToolType::FINGER)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
.x(100)
.y(100))
.build(),
@@ -1923,12 +1958,15 @@
wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo(), *wallpaperWindow->getInfo()},
+ {},
+ 0,
+ 0});
// Touch down on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1944,7 +1982,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(300).y(100))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1955,7 +1993,8 @@
expectedWallpaperFlags);
// Now, leftWindow, which received the first finger, disappears.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
leftWindow->consumeMotionCancel();
// Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
@@ -1969,7 +2008,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(310).y(110))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
rightWindow->consumeMotionMove();
@@ -1999,12 +2038,15 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo(), *wallpaperWindow->getInfo()},
+ {},
+ 0,
+ 0});
// Touch down on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -2014,7 +2056,7 @@
// Move to right window, the left window should receive cancel.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {201, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -2051,7 +2093,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
// Two pointers down
@@ -2124,7 +2166,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
// All times need to start at the current time, otherwise the dispatcher will drop the events as
// stale.
const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -2132,86 +2175,74 @@
const int32_t touchDeviceId = 4;
// Move the cursor from right
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
.downTime(baseTime + 10)
.eventTime(baseTime + 20)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// .. to the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
.downTime(baseTime + 10)
.eventTime(baseTime + 30)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(110)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Now tap the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 40)
.eventTime(baseTime + 40)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// release tap
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 40)
.eventTime(baseTime + 50)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
// Tap the window on the right
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 60)
.eventTime(baseTime + 60)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// release tap
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 60)
.eventTime(baseTime + 70)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
@@ -2236,7 +2267,7 @@
window->setFrame(Rect(0, 0, 200, 200));
// Only a single window is present at first
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Start hovering in the window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
@@ -2255,7 +2286,8 @@
obscuringWindow->setNoInputChannel(true);
obscuringWindow->setFocusable(false);
obscuringWindow->setAlpha(1.0);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// While this new obscuring window is present, the hovering is stopped
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
@@ -2264,7 +2296,7 @@
window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
// Now the obscuring window goes away.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// And a new hover gesture starts.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
@@ -2284,7 +2316,7 @@
window->setFrame(Rect(0, 0, 200, 200));
// Only a single window is present at first
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Start hovering in the window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
@@ -2303,7 +2335,8 @@
obscuringWindow->setNoInputChannel(true);
obscuringWindow->setFocusable(false);
obscuringWindow->setAlpha(1.0);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// While this new obscuring window is present, the hovering continues. The event can't go to the
// bottom window due to obstructed touches, so it should generate HOVER_EXIT for that window.
@@ -2314,7 +2347,7 @@
window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
// Now the obscuring window goes away.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Hovering continues in the same position. The hovering pointer re-enters the bottom window,
// so it should generate a HOVER_ENTER
@@ -2347,11 +2380,11 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
const int32_t mouseDeviceId = 6;
- NotifyMotionArgs args;
// Start hovering over the left window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
@@ -2423,11 +2456,10 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
const int32_t mouseDeviceId = 6;
- NotifyMotionArgs args;
// First touch pointer down
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -2490,19 +2522,16 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
- NotifyMotionArgs args;
// Pretend a test injects an ACTION_DOWN mouse event, but forgets to lift up the touch after
// completion.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.deviceId(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
.build()));
window->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
@@ -2543,41 +2572,36 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
const int32_t mouseDeviceId = 6;
const int32_t touchDeviceId = 4;
// Hover over the left window. Keep the cursor there.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Tap on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
@@ -2585,27 +2609,21 @@
// First finger down on right window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// Second finger down on the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
- .pointer(PointerBuilder(1, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
@@ -2626,69 +2644,59 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t stylusDeviceId = 5;
const int32_t touchDeviceId = 4;
// Start hovering with stylus
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Finger down on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// Try to continue hovering with stylus. Since we are already down, injection should fail
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(60).y(60))
.build()));
// No event should be sent. This event should be ignored because a pointer from another device
// is already down.
// Lift up the finger
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
// Now that the touch is gone, stylus hovering should start working again
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(70).y(70))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// No more events
@@ -2712,7 +2720,7 @@
window->setNoInputChannel(true);
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Start hovering with stylus
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
@@ -2770,7 +2778,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
const int32_t mouseDeviceId = 7;
const int32_t touchDeviceId = 4;
@@ -2865,7 +2873,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", DISPLAY_ID);
- mDispatcher->setInputWindows({{DISPLAY_ID, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Touch down on the empty space
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{-1, -1}}));
@@ -2893,7 +2901,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID);
window2->setTouchableRegion(Region{{100, 0, 200, 100}});
- mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
+ mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
// Touch down on the non-touchable window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
@@ -2921,28 +2929,27 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID);
window2->setTouchableRegion(Region{{100, 0, 200, 100}});
- mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
+ mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
// Touch down on the first window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
mDispatcher->waitForIdle();
- InputEvent* inputEvent1 = window1->consume();
- ASSERT_NE(inputEvent1, nullptr);
+
+ MotionEvent* motionEvent1 = window1->consumeMotion();
+ ASSERT_NE(motionEvent1, nullptr);
window2->assertNoEvents();
- MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
- nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
- ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime());
+ nsecs_t downTimeForWindow1 = motionEvent1->getDownTime();
+ ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime());
// Now touch down on the window with another pointer
mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
mDispatcher->waitForIdle();
- InputEvent* inputEvent2 = window2->consume();
- ASSERT_NE(inputEvent2, nullptr);
- MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
- nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
+ MotionEvent* motionEvent2 = window2->consumeMotion();
+ ASSERT_NE(motionEvent2, nullptr);
+ nsecs_t downTimeForWindow2 = motionEvent2->getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
- ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime());
+ ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime());
// Now move the pointer on the second window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
@@ -2981,11 +2988,12 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
// Start cursor position in right window so that we can move the cursor to left window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(900).y(400))
@@ -2994,7 +3002,7 @@
// Move cursor into left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3004,7 +3012,7 @@
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3013,7 +3021,7 @@
windowLeft->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
@@ -3023,7 +3031,7 @@
windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
AINPUT_SOURCE_MOUSE)
.buttonState(0)
@@ -3033,7 +3041,7 @@
windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3042,7 +3050,7 @@
// Move mouse cursor back to right window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(900).y(400))
@@ -3065,7 +3073,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 600, 800));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
const int32_t mouseDeviceId = 6;
@@ -3126,16 +3134,14 @@
window->setFrame(Rect(0, 0, 600, 800));
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Send mouse cursor to the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
@@ -3160,26 +3166,22 @@
window->setFrame(Rect(0, 0, 600, 800));
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Send mouse cursor to the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
.build()));
// Move mouse cursor
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(110)
- .y(110))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
@@ -3192,13 +3194,11 @@
WithSource(AINPUT_SOURCE_MOUSE)));
// Touch down on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(200)
- .y(200))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
WithSource(AINPUT_SOURCE_MOUSE)));
@@ -3216,13 +3216,11 @@
// Touch UP on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(200)
- .y(200))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
.build()));
spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3233,13 +3231,11 @@
// One more tap - DOWN
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(250)
- .y(250))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(250))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3248,13 +3244,11 @@
// Touch UP on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(250)
- .y(250))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(250))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3275,10 +3269,10 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3286,7 +3280,7 @@
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3295,7 +3289,7 @@
window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
@@ -3305,7 +3299,7 @@
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
AINPUT_SOURCE_MOUSE)
.buttonState(0)
@@ -3315,7 +3309,7 @@
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3325,7 +3319,7 @@
// We already canceled the hovering implicitly by injecting the "DOWN" event without lifting the
// hover first. Therefore, injection of HOVER_EXIT is inconsistent, and should fail.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3345,20 +3339,18 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(400))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Remove the window, but keep the channel.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
}
@@ -3371,7 +3363,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t mouseDeviceId = 7;
const int32_t touchDeviceId = 4;
@@ -3406,7 +3398,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Inject a hover_move from mouse.
NotifyMotionArgs motionArgs =
@@ -3449,40 +3441,36 @@
SECOND_DISPLAY_ID);
windowSecondDisplay->setFrame(Rect(0, 0, 600, 800));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
- {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowDefaultDisplay->getInfo(), *windowSecondDisplay->getInfo()}, {}, 0, 0});
// Set cursor position in window in default display and check that hover enter and move
// events are generated.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.displayId(ADISPLAY_ID_DEFAULT)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(600))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(600))
.build()));
windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Remove all windows in secondary display and check that no event happens on window in
// primary display.
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, {SECOND_DISPLAY_ID, {}}});
+ mDispatcher->onWindowInfosChanged({{*windowDefaultDisplay->getInfo()}, {}, 0, 0});
+
windowDefaultDisplay->assertNoEvents();
// Move cursor position in window in default display and check that only hover move
// event is generated and not hover enter event.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
- {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowDefaultDisplay->getInfo(), *windowSecondDisplay->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.displayId(ADISPLAY_ID_DEFAULT)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(400)
- .y(700))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(400).y(700))
.build()));
windowDefaultDisplay->consumeMotionEvent(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
@@ -3502,12 +3490,13 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
// Inject an event with coordinate in the area of right window, with mouse cursor in the area of
// left window. This event should be dispatched to the left window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowRight->assertNoEvents();
@@ -3519,7 +3508,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -3541,7 +3530,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
@@ -3561,7 +3550,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(10).y(10))
@@ -3586,7 +3575,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -3602,23 +3591,28 @@
std::chrono::nanoseconds(interceptKeyTimeout).count());
}
+/**
+ * Keys with ACTION_UP are delivered immediately, even if a long 'intercept key timeout' is set.
+ */
TEST_F(InputDispatcherTest, InterceptKeyIfKeyUp) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
- mFakePolicy->setInterceptKeyTimeout(150ms);
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
-
- // Window should receive key event immediately when same key up.
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // Set a value that's significantly larger than the default consumption timeout. If the
+ // implementation is correct, the actual value doesn't matter; it won't slow down the test.
+ mFakePolicy->setInterceptKeyTimeout(600ms);
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
+ // Window should receive key event immediately when same key up.
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
}
@@ -3641,7 +3635,7 @@
outsideWindow->setFrame(Rect{100, 100, 200, 200});
outsideWindow->setWatchOutsideTouch(true);
// outsideWindow must be above 'window' to receive ACTION_OUTSIDE events when 'window' is tapped
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {outsideWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*outsideWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Tap on first window.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -3674,7 +3668,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Third Window",
ADISPLAY_ID_DEFAULT);
thirdWindow->setFrame(Rect{200, 200, 300, 300});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, secondWindow, thirdWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*window->getInfo(), *secondWindow->getInfo(), *thirdWindow->getInfo()}, {}, 0, 0});
// First pointer lands outside all windows. `window` does not get ACTION_OUTSIDE.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -3743,7 +3738,7 @@
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -3756,7 +3751,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(-30).y(-50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -3768,6 +3763,44 @@
EXPECT_EQ(-10, event->getY(1)); // -50 + 40
}
+/**
+ * Two windows: a splittable and a non-splittable.
+ * The non-splittable window shouldn't receive any "incomplete" gestures.
+ * Send the first pointer to the splittable window, and then touch the non-splittable window.
+ * The second pointer should be dropped because the initial window is splittable, so it won't get
+ * any pointers outside of it, and the second window is non-splittable, so it shouldn't get any
+ * "incomplete" gestures.
+ */
+TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setPreventSplitting(false);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right non-splittable Window",
+ ADISPLAY_ID_DEFAULT);
+ rightWindow->setPreventSplitting(true);
+ rightWindow->setFrame(Rect(100, 100, 200, 200));
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ // Touch down on left, splittable window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
+ .build());
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, TouchpadThreeFingerSwipeOnlySentToTrustedOverlays) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -3779,7 +3812,7 @@
trustedOverlay->setSpy(true);
trustedOverlay->setTrustedOverlay(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {trustedOverlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*trustedOverlay->getInfo(), *window->getInfo()}, {}, 0, 0});
// Start a three-finger touchpad swipe
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
@@ -3841,7 +3874,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Start a three-finger touchpad swipe
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
@@ -3889,6 +3922,68 @@
}
/**
+ * Send a two-pointer gesture to a single window. The window's orientation changes in response to
+ * the first pointer.
+ * Ensure that the second pointer and the subsequent gesture is correctly delivered to the window.
+ */
+TEST_F(InputDispatcherTest, MultiplePointersWithRotatingWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 400, 400));
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 10)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Change the transform so that the orientation is now different from original.
+ window->setWindowTransform(0, -1, 1, 0);
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 30)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
+ .build());
+
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+
+ // Finish the gesture and start a new one. Ensure all events are sent to the window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 40)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
+ .build());
+
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_UP));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 50)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+
+ window->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(baseTime + 60)
+ .eventTime(baseTime + 60)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .build());
+
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+}
+
+/**
* Ensure the correct coordinate spaces are used by InputDispatcher.
*
* InputDispatcher works in the display space, so its coordinate system is relative to the display
@@ -3972,7 +4067,7 @@
// Send down to the first window. The point is represented in the logical display space. The
// point is selected so that if the hit test was done in logical display space, then it would
// end up in the incorrect window.
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
PointF{75 * 2, 55 * 4});
firstWindow->consumeMotionDown();
@@ -3999,7 +4094,7 @@
.build();
event.transform(matrix);
- injectMotionEvent(mDispatcher, event, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, event, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT);
firstWindow->consumeMotionDown();
@@ -4126,7 +4221,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaper->setIsWallpaper(true);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow, wallpaper}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -4181,7 +4277,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -4224,7 +4321,8 @@
secondWindow->setPreventSplitting(true);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -4286,8 +4384,11 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper2", ADISPLAY_ID_DEFAULT);
wallpaper2->setIsWallpaper(true);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {firstWindow, wallpaper1, secondWindow, wallpaper2}}});
+ mDispatcher->onWindowInfosChanged({{*firstWindow->getInfo(), *wallpaper1->getInfo(),
+ *secondWindow->getInfo(), *wallpaper2->getInfo()},
+ {},
+ 0,
+ 0});
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -4350,7 +4451,8 @@
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
PointF pointInFirst = {300, 200};
PointF pointInSecond = {300, 600};
@@ -4411,7 +4513,8 @@
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
PointF pointInFirst = {300, 200};
PointF pointInSecond = {300, 600};
@@ -4469,26 +4572,26 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
- sp<FakeWindowHandle> mirrorWindowInPrimary =
- firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> mirrorWindowInPrimary = firstWindowInPrimary->clone(ADISPLAY_ID_DEFAULT);
mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
- sp<FakeWindowHandle> firstWindowInSecondary =
- firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+ sp<FakeWindowHandle> firstWindowInSecondary = firstWindowInPrimary->clone(SECOND_DISPLAY_ID);
firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
- sp<FakeWindowHandle> secondWindowInSecondary =
- secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+ sp<FakeWindowHandle> secondWindowInSecondary = secondWindowInPrimary->clone(SECOND_DISPLAY_ID);
secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
// Update window info, let it find window handle of second display first.
- mDispatcher->setInputWindows(
- {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
- {ADISPLAY_ID_DEFAULT,
- {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindowInSecondary->getInfo(), *secondWindowInSecondary->getInfo(),
+ *mirrorWindowInPrimary->getInfo(), *firstWindowInPrimary->getInfo(),
+ *secondWindowInPrimary->getInfo()},
+ {},
+ 0,
+ 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4503,14 +4606,14 @@
secondWindowInPrimary->consumeMotionDown();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionMove();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
@@ -4528,27 +4631,28 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
- sp<FakeWindowHandle> mirrorWindowInPrimary =
- firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> mirrorWindowInPrimary = firstWindowInPrimary->clone(ADISPLAY_ID_DEFAULT);
mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
- sp<FakeWindowHandle> firstWindowInSecondary =
- firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+ sp<FakeWindowHandle> firstWindowInSecondary = firstWindowInPrimary->clone(SECOND_DISPLAY_ID);
firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
- sp<FakeWindowHandle> secondWindowInSecondary =
- secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+ sp<FakeWindowHandle> secondWindowInSecondary = secondWindowInPrimary->clone(SECOND_DISPLAY_ID);
secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
// Update window info, let it find window handle of second display first.
- mDispatcher->setInputWindows(
- {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
- {ADISPLAY_ID_DEFAULT,
- {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindowInSecondary->getInfo(), *secondWindowInSecondary->getInfo(),
+ *mirrorWindowInPrimary->getInfo(), *firstWindowInPrimary->getInfo(),
+ *secondWindowInPrimary->getInfo()},
+ {},
+ 0,
+ 0});
// Touch on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
+ {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -4562,14 +4666,14 @@
secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
SECOND_DISPLAY_ID, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
@@ -4581,7 +4685,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4592,6 +4696,7 @@
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
// Should have poked user activity
+ mDispatcher->waitForIdle();
mFakePolicy->assertUserActivityPoked();
}
@@ -4602,7 +4707,7 @@
window->setDisableUserActivity(true);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4613,6 +4718,7 @@
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
// Should have poked user activity
+ mDispatcher->waitForIdle();
mFakePolicy->assertUserActivityNotPoked();
}
@@ -4622,7 +4728,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4643,7 +4749,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4665,7 +4771,7 @@
window->setDisableUserActivity(true);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4685,10 +4791,10 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4696,6 +4802,7 @@
AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Should have poked user activity
+ mDispatcher->waitForIdle();
mFakePolicy->assertUserActivityPoked();
}
@@ -4704,7 +4811,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
mDispatcher->waitForIdle();
@@ -4718,7 +4825,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Send key
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
@@ -4745,7 +4852,8 @@
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
PointF pointInFirst = {300, 200};
PointF pointInSecond = {300, 600};
@@ -4789,7 +4897,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
@@ -4815,7 +4923,9 @@
expectedFlags);
}
- std::optional<int32_t> receiveEvent() { return mInputReceiver->receiveEvent(); }
+ std::optional<int32_t> receiveEvent() {
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ }
void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
@@ -4848,18 +4958,7 @@
/*expectedFlags=*/0);
}
- MotionEvent* consumeMotion() {
- InputEvent* event = mInputReceiver->consume();
- if (!event) {
- ADD_FAILURE() << "No event was produced";
- return nullptr;
- }
- if (event->getType() != InputEventType::MOTION) {
- ADD_FAILURE() << "Expected MotionEvent, got " << *event;
- return nullptr;
- }
- return static_cast<MotionEvent*>(event);
- }
+ MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
void assertNoEvents() { mInputReceiver->assertNoEvents(); }
@@ -4884,9 +4983,9 @@
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4895,7 +4994,7 @@
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4903,14 +5002,14 @@
monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
// Now the foreground window goes away
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
window->consumeMotionCancel();
monitor.assertNoEvents(); // Global monitor does not get a cancel yet
// If more events come in, there will be no more foreground window to send them to. This will
// cause a cancel for the monitor, as well.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {120, 200}))
<< "Injection should fail because the window was removed";
window->assertNoEvents();
@@ -4922,12 +5021,12 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -4939,10 +5038,10 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -4952,7 +5051,7 @@
EXPECT_NE(OK, mDispatcher->pilferPointers(monitor.getToken()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4964,14 +5063,14 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
window->setWindowOffset(20, 40);
window->setWindowTransform(0, 1, -1, 0);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
MotionEvent* event = monitor.consumeMotion();
@@ -4984,7 +5083,7 @@
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Injection should fail if there is a monitor, but no touchable window";
monitor.assertNoEvents();
}
@@ -4994,7 +5093,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -5031,13 +5130,13 @@
window->setFocusable(true);
SCOPED_TRACE("Check default value of touch mode");
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
SCOPED_TRACE("Remove the window to trigger focus loss");
window->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
window->consumeFocusEvent(/*hasFocus=*/false, /*inTouchMode=*/true);
SCOPED_TRACE("Disable touch mode");
@@ -5045,13 +5144,13 @@
/*hasPermission=*/true, ADISPLAY_ID_DEFAULT);
window->consumeTouchModeEvent(false);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/false);
SCOPED_TRACE("Remove the window to trigger focus loss");
window->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
window->consumeFocusEvent(/*hasFocus=*/false, /*inTouchMode=*/false);
SCOPED_TRACE("Enable touch mode again");
@@ -5059,7 +5158,7 @@
/*hasPermission=*/true, ADISPLAY_ID_DEFAULT);
window->consumeTouchModeEvent(true);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -5074,7 +5173,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -5082,7 +5181,7 @@
const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
mDispatcher->notifyKey(keyArgs);
- InputEvent* event = window->consume();
+ KeyEvent* event = window->consumeKey();
ASSERT_NE(event, nullptr);
std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
@@ -5126,7 +5225,7 @@
ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(motionArgs);
- InputEvent* event = window->consume();
+ MotionEvent* event = window->consumeMotion();
ASSERT_NE(event, nullptr);
std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
@@ -5220,11 +5319,12 @@
// Top window is also focusable but is not granted focus.
windowTop->setFocusable(true);
windowSecond->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
setFocusedWindow(windowSecond);
windowSecond->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
@@ -5241,12 +5341,11 @@
window->setFocusable(true);
// Release channel for window is no longer valid.
window->releaseChannel();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
// Test inject a key down, should timeout.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
// window channel is invalid, so it should not receive any input event.
window->assertNoEvents();
@@ -5259,12 +5358,11 @@
window->setFocusable(false);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
// Test inject a key down, should timeout.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
// window is not focusable, so it should not receive any input event.
window->assertNoEvents();
@@ -5280,16 +5378,18 @@
windowTop->setFocusable(true);
windowSecond->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
setFocusedWindow(windowTop);
windowTop->consumeFocusEvent(true);
windowTop->editInfo()->focusTransferTarget = windowSecond->getToken();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
windowSecond->consumeFocusEvent(true);
windowTop->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
@@ -5307,11 +5407,12 @@
windowTop->setFocusable(true);
windowSecond->setFocusable(false);
windowTop->editInfo()->focusTransferTarget = windowSecond->getToken();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
setFocusedWindow(windowTop);
windowTop->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Event should be dropped.
@@ -5331,7 +5432,8 @@
window->setFocusable(true);
previousFocusedWindow->setFocusable(true);
window->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, previousFocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*window->getInfo(), *previousFocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(previousFocusedWindow);
previousFocusedWindow->consumeFocusEvent(true);
@@ -5341,15 +5443,15 @@
// Injected key goes to pending queue.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
// Window does not get focus event or key down.
window->assertNoEvents();
// Window becomes visible.
window->setVisible(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Window receives focus event.
window->consumeFocusEvent(true);
@@ -5365,7 +5467,7 @@
// window is granted focus.
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -5398,8 +5500,8 @@
* FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
*/
TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) {
- constexpr int32_t SLIPPERY_PID = WINDOW_PID + 1;
- constexpr int32_t SLIPPERY_UID = WINDOW_UID + 1;
+ constexpr gui::Pid SLIPPERY_PID{WINDOW_PID.val() + 1};
+ constexpr gui::Uid SLIPPERY_UID{WINDOW_UID.val() + 1};
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -5417,8 +5519,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
slipperyExitWindow->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*slipperyExitWindow->getInfo(), *slipperyEnterWindow->getInfo()}, {}, 0, 0});
// Use notifyMotion instead of injecting to avoid dealing with injection permissions
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -5426,8 +5528,8 @@
{{50, 50}}));
slipperyExitWindow->consumeMotionDown();
slipperyExitWindow->setFrame(Rect(70, 70, 100, 100));
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*slipperyExitWindow->getInfo(), *slipperyEnterWindow->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_MOVE,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
@@ -5459,8 +5561,8 @@
rightDropTouchesWindow->setFrame(Rect(100, 0, 200, 100));
rightDropTouchesWindow->setDropInput(true);
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {leftSlipperyWindow, rightDropTouchesWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftSlipperyWindow->getInfo(), *rightDropTouchesWindow->getInfo()}, {}, 0, 0});
// Start touch in the left window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -5479,6 +5581,110 @@
rightDropTouchesWindow->assertNoEvents();
}
+TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithMotions) {
+ using Uid = gui::Uid;
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWindow->setOwnerInfo(gui::Pid{1}, Uid{101});
+
+ sp<FakeWindowHandle> rightSpy =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right spy", ADISPLAY_ID_DEFAULT);
+ rightSpy->setFrame(Rect(100, 0, 200, 100));
+ rightSpy->setOwnerInfo(gui::Pid{2}, Uid{102});
+ rightSpy->setSpy(true);
+ rightSpy->setTrustedOverlay(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(100, 0, 200, 100));
+ rightWindow->setOwnerInfo(gui::Pid{3}, Uid{103});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightSpy->getInfo(), *rightWindow->getInfo(), *leftWindow->getInfo()}, {}, 0, 0});
+
+ // Touch in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ ASSERT_NO_FATAL_FAILURE(leftWindow->consumeMotionDown());
+ mDispatcher->waitForIdle();
+ ASSERT_NO_FATAL_FAILURE(
+ mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {Uid{101}}));
+
+ // Touch another finger over the right windows
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .build());
+ ASSERT_NO_FATAL_FAILURE(rightSpy->consumeMotionDown());
+ ASSERT_NO_FATAL_FAILURE(rightWindow->consumeMotionDown());
+ ASSERT_NO_FATAL_FAILURE(leftWindow->consumeMotionMove());
+ mDispatcher->waitForIdle();
+ ASSERT_NO_FATAL_FAILURE(
+ mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID,
+ {Uid{101}, Uid{102}, Uid{103}}));
+
+ // Release finger over left window. The UP actions are not treated as device interaction.
+ // The windows that did not receive the UP pointer will receive MOVE events, but since this
+ // is part of the UP action, we do not treat this as device interaction.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .build());
+ ASSERT_NO_FATAL_FAILURE(leftWindow->consumeMotionUp());
+ ASSERT_NO_FATAL_FAILURE(rightSpy->consumeMotionMove());
+ ASSERT_NO_FATAL_FAILURE(rightWindow->consumeMotionMove());
+ mDispatcher->waitForIdle();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
+
+ // Move remaining finger
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .build());
+ ASSERT_NO_FATAL_FAILURE(rightSpy->consumeMotionMove());
+ ASSERT_NO_FATAL_FAILURE(rightWindow->consumeMotionMove());
+ mDispatcher->waitForIdle();
+ ASSERT_NO_FATAL_FAILURE(
+ mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {Uid{102}, Uid{103}}));
+
+ // Release all fingers
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .build());
+ ASSERT_NO_FATAL_FAILURE(rightSpy->consumeMotionUp());
+ ASSERT_NO_FATAL_FAILURE(rightWindow->consumeMotionUp());
+ mDispatcher->waitForIdle();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
+}
+
+TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithKeys) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+ window->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+ ASSERT_NO_FATAL_FAILURE(window->consumeFocusEvent(true));
+
+ mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build());
+ ASSERT_NO_FATAL_FAILURE(window->consumeKeyDown(ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
+ ASSERT_NO_FATAL_FAILURE(
+ mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {gui::Uid{101}}));
+
+ // The UP actions are not treated as device interaction.
+ mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).build());
+ ASSERT_NO_FATAL_FAILURE(window->consumeKeyUp(ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
@@ -5489,10 +5695,9 @@
virtual void SetUp() override {
mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
- mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
- mDispatcher->requestRefreshConfiguration();
- mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
+ mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
ASSERT_EQ(OK, mDispatcher->start());
setUpWindow();
@@ -5503,7 +5708,7 @@
mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mWindow->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -5520,15 +5725,8 @@
void expectKeyRepeatOnce(int32_t repeatCount) {
SCOPED_TRACE(StringPrintf("Checking event with repeat count %" PRId32, repeatCount));
- InputEvent* repeatEvent = mWindow->consume();
- ASSERT_NE(nullptr, repeatEvent);
-
- ASSERT_EQ(InputEventType::KEY, repeatEvent->getType());
-
- KeyEvent* repeatKeyEvent = static_cast<KeyEvent*>(repeatEvent);
- uint32_t eventAction = repeatKeyEvent->getAction();
- EXPECT_EQ(AKEY_EVENT_ACTION_DOWN, eventAction);
- EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount());
+ mWindow->consumeKeyEvent(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithRepeatCount(repeatCount)));
}
void sendAndConsumeKeyUp(int32_t deviceId) {
@@ -5608,7 +5806,7 @@
GTEST_SKIP() << "Flaky test (b/270393106)";
sendAndConsumeKeyDown(/*deviceId=*/1);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- InputEvent* repeatEvent = mWindow->consume();
+ KeyEvent* repeatEvent = mWindow->consumeKey();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER,
IdGenerator::getSource(repeatEvent->getId()));
@@ -5621,7 +5819,7 @@
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- InputEvent* repeatEvent = mWindow->consume();
+ KeyEvent* repeatEvent = mWindow->consumeKey();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
int32_t id = repeatEvent->getId();
EXPECT_EQ(idSet.end(), idSet.find(id));
@@ -5642,7 +5840,8 @@
// Set focus window for primary display, but focused display would be second one.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
windowInPrimary->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}});
+ mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0});
+
setFocusedWindow(windowInPrimary);
windowInPrimary->consumeFocusEvent(true);
@@ -5655,7 +5854,8 @@
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocusable(true);
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowInPrimary->getInfo(), *windowInSecondary->getInfo()}, {}, 0, 0});
setFocusedWindow(windowInSecondary);
windowInSecondary->consumeFocusEvent(true);
}
@@ -5679,14 +5879,14 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
@@ -5695,27 +5895,26 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDownNoRepeat(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test inject a key down without display id specified.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
// Remove all windows in secondary display.
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
+ mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0});
// Old focus should receive a cancel event.
windowInSecondary->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDownNoRepeat(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
windowInPrimary->assertNoEvents();
windowInSecondary->consumeFocusEvent(false);
windowInSecondary->assertNoEvents();
@@ -5730,7 +5929,7 @@
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -5739,7 +5938,7 @@
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -5748,7 +5947,7 @@
// Lift up the touch from the second display
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInSecondary->consumeMotionUp(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionUp(SECOND_DISPLAY_ID);
@@ -5757,7 +5956,7 @@
// If specific a display, it will dispatch to the focused window of particular display,
// or it will dispatch to the focused window of focused display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -5774,7 +5973,7 @@
FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -5786,13 +5985,19 @@
sp<FakeWindowHandle> secondWindowInPrimary =
sp<FakeWindowHandle>::make(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
secondWindowInPrimary->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary, secondWindowInPrimary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowInPrimary->getInfo(), *secondWindowInPrimary->getInfo(),
+ *windowInSecondary->getInfo()},
+ {},
+ 0,
+ 0});
setFocusedWindow(secondWindowInPrimary);
windowInPrimary->consumeFocusEvent(false);
secondWindowInPrimary->consumeFocusEvent(true);
// Test inject a key down.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->assertNoEvents();
@@ -5807,14 +6012,14 @@
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
@@ -5828,14 +6033,14 @@
// Test inject a move motion event, no window/monitor should receive the event.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
SECOND_DISPLAY_ID, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
windowInSecondary->assertNoEvents();
@@ -5856,7 +6061,7 @@
mDispatcher->notifyMotion(motionArgs);
ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
- const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue());
+ const auto xy = transform.transform(motionArgs.pointerCoords[0].getXYValue());
mFakePolicy->assertFilterInputEventWasCalled(motionArgs, xy);
} else {
mFakePolicy->assertFilterInputEventWasNotCalled();
@@ -5883,36 +6088,36 @@
// Test InputFilter for MotionEvent
TEST_F(InputFilterTest, MotionEvent_InputFilter) {
// Since the InputFilter is disabled by default, check if touch events aren't filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
- testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/false);
// Enable InputFilter
mDispatcher->setInputFilterEnabled(true);
// Test touch on both primary and second display, and check if both events are filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true);
- testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true);
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/true);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/true);
// Disable InputFilter
mDispatcher->setInputFilterEnabled(false);
// Test touch on both primary and second display, and check if both events aren't filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
- testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/false);
}
// Test InputFilter for KeyEvent
TEST_F(InputFilterTest, KeyEvent_InputFilter) {
// Since the InputFilter is disabled by default, check if key event aren't filtered.
- testNotifyKey(/*expectToBeFiltered*/ false);
+ testNotifyKey(/*expectToBeFiltered=*/false);
// Enable InputFilter
mDispatcher->setInputFilterEnabled(true);
// Send a key event, and check if it is filtered.
- testNotifyKey(/*expectToBeFiltered*/ true);
+ testNotifyKey(/*expectToBeFiltered=*/true);
// Disable InputFilter
mDispatcher->setInputFilterEnabled(false);
// Send a key event, and check if it isn't filtered.
- testNotifyKey(/*expectToBeFiltered*/ false);
+ testNotifyKey(/*expectToBeFiltered=*/false);
}
// Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the
@@ -5935,8 +6140,8 @@
mDispatcher->setInputFilterEnabled(true);
// Ensure the correct transforms are used for the displays.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform);
- testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform);
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/true, firstDisplayTransform);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/true, secondDisplayTransform);
}
class InputFilterInjectionPolicyTest : public InputDispatcherTest {
@@ -5958,7 +6163,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
mWindow->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -5975,15 +6180,10 @@
POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{},
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms,
policyFlags | additionalPolicyFlags));
- InputEvent* received = mWindow->consume();
- ASSERT_NE(nullptr, received);
- ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
- ASSERT_EQ(received->getType(), InputEventType::KEY);
- KeyEvent& keyEvent = static_cast<KeyEvent&>(*received);
- ASSERT_EQ(flags, keyEvent.getFlags());
+ mWindow->consumeKeyEvent(AllOf(WithDeviceId(resolvedDeviceId), WithFlags(flags)));
}
void testInjectedMotion(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId,
@@ -6005,20 +6205,15 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, eventTime,
eventTime,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER;
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{},
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms,
policyFlags | additionalPolicyFlags));
- InputEvent* received = mWindow->consume();
- ASSERT_NE(nullptr, received);
- ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
- ASSERT_EQ(received->getType(), InputEventType::MOTION);
- MotionEvent& motionEvent = static_cast<MotionEvent&>(*received);
- ASSERT_EQ(flags, motionEvent.getFlags());
+ mWindow->consumeMotionEvent(AllOf(WithFlags(flags), WithDeviceId(resolvedDeviceId)));
}
private:
@@ -6070,7 +6265,8 @@
mFocusedWindow->setFocusable(true);
// Expect one focus window exist in display.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -6093,7 +6289,7 @@
// the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{20, 20}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeMotionDown();
@@ -6107,7 +6303,8 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT,
+ {20, 20}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -6119,7 +6316,7 @@
// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDownNoRepeat(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
@@ -6132,7 +6329,7 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_TOUCH_POINT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -6150,7 +6347,7 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(20).y(20))
.addFlag(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)
.build();
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, event))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, event))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
@@ -6176,7 +6373,7 @@
ADISPLAY_ID_DEFAULT, mWindow1->getToken());
mWindow2->setFrame(Rect(100, 100, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
}
protected:
@@ -6192,28 +6389,24 @@
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
const std::vector<PointF>& points) {
const std::string name = window->getName();
- InputEvent* event = window->consume();
+ MotionEvent* motionEvent = window->consumeMotion();
- ASSERT_NE(nullptr, event) << name.c_str()
- << ": consumer should have returned non-NULL event.";
+ ASSERT_NE(nullptr, motionEvent)
+ << name.c_str() << ": consumer should have returned non-NULL event.";
- ASSERT_EQ(InputEventType::MOTION, event->getType())
- << name.c_str() << ": expected MotionEvent, got " << *event;
-
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- assertMotionAction(expectedAction, motionEvent.getAction());
- ASSERT_EQ(points.size(), motionEvent.getPointerCount());
+ assertMotionAction(expectedAction, motionEvent->getAction());
+ ASSERT_EQ(points.size(), motionEvent->getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
float expectedY = points[i].y;
- EXPECT_EQ(expectedX, motionEvent.getX(i))
+ EXPECT_EQ(expectedX, motionEvent->getX(i))
<< "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent.getX(i);
- EXPECT_EQ(expectedY, motionEvent.getY(i))
+ << ", got " << motionEvent->getX(i);
+ EXPECT_EQ(expectedY, motionEvent->getY(i))
<< "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent.getY(i);
+ << ", got " << motionEvent->getY(i);
}
}
@@ -6245,6 +6438,7 @@
TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) {
// Set scale value for window2
mWindow2->setWindowScale(0.5f, 0.5f);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch Window 1
PointF touchedPoint = {10, 10};
@@ -6261,12 +6455,14 @@
// Update the transform so rotation is set
mWindow2->setWindowTransform(0, -1, 1, 0);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
@@ -6284,12 +6480,14 @@
// Update the transform so rotation is set for Window 2
mWindow2->setWindowTransform(0, -1, 1, 0);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
@@ -6315,6 +6513,7 @@
// Touch Window 2
mWindow2->setWindowTransform(0, -1, 1, 0);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
@@ -6328,6 +6527,7 @@
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
mWindow1->setWindowScale(0.5f, 0.5f);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
@@ -6354,7 +6554,7 @@
*/
TEST_F(InputDispatcherMultiWindowSameTokenTests, TouchDoesNotSlipEvenIfSlippery) {
mWindow1->setSlippery(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch down in window 1
mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -6376,7 +6576,7 @@
* that the pointer is hovering over may have a different transform.
*/
TEST_F(InputDispatcherMultiWindowSameTokenTests, HoverIntoClone) {
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Start hover in window 1
mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_TOUCHSCREEN,
@@ -6398,17 +6598,17 @@
InputDispatcherTest::SetUp();
mApplication = std::make_shared<FakeApplicationHandle>();
- mApplication->setDispatchingTimeout(20ms);
+ mApplication->setDispatchingTimeout(100ms);
mWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "TestWindow",
ADISPLAY_ID_DEFAULT);
mWindow->setFrame(Rect(0, 0, 30, 30));
- mWindow->setDispatchingTimeout(30ms);
+ mWindow->setDispatchingTimeout(100ms);
mWindow->setFocusable(true);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -6419,16 +6619,17 @@
}
protected:
+ static constexpr std::chrono::duration SPY_TIMEOUT = 200ms;
std::shared_ptr<FakeApplicationHandle> mApplication;
sp<FakeWindowHandle> mWindow;
static constexpr PointF WINDOW_LOCATION = {20, 20};
void tapOnWindow() {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
}
@@ -6438,8 +6639,8 @@
spy->setTrustedOverlay(true);
spy->setFocusable(false);
spy->setSpy(true);
- spy->setDispatchingTimeout(30ms);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, mWindow}}});
+ spy->setDispatchingTimeout(SPY_TIMEOUT);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
return spy;
}
};
@@ -6455,7 +6656,7 @@
// Send a regular key and respond, which should not cause an ANR.
TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher));
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -6463,12 +6664,12 @@
TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, CONSUME_TIMEOUT_EVENT_EXPECTED,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not go to window because we have no focused window.
@@ -6487,7 +6688,7 @@
// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
@@ -6505,7 +6706,7 @@
// Send a key to the app and have the app not respond right away.
TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
// Inject a key, and don't respond - expect that ANR is called.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6516,12 +6717,12 @@
// We have a focused application, but no focused window
TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
// taps on the window work as normal
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
mDispatcher->waitForIdle();
@@ -6531,8 +6732,8 @@
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, /*allowKeyRepeat=*/false);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 50ms, /*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
@@ -6545,7 +6746,7 @@
*/
TEST_F(InputDispatcherSingleWindowAnr, StaleKeyEventDoesNotAnr) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
KeyEvent event;
@@ -6554,7 +6755,7 @@
// Define a valid key down event that is stale (too old).
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
+ INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /*flags=*/0, AKEYCODE_A, KEY_A,
AMETA_NONE, /*repeatCount=*/1, eventTime, eventTime);
const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
@@ -6575,15 +6776,15 @@
// Make sure that we don't notify policy twice about the same ANR.
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, /*allowKeyRepeat=*/false);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms, /*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration appTimeout =
mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6601,20 +6802,17 @@
// We have a focused application, but no focused window
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
// Once a focused event arrives, we get an ANR for this application
- const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
// Future focused events get dropped right away
- ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(*mDispatcher));
ASSERT_TRUE(mDispatcher->waitForIdle());
mWindow->assertNoEvents();
}
@@ -6630,14 +6828,14 @@
*/
TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// Now send ACTION_UP, with identical timestamp
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
@@ -6654,7 +6852,7 @@
sp<FakeWindowHandle> spy = addSpyWindow();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
mWindow->consumeMotionDown();
@@ -6676,9 +6874,9 @@
sp<FakeWindowHandle> spy = addSpyWindow();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT));
mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher, ADISPLAY_ID_DEFAULT));
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6723,19 +6921,20 @@
}
TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) {
- mDispatcher->setMonitorDispatchingTimeoutForTest(30ms);
+ mDispatcher->setMonitorDispatchingTimeoutForTest(SPY_TIMEOUT);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
const std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(30ms, monitor.getToken(), MONITOR_PID);
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(SPY_TIMEOUT, monitor.getToken(),
+ MONITOR_PID);
monitor.finishEvent(*consumeSeq);
monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
@@ -6777,7 +6976,7 @@
// it.
TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6797,19 +6996,19 @@
/**
* If a window is processing a motion event, and then a key event comes in, the key event should
- * not to to the focused window until the motion is processed.
+ * not get delivered to the focused window until the motion is processed.
*
* Warning!!!
* This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
* and the injection timeout that we specify when injecting the key.
- * We must have the injection timeout (10ms) be smaller than
+ * We must have the injection timeout (100ms) be smaller than
* KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
*
* If that value changes, this test should also change.
*/
TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
tapOnWindow();
std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
@@ -6822,13 +7021,15 @@
// we will receive INJECTION_TIMED_OUT as the result.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
// Key will not be sent to the window, yet, because the window is still processing events
// and the key remains pending, waiting for the touch events to be processed
- std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // Make sure that `assertNoEvents` doesn't wait too long, because it could cause an ANR.
+ // Rely here on the fact that it uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mWindow->assertNoEvents();
std::this_thread::sleep_for(500ms);
// if we wait long enough though, dispatcher will give up, and still send the key
@@ -6847,7 +7048,7 @@
TEST_F(InputDispatcherSingleWindowAnr,
PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) {
mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
tapOnWindow();
std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
@@ -6857,11 +7058,13 @@
// Don't finish the events yet, and send a key
// Injection is async, so it will succeed
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
// At this point, key is still pending, and should not be sent to the application yet.
- std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // Make sure the `assertNoEvents` check doesn't take too long. It uses
+ // CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mWindow->assertNoEvents();
// Now tap down again. It should cause the pending key to go to the focused window right away.
tapOnWindow();
@@ -6874,12 +7077,61 @@
mWindow->assertNoEvents();
}
+/**
+ * Send an event to the app and have the app not respond right away.
+ * When ANR is raised, policy will tell the dispatcher to cancel the events for that window.
+ * So InputDispatcher will enqueue ACTION_CANCEL event as well.
+ * At some point, the window becomes responsive again.
+ * Ensure that subsequent events get dropped, and the next gesture is delivered.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, TwoGesturesWithAnr) {
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10))
+ .build());
+
+ std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
+ ASSERT_TRUE(sequenceNum);
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
+
+ mWindow->finishEvent(*sequenceNum);
+ mWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
+
+ // Now that the window is responsive, let's continue the gesture.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(11).y(11))
+ .build());
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(11).y(11))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(3).y(3))
+ .build());
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(11).y(11))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(3).y(3))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(11).y(11))
+ .build());
+ // We already canceled this pointer, so the window shouldn't get any new events.
+ mWindow->assertNoEvents();
+
+ // Start another one.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(15).y(15))
+ .build());
+ mWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+}
+
class InputDispatcherMultiWindowAnr : public InputDispatcherTest {
virtual void SetUp() override {
InputDispatcherTest::SetUp();
mApplication = std::make_shared<FakeApplicationHandle>();
- mApplication->setDispatchingTimeout(10ms);
+ mApplication->setDispatchingTimeout(100ms);
mUnfocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Unfocused",
ADISPLAY_ID_DEFAULT);
mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
@@ -6888,7 +7140,7 @@
mFocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Focused",
ADISPLAY_ID_DEFAULT);
- mFocusedWindow->setDispatchingTimeout(30ms);
+ mFocusedWindow->setDispatchingTimeout(100ms);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
// Set focused application.
@@ -6896,7 +7148,8 @@
mFocusedWindow->setFocusable(true);
// Expect one focus window exist in display.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -6923,10 +7176,10 @@
private:
void tap(const PointF& location) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
}
};
@@ -6935,7 +7188,7 @@
// should be ANR'd first.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -6946,7 +7199,7 @@
mFakePolicy->assertNotifyAnrWasNotCalled();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(unfocusedSequenceNum);
@@ -6974,20 +7227,22 @@
// But we should receive ANR for both.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) {
// Set the timeout for unfocused window to match the focused window
- mUnfocusedWindow->setDispatchingTimeout(10ms);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mUnfocusedWindow->setDispatchingTimeout(
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
+ mDispatcher->onWindowInfosChanged(
+ {{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
tapOnFocusedWindow();
// we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
- sp<IBinder> anrConnectionToken1, anrConnectionToken2;
- ASSERT_NO_FATAL_FAILURE(anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms));
- ASSERT_NO_FATAL_FAILURE(anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms));
-
// We don't know which window will ANR first. But both of them should happen eventually.
- ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
- mFocusedWindow->getToken() == anrConnectionToken2);
- ASSERT_TRUE(mUnfocusedWindow->getToken() == anrConnectionToken1 ||
- mUnfocusedWindow->getToken() == anrConnectionToken2);
+ std::array<sp<IBinder>, 2> anrConnectionTokens = {mFakePolicy->getUnresponsiveWindowToken(
+ mFocusedWindow->getDispatchingTimeout(
+ DISPATCHING_TIMEOUT)),
+ mFakePolicy->getUnresponsiveWindowToken(0ms)};
+
+ ASSERT_THAT(anrConnectionTokens,
+ ::testing::UnorderedElementsAre(testing::Eq(mFocusedWindow->getToken()),
+ testing::Eq(mUnfocusedWindow->getToken())));
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -6996,15 +7251,13 @@
mFocusedWindow->consumeMotionUp();
mUnfocusedWindow->consumeMotionOutside();
- sp<IBinder> responsiveToken1, responsiveToken2;
- ASSERT_NO_FATAL_FAILURE(responsiveToken1 = mFakePolicy->getResponsiveWindowToken());
- ASSERT_NO_FATAL_FAILURE(responsiveToken2 = mFakePolicy->getResponsiveWindowToken());
+ std::array<sp<IBinder>, 2> responsiveTokens = {mFakePolicy->getResponsiveWindowToken(),
+ mFakePolicy->getResponsiveWindowToken()};
// Both applications should be marked as responsive, in any order
- ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
- mFocusedWindow->getToken() == responsiveToken2);
- ASSERT_TRUE(mUnfocusedWindow->getToken() == responsiveToken1 ||
- mUnfocusedWindow->getToken() == responsiveToken2);
+ ASSERT_THAT(responsiveTokens,
+ ::testing::UnorderedElementsAre(testing::Eq(mFocusedWindow->getToken()),
+ testing::Eq(mUnfocusedWindow->getToken())));
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -7027,10 +7280,10 @@
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
// Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
// valid touch target
@@ -7051,7 +7304,7 @@
// If you tap outside of all windows, there will not be ANR
TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
LOCATION_OUTSIDE_ALL_WINDOWS));
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -7060,10 +7313,11 @@
// Since the focused window is paused, tapping on it should not produce any events
TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
mFocusedWindow->setPaused(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
@@ -7077,13 +7331,13 @@
/**
* If a window is processing a motion event, and then a key event comes in, the key event should
- * not to to the focused window until the motion is processed.
+ * not get delivered to the focused window until the motion is processed.
* If a different window becomes focused at this time, the key should go to that window instead.
*
* Warning!!!
* This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
* and the injection timeout that we specify when injecting the key.
- * We must have the injection timeout (10ms) be smaller than
+ * We must have the injection timeout (100ms) be smaller than
* KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
*
* If that value changes, this test should also change.
@@ -7091,7 +7345,8 @@
TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) {
// Set a long ANR timeout to prevent it from triggering
mFocusedWindow->setDispatchingTimeout(2s);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
tapOnUnfocusedWindow();
std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent();
@@ -7103,18 +7358,21 @@
// window even if motions are still being processed.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not be sent to the window, yet, because the window is still processing events
- // and the key remains pending, waiting for the touch events to be processed
- std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // and the key remains pending, waiting for the touch events to be processed.
+ // Make sure `assertNoEvents` doesn't take too long. It uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED
+ // under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mFocusedWindow->assertNoEvents();
// Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
mFocusedWindow->setFocusable(false);
mUnfocusedWindow->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mUnfocusedWindow);
// Focus events should precede the key events
@@ -7192,20 +7450,21 @@
TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
std::shared_ptr<FakeApplicationHandle> focusedApplication =
std::make_shared<FakeApplicationHandle>();
- focusedApplication->setDispatchingTimeout(60ms);
+ focusedApplication->setDispatchingTimeout(200ms);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
mFocusedWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
mFocusedWindow->consumeFocusEvent(false);
// Send a key. The ANR timer should start because there is no focused window.
// 'focusedApplication' will get blamed if this timer completes.
// Key will not be sent anywhere because we have no focused window. It will remain pending.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
@@ -7216,7 +7475,7 @@
// simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
// For this test, it means that the key would get delivered to the window once it becomes
// focused.
- std::this_thread::sleep_for(10ms);
+ std::this_thread::sleep_for(100ms);
// Touch unfocused window. This should force the pending key to get dropped.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -7227,7 +7486,8 @@
// process (== drop) the key event, and by that time, ANR will be raised.
// Set the focused window first.
mFocusedWindow->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
// We do not call "setFocusedApplication" here, even though the newly focused window belongs
@@ -7264,7 +7524,8 @@
ADISPLAY_ID_DEFAULT);
mBottomWindow->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mNoInputWindow->getInfo(), *mBottomWindow->getInfo()}, {}, 0, 0});
}
protected:
@@ -7299,7 +7560,8 @@
mNoInputWindow->setNoInputChannel(true);
mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mNoInputWindow->getInfo(), *mBottomWindow->getInfo()}, {}, 0, 0});
PointF touchedPoint = {10, 10};
@@ -7326,7 +7588,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
mWindow->setFocusable(true);
mMirror->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
}
};
@@ -7336,7 +7598,7 @@
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
}
@@ -7348,20 +7610,20 @@
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
mMirror->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
// window loses focus since one of the windows associated with the token in not focusable
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7373,30 +7635,30 @@
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
mMirror->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
mWindow->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
// window loses focus only after all windows associated with the token become invisible.
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7407,28 +7669,28 @@
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
// single window is removed but the window token remains focused
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mMirror->getInfo()}, {}, 0, 0});
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
// Both windows are removed
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7438,16 +7700,16 @@
// Request focus on an invisible mirror.
mWindow->setVisible(false);
mMirror->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
setFocusedWindow(mMirror);
// Injected key goes to pending queue.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
mMirror->setVisible(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
// window gets focused
mWindow->consumeFocusEvent(true);
@@ -7471,7 +7733,8 @@
mSecondWindow->setFocusable(true);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
@@ -7618,9 +7881,9 @@
static_assert(1 - (1 - OPACITY_FAR_BELOW_THRESHOLD) * (1 - OPACITY_FAR_BELOW_THRESHOLD) <
MAXIMUM_OBSCURING_OPACITY);
- static const int32_t TOUCHED_APP_UID = 10001;
- static const int32_t APP_B_UID = 10002;
- static const int32_t APP_C_UID = 10003;
+ static constexpr gui::Uid TOUCHED_APP_UID{10001};
+ static constexpr gui::Uid APP_B_UID{10002};
+ static constexpr gui::Uid APP_C_UID{10003};
sp<FakeWindowHandle> mTouchWindow;
@@ -7635,7 +7898,7 @@
mTouchWindow.clear();
}
- sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name, TouchOcclusionMode mode,
+ sp<FakeWindowHandle> getOccludingWindow(gui::Uid uid, std::string name, TouchOcclusionMode mode,
float alpha = 1.0f) {
sp<FakeWindowHandle> window = getWindow(uid, name);
window->setTouchable(false);
@@ -7644,12 +7907,12 @@
return window;
}
- sp<FakeWindowHandle> getWindow(int32_t uid, std::string name) {
+ sp<FakeWindowHandle> getWindow(gui::Uid uid, std::string name) {
std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(app, mDispatcher, name, ADISPLAY_ID_DEFAULT);
// Generate an arbitrary PID based on the UID
- window->setOwnerInfo(1777 + (uid % 10000), uid);
+ window->setOwnerInfo(gui::Pid{static_cast<pid_t>(1777 + (uid.val() % 10000))}, uid);
return window;
}
@@ -7663,7 +7926,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithBlockUntrustedOcclusionMode_BlocksTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7674,7 +7937,7 @@
WindowWithBlockUntrustedOcclusionModeWithOpacityBelowThreshold_BlocksTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.7f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7685,7 +7948,7 @@
WindowWithBlockUntrustedOcclusionMode_DoesNotReceiveTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7694,7 +7957,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithAllowOcclusionMode_AllowsTouch) {
const sp<FakeWindowHandle>& w = getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::ALLOW);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7705,7 +7968,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
w->setFrame(Rect(0, 0, 50, 50));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch({PointF{100, 100}});
@@ -7715,7 +7978,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowFromSameUid_AllowsTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(TOUCHED_APP_UID, "A", TouchOcclusionMode::BLOCK_UNTRUSTED);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7725,7 +7988,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacity_AllowsTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7735,7 +7998,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacity_DoesNotReceiveTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7752,7 +8015,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
w->setWatchOutsideTouch(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7763,7 +8026,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
w->setWatchOutsideTouch(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7774,7 +8037,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7785,7 +8048,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
MAXIMUM_OBSCURING_OPACITY);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7796,7 +8059,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7811,7 +8074,8 @@
const sp<FakeWindowHandle>& w2 =
getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*w1->getInfo(), *w2->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7826,7 +8090,8 @@
const sp<FakeWindowHandle>& w2 =
getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
OPACITY_FAR_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*w1->getInfo(), *w2->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7841,7 +8106,8 @@
const sp<FakeWindowHandle>& wC =
getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wB->getInfo(), *wC->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7855,7 +8121,8 @@
const sp<FakeWindowHandle>& wC =
getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wB->getInfo(), *wC->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7870,7 +8137,8 @@
const sp<FakeWindowHandle>& wB =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wA->getInfo(), *wB->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7885,7 +8153,8 @@
const sp<FakeWindowHandle>& wB =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wA->getInfo(), *wB->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7896,7 +8165,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7906,7 +8175,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, SelfWindowWithBlockUntrustedMode_AllowsTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::BLOCK_UNTRUSTED);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7918,7 +8187,7 @@
mDispatcher->setMaximumObscuringOpacityForTouch(0.0f);
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.1f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7929,7 +8198,7 @@
mDispatcher->setMaximumObscuringOpacityForTouch(0.0f);
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.0f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7942,7 +8211,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7957,7 +8226,8 @@
const sp<FakeWindowHandle>& w2 =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*w1->getInfo(), *w2->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7977,7 +8247,8 @@
const sp<FakeWindowHandle>& wC =
getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wB->getInfo(), *wC->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7993,7 +8264,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
w->setApplicationToken(mTouchWindow->getApplicationToken());
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8027,40 +8298,44 @@
mSpyWindow->setFrame(Rect(0, 0, 200, 100));
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mSpyWindow, mWindow, mSecondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()},
+ {},
+ 0,
+ 0});
}
void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
switch (fromSource) {
case AINPUT_SOURCE_TOUCHSCREEN:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
break;
case AINPUT_SOURCE_STYLUS:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(
- mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_STYLUS)
- .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
- .build()));
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_STYLUS)
+ .buttonState(
+ AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::STYLUS)
+ .x(50)
+ .y(50))
+ .build()));
break;
case AINPUT_SOURCE_MOUSE:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(
- mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
- .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
- .x(50)
- .y(50))
- .build()));
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ ToolType::MOUSE)
+ .x(50)
+ .y(50))
+ .build()));
break;
default:
FAIL() << "Source " << fromSource << " doesn't support drag and drop";
@@ -8084,8 +8359,11 @@
mDragWindow =
sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}});
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {mDragWindow, mSpyWindow, mWindow, mSecondWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mDragWindow->getInfo(), *mSpyWindow->getInfo(),
+ *mWindow->getInfo(), *mSecondWindow->getInfo()},
+ {},
+ 0,
+ 0});
// Transfer touch focus to the drag window
bool transferred =
@@ -8104,7 +8382,7 @@
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8113,7 +8391,7 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8122,7 +8400,7 @@
// Move back to original window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8130,7 +8408,8 @@
mSecondWindow->consumeDragEvent(true, -50, 50);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
mWindow->assertNoEvents();
@@ -8150,7 +8429,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -8166,7 +8445,7 @@
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8175,7 +8454,7 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8184,11 +8463,11 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -8198,7 +8477,7 @@
// Move on window and keep button pressed.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
@@ -8210,7 +8489,7 @@
// Move to another window and release button, expect to drop item.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
@@ -8219,11 +8498,11 @@
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
- mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
// nothing to the window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
@@ -8239,11 +8518,12 @@
// Set second window invisible.
mSecondWindow->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mDragWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8252,7 +8532,7 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8261,11 +8541,11 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(nullptr);
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, nullptr);
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -8275,7 +8555,7 @@
mWindow->setPreventSplitting(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -8288,7 +8568,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(75).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionPointerDown(/*pointerIndex=*/1);
@@ -8300,7 +8580,7 @@
TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
// First down on second window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -8315,7 +8595,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -8331,7 +8611,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
mWindow->consumeDragEvent(false, 50, 50);
@@ -8345,10 +8625,10 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(mWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->consumeMotionMove();
}
@@ -8359,11 +8639,16 @@
// Update window of second display.
sp<FakeWindowHandle> windowInSecondary =
sp<FakeWindowHandle>::make(mApp, mDispatcher, "D_2", SECOND_DISPLAY_ID);
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
+ *mSecondWindow->getInfo(), *windowInSecondary->getInfo()},
+ {},
+ 0,
+ 0});
// Let second display has a touch state.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.displayId(SECOND_DISPLAY_ID)
@@ -8372,11 +8657,16 @@
windowInSecondary->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
SECOND_DISPLAY_ID, /*expectedFlag=*/0);
// Update window again.
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
+ *mSecondWindow->getInfo(), *windowInSecondary->getInfo()},
+ {},
+ 0,
+ 0});
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8385,7 +8675,7 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8394,11 +8684,11 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -8407,11 +8697,10 @@
startDrag(true, AINPUT_SOURCE_MOUSE);
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(50)
.y(50))
.build()))
@@ -8422,11 +8711,10 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(150)
.y(50))
.build()))
@@ -8437,17 +8725,16 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(150)
.y(50))
.build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -8461,7 +8748,7 @@
window->setDropInput(true);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -8471,11 +8758,13 @@
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT));
window->assertNoEvents();
// With the flag cleared, the window should get input
window->setDropInput(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
@@ -8493,16 +8782,17 @@
sp<FakeWindowHandle>::make(obscuringApplication, mDispatcher, "obscuringWindow",
ADISPLAY_ID_DEFAULT);
obscuringWindow->setFrame(Rect(0, 0, 50, 50));
- obscuringWindow->setOwnerInfo(111, 111);
+ obscuringWindow->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
obscuringWindow->setTouchable(false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Test window", ADISPLAY_ID_DEFAULT);
window->setDropInputIfObscured(true);
- window->setOwnerInfo(222, 222);
+ window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -8512,11 +8802,14 @@
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT));
window->assertNoEvents();
// With the flag cleared, the window should get input
window->setDropInputIfObscured(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
@@ -8534,16 +8827,17 @@
sp<FakeWindowHandle>::make(obscuringApplication, mDispatcher, "obscuringWindow",
ADISPLAY_ID_DEFAULT);
obscuringWindow->setFrame(Rect(0, 0, 50, 50));
- obscuringWindow->setOwnerInfo(111, 111);
+ obscuringWindow->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
obscuringWindow->setTouchable(false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Test window", ADISPLAY_ID_DEFAULT);
window->setDropInputIfObscured(true);
- window->setOwnerInfo(222, 222);
+ window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -8553,10 +8847,13 @@
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT));
window->assertNoEvents();
// When the window is no longer obscured because it went on top, it should get input
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*window->getInfo(), *obscuringWindow->getInfo()}, {}, 0, 0});
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
@@ -8592,8 +8889,11 @@
mThirdWindow->setFocusable(true);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}},
- {SECOND_DISPLAY_ID, {mThirdWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mWindow->getInfo(), *mSecondWindow->getInfo(), *mThirdWindow->getInfo()},
+ {},
+ 0,
+ 0});
mThirdWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
mWindow->consumeFocusEvent(true);
@@ -8615,7 +8915,7 @@
}
}
- void changeAndVerifyTouchModeInMainDisplayOnly(bool inTouchMode, int32_t pid, int32_t uid,
+ void changeAndVerifyTouchModeInMainDisplayOnly(bool inTouchMode, gui::Pid pid, gui::Uid uid,
bool hasPermission) {
ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission,
ADISPLAY_ID_DEFAULT));
@@ -8634,9 +8934,9 @@
TEST_F(InputDispatcherTouchModeChangedTests, NonFocusedWindowOwnerCannotChangeTouchMode) {
const WindowInfo& windowInfo = *mWindow->getInfo();
- int32_t ownerPid = windowInfo.ownerPid;
- int32_t ownerUid = windowInfo.ownerUid;
- mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
+ gui::Pid ownerPid = windowInfo.ownerPid;
+ gui::Uid ownerUid = windowInfo.ownerUid;
+ mWindow->setOwnerInfo(gui::Pid::INVALID, gui::Uid::INVALID);
ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, ownerPid,
ownerUid, /*hasPermission=*/false,
ADISPLAY_ID_DEFAULT));
@@ -8646,9 +8946,9 @@
TEST_F(InputDispatcherTouchModeChangedTests, NonWindowOwnerMayChangeTouchModeOnPermissionGranted) {
const WindowInfo& windowInfo = *mWindow->getInfo();
- int32_t ownerPid = windowInfo.ownerPid;
- int32_t ownerUid = windowInfo.ownerUid;
- mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
+ gui::Pid ownerPid = windowInfo.ownerPid;
+ gui::Uid ownerUid = windowInfo.ownerUid;
+ mWindow->setOwnerInfo(gui::Pid::INVALID, gui::Uid::INVALID);
changeAndVerifyTouchModeInMainDisplayOnly(!InputDispatcher::kDefaultInTouchMode, ownerPid,
ownerUid, /*hasPermission=*/true);
}
@@ -8674,13 +8974,14 @@
TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInteractedWindow) {
// Interact with the window first.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
// Then remove focus.
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
// Assert that caller can switch touch mode by owning one of the last interacted window.
const WindowInfo& windowInfo = *mWindow->getInfo();
@@ -8722,11 +9023,12 @@
* Adding a spy window that is not a trusted overlay causes Dispatcher to abort.
*/
TEST_F(InputDispatcherSpyWindowDeathTest, UntrustedSpy_AbortsDispatcher) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
ScopedSilentDeath _silentDeath;
auto spy = createSpy();
spy->setTrustedOverlay(false);
- ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}),
+ ASSERT_DEATH(mDispatcher->onWindowInfosChanged({{*spy->getInfo()}, {}, 0, 0}),
".* not a trusted overlay");
}
@@ -8735,10 +9037,10 @@
*/
TEST_F(InputDispatcherSpyWindowTest, NoForegroundWindow) {
auto spy = createSpy();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
@@ -8760,7 +9062,8 @@
auto spy1 = createSpy();
auto spy2 = createSpy();
auto spy3 = createSpy();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window, spy3}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*spy1->getInfo(), *spy2->getInfo(), *window->getInfo(), *spy3->getInfo()}, {}, 0, 0});
const std::vector<sp<FakeWindowHandle>> channels{spy1, spy2, window, spy3};
const size_t numChannels = channels.size();
@@ -8777,7 +9080,7 @@
}
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
std::vector<size_t> eventOrder;
@@ -8812,10 +9115,10 @@
auto window = createForeground();
auto spy = createSpy();
spy->setTouchable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
spy->assertNoEvents();
@@ -8830,23 +9133,23 @@
auto window = createForeground();
auto spy = createSpy();
spy->setTouchableRegion(Region{{0, 0, 20, 20}});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// Inject an event outside the spy window's touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy->assertNoEvents();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionUp();
spy->assertNoEvents();
// Inject an event inside the spy window's touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{5, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -8859,16 +9162,16 @@
*/
TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) {
auto window = createForeground();
- window->setOwnerInfo(12, 34);
+ window->setOwnerInfo(gui::Pid{12}, gui::Uid{34});
auto spy = createSpy();
spy->setWatchOutsideTouch(true);
- spy->setOwnerInfo(56, 78);
+ spy->setOwnerInfo(gui::Pid{56}, gui::Uid{78});
spy->setFrame(Rect{0, 0, 20, 20});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// Inject an event outside the spy window's frame and touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -8886,10 +9189,11 @@
windowRight->setFrame({100, 0, 200, 200});
auto spy = createSpy();
spy->setFrame({0, 0, 200, 200});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, windowLeft, windowRight}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowLeft->consumeMotionDown();
@@ -8902,7 +9206,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowRight->consumeMotionDown();
@@ -8918,10 +9222,10 @@
window->setFrame({0, 0, 200, 200});
auto spyRight = createSpy();
spyRight->setFrame({100, 0, 200, 200});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyRight, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyRight->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -8934,7 +9238,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionPointerDown(/*pointerIndex=*/1);
@@ -8954,11 +9258,11 @@
auto window = createForeground();
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down, no window touched.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -8973,7 +9277,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -8990,15 +9294,15 @@
spy->setFocusable(false);
auto window = createForeground();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
window->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
window->consumeKeyUp(ADISPLAY_ID_NONE);
@@ -9015,10 +9319,11 @@
auto window = createForeground();
auto spy1 = createSpy();
auto spy2 = createSpy();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*spy1->getInfo(), *spy2->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy1->consumeMotionDown();
@@ -9032,7 +9337,7 @@
// The rest of the gesture should only be sent to the second spy window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy2->consumeMotionMove();
@@ -9047,10 +9352,10 @@
TEST_F(InputDispatcherPilferPointersTest, CanPilferAfterWindowIsRemovedMidStream) {
auto window = createForeground();
auto spy = createSpy();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -9060,7 +9365,7 @@
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
@@ -9073,11 +9378,11 @@
auto spy = createSpy();
auto window = createForeground();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down on the window and the spy.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown();
@@ -9096,7 +9401,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -9112,7 +9417,7 @@
.pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(-5).y(-5))
.build();
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
@@ -9129,11 +9434,11 @@
auto window = createForeground();
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down on the window only
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 150}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9147,7 +9452,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(10))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown();
@@ -9163,7 +9468,7 @@
.pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionPointerDown(1);
@@ -9171,8 +9476,8 @@
// Spy window pilfers the pointers.
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
- window->consumeMotionPointerUp(/* idx */ 2, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
- window->consumeMotionPointerUp(/* idx */ 1, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+ window->consumeMotionPointerUp(/*idx=*/2, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+ window->consumeMotionPointerUp(/*idx=*/1, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
spy->assertNoEvents();
window->assertNoEvents();
@@ -9189,11 +9494,11 @@
auto window = createForeground();
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down on both spy and window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{10, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9208,7 +9513,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionPointerDown(1);
@@ -9232,11 +9537,11 @@
auto window = createForeground();
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down on both window and spy
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{10, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9255,7 +9560,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9275,7 +9580,7 @@
sp<FakeWindowHandle>::make(overlayApplication, mDispatcher,
"Stylus interceptor window", ADISPLAY_ID_DEFAULT);
overlay->setFocusable(false);
- overlay->setOwnerInfo(111, 111);
+ overlay->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
overlay->setTouchable(false);
overlay->setInterceptsStylus(true);
overlay->setTrustedOverlay(true);
@@ -9286,10 +9591,10 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Application window",
ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- window->setOwnerInfo(222, 222);
+ window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
return {std::move(overlay), std::move(window)};
@@ -9313,18 +9618,20 @@
using InputDispatcherStylusInterceptorDeathTest = InputDispatcherStylusInterceptorTest;
TEST_F(InputDispatcherStylusInterceptorDeathTest, UntrustedOverlay_AbortsDispatcher) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
ScopedSilentDeath _silentDeath;
auto [overlay, window] = setupStylusOverlayScenario();
overlay->setTrustedOverlay(false);
// Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort.
- ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}),
+ ASSERT_DEATH(mDispatcher->onWindowInfosChanged(
+ {{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0}),
".* not a trusted overlay");
}
TEST_F(InputDispatcherStylusInterceptorTest, ConsmesOnlyStylusEvents) {
auto [overlay, window] = setupStylusOverlayScenario();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
overlay->consumeMotionDown();
@@ -9343,7 +9650,7 @@
TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) {
auto [overlay, window] = setupStylusOverlayScenario();
overlay->setSpy(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
overlay->consumeMotionDown();
@@ -9372,7 +9679,7 @@
TEST_F(InputDispatcherStylusInterceptorTest, StylusHandwritingScenario) {
auto [overlay, window] = setupStylusOverlayScenario();
overlay->setSpy(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
overlay->consumeMotionDown();
@@ -9384,7 +9691,7 @@
// The interceptor configures itself so that it is no longer a spy.
overlay->setSpy(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
// It continues to receive the rest of the stylus gesture.
sendStylusEvent(AMOTION_EVENT_ACTION_MOVE);
@@ -9396,16 +9703,16 @@
}
struct User {
- int32_t mPid;
- int32_t mUid;
+ gui::Pid mPid;
+ gui::Uid mUid;
uint32_t mPolicyFlags{DEFAULT_POLICY_FLAGS};
std::unique_ptr<InputDispatcher>& mDispatcher;
- User(std::unique_ptr<InputDispatcher>& dispatcher, int32_t pid, int32_t uid)
+ User(std::unique_ptr<InputDispatcher>& dispatcher, gui::Pid pid, gui::Uid uid)
: mPid(pid), mUid(uid), mDispatcher(dispatcher) {}
InputEventInjectionResult injectTargetedMotion(int32_t action) const {
- return injectMotionEvent(mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
+ return injectMotionEvent(*mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {100, 200},
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
@@ -9414,7 +9721,7 @@
}
InputEventInjectionResult injectTargetedKey(int32_t action) const {
- return inputdispatcher::injectKey(mDispatcher, action, /*repeatCount=*/0, ADISPLAY_ID_NONE,
+ return inputdispatcher::injectKey(*mDispatcher, action, /*repeatCount=*/0, ADISPLAY_ID_NONE,
InputEventInjectionSync::WAIT_FOR_RESULT,
INJECT_EVENT_TIMEOUT, /*allowKeyRepeat=*/false, {mUid},
mPolicyFlags);
@@ -9434,9 +9741,9 @@
using InputDispatcherTargetedInjectionTest = InputDispatcherTest;
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedWindow) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
@@ -9451,11 +9758,11 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedWindow) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
- auto rando = User(mDispatcher, 20, 21);
+ auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
EXPECT_EQ(InputEventInjectionResult::TARGET_MISMATCH,
rando.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
@@ -9468,12 +9775,12 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedSpyWindow) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
auto spy = owner.createWindow();
spy->setSpy(true);
spy->setTrustedOverlay(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
@@ -9482,14 +9789,14 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedSpyWindow) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- auto rando = User(mDispatcher, 20, 21);
+ auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
auto randosSpy = rando.createWindow();
randosSpy->setSpy(true);
randosSpy->setTrustedOverlay(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosSpy, window}}});
+ mDispatcher->onWindowInfosChanged({{*randosSpy->getInfo(), *window->getInfo()}, {}, 0, 0});
// The event is targeted at owner's window, so injection should succeed, but the spy should
// not receive the event.
@@ -9500,18 +9807,18 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoAnyWindowWhenNotTargeting) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- auto rando = User(mDispatcher, 20, 21);
+ auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
auto randosSpy = rando.createWindow();
randosSpy->setSpy(true);
randosSpy->setTrustedOverlay(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosSpy, window}}});
+ mDispatcher->onWindowInfosChanged({{*randosSpy->getInfo(), *window->getInfo()}, {}, 0, 0});
// A user that has injection permission can inject into any window.
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT));
randosSpy->consumeMotionDown();
window->consumeMotionDown();
@@ -9519,26 +9826,26 @@
setFocusedWindow(randosSpy);
randosSpy->consumeFocusEvent(true);
- EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher));
randosSpy->consumeKeyDown(ADISPLAY_ID_NONE);
window->assertNoEvents();
}
-TEST_F(InputDispatcherTargetedInjectionTest, CanGenerateActionOutsideToOtherUids) {
- auto owner = User(mDispatcher, 10, 11);
+TEST_F(InputDispatcherTargetedInjectionTest, CannotGenerateActionOutsideToOtherUids) {
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- auto rando = User(mDispatcher, 20, 21);
+ auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
auto randosWindow = rando.createWindow();
randosWindow->setFrame(Rect{-10, -10, -5, -5});
randosWindow->setWatchOutsideTouch(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*randosWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
- // We allow generation of ACTION_OUTSIDE events into windows owned by different uids.
+ // Do not allow generation of ACTION_OUTSIDE events into windows owned by different uids.
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
window->consumeMotionDown();
- randosWindow->consumeMotionOutside();
+ randosWindow->assertNoEvents();
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bbb166c..477beaf 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -161,6 +161,7 @@
// fake mapping which would normally come from keyCharacterMap
std::unordered_map<int32_t, int32_t> mKeyCodeMapping;
std::vector<int32_t> mSupportedKeyCodes;
+ std::list<NotifyArgs> mProcessResult;
std::mutex mLock;
std::condition_variable mStateChangedCondition;
@@ -191,6 +192,14 @@
mMetaState = metaState;
}
+ // Sets the return value for the `process` call.
+ void setProcessResult(std::list<NotifyArgs> notifyArgs) {
+ mProcessResult.clear();
+ for (auto notifyArg : notifyArgs) {
+ mProcessResult.push_back(notifyArg);
+ }
+ }
+
void assertConfigureWasCalled() {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
@@ -291,7 +300,7 @@
mLastEvent = *rawEvent;
mProcessWasCalled = true;
mStateChangedCondition.notify_all();
- return {};
+ return mProcessResult;
}
int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
@@ -1534,8 +1543,8 @@
NotifyMotionArgs args;
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
EXPECT_EQ(action, args.action);
- ASSERT_EQ(points.size(), args.pointerCount);
- for (size_t i = 0; i < args.pointerCount; i++) {
+ ASSERT_EQ(points.size(), args.getPointerCount());
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
EXPECT_EQ(points[i].x, args.pointerCoords[i].getX());
EXPECT_EQ(points[i].y, args.pointerCoords[i].getY());
}
@@ -1877,7 +1886,7 @@
::testing::Types<UinputTouchScreen, UinputExternalStylus, UinputExternalStylusWithPressure>;
TYPED_TEST_SUITE(StylusButtonIntegrationTest, StylusButtonIntegrationTestTypes);
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsGenerateKeyEvents) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsGenerateKeyEvents) {
const auto stylusId = TestFixture::mStylusInfo.getId();
TestFixture::mStylus->pressKey(BTN_STYLUS);
@@ -1891,7 +1900,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsSurroundingTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -1937,7 +1946,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsSurroundingHoveringTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingHoveringTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -2013,7 +2022,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsWithinTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -2067,7 +2076,7 @@
WithDeviceId(touchscreenId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonMotionEventsDisabled) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) {
TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false);
TestFixture::mReader->requestRefreshConfiguration(
InputReaderConfiguration::Change::STYLUS_BUTTON_REPORTING);
@@ -2124,7 +2133,7 @@
// ongoing stylus gesture that is being emitted by the touchscreen.
using ExternalStylusIntegrationTest = TouchIntegrationTest;
-TEST_F(ExternalStylusIntegrationTest, DISABLED_FusedExternalStylusPressureReported) {
+TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus capable of reporting pressure data that
@@ -2170,7 +2179,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
}
-TEST_F(ExternalStylusIntegrationTest, DISABLED_FusedExternalStylusPressureNotReported) {
+TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus capable of reporting pressure data that
@@ -2238,7 +2247,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
}
-TEST_F(ExternalStylusIntegrationTest, DISABLED_UnfusedExternalStylus) {
+TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus device that does not support pressure. It should not affect any
@@ -2475,6 +2484,73 @@
ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
}
+TEST_F(InputDeviceTest, WakeDevice_AddsWakeFlagToProcessNotifyArgs) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1");
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ NotifyMotionArgs args1;
+ NotifySwitchArgs args2;
+ NotifyKeyArgs args3;
+ mapper.setProcessResult({args1, args2, args3});
+
+ InputReaderConfiguration config;
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+ RawEvent event;
+ event.deviceId = EVENTHUB_ID;
+ std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+ for (auto& arg : notifyArgs) {
+ if (const auto notifyMotionArgs = std::get_if<NotifyMotionArgs>(&arg)) {
+ ASSERT_EQ(POLICY_FLAG_WAKE, notifyMotionArgs->policyFlags);
+ } else if (const auto notifySwitchArgs = std::get_if<NotifySwitchArgs>(&arg)) {
+ ASSERT_EQ(POLICY_FLAG_WAKE, notifySwitchArgs->policyFlags);
+ } else if (const auto notifyKeyArgs = std::get_if<NotifyKeyArgs>(&arg)) {
+ ASSERT_EQ(POLICY_FLAG_WAKE, notifyKeyArgs->policyFlags);
+ }
+ }
+}
+
+TEST_F(InputDeviceTest, NotWakeDevice_DoesNotAddWakeFlagToProcessNotifyArgs) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "0");
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ NotifyMotionArgs args;
+ mapper.setProcessResult({args});
+
+ InputReaderConfiguration config;
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+ RawEvent event;
+ event.deviceId = EVENTHUB_ID;
+ std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+ // POLICY_FLAG_WAKE is not added to the NotifyArgs.
+ ASSERT_EQ(0u, std::get<NotifyMotionArgs>(notifyArgs.front()).policyFlags);
+}
+
+TEST_F(InputDeviceTest, NotWakeDevice_DoesNotRemoveExistingWakeFlagFromProcessNotifyArgs) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "0");
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ NotifyMotionArgs args;
+ args.policyFlags = POLICY_FLAG_WAKE;
+ mapper.setProcessResult({args});
+
+ InputReaderConfiguration config;
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+ RawEvent event;
+ event.deviceId = EVENTHUB_ID;
+ std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+ // The POLICY_FLAG_WAKE is preserved, despite the device being a non-wake device.
+ ASSERT_EQ(POLICY_FLAG_WAKE, std::get<NotifyMotionArgs>(notifyArgs.front()).policyFlags);
+}
+
// A single input device is associated with a specific display. Check that:
// 1. Device is disabled if the viewport corresponding to the associated display is not found
// 2. Device is disabled when setEnabled API is called
@@ -3962,7 +4038,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(uint32_t(1), args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
@@ -3980,7 +4056,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(uint32_t(1), args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
@@ -4001,7 +4077,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(0, args.buttonState);
ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(uint32_t(1), args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
@@ -4019,7 +4095,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(0, args.buttonState);
ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(uint32_t(1), args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
@@ -5282,7 +5358,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5306,7 +5382,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5329,7 +5405,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5379,7 +5455,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5402,7 +5478,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5447,7 +5523,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5474,7 +5550,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5499,7 +5575,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5542,7 +5618,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5567,7 +5643,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5590,7 +5666,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5916,6 +5992,40 @@
mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
}
+TEST_F(SingleTouchInputMapperTest, Process_DoesntCheckPhysicalFrameForTouchpads) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(fakePointerController);
+
+ addConfigurationProperty("touch.deviceType", "pointer");
+ prepareAxes(POSITION);
+ prepareDisplay(ui::ROTATION_0);
+ auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
+
+ // Set a physical frame in the display viewport.
+ auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ viewport->physicalLeft = 20;
+ viewport->physicalTop = 600;
+ viewport->physicalRight = 30;
+ viewport->physicalBottom = 610;
+ mFakePolicy->updateViewport(*viewport);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // Start the touch.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
+ processSync(mapper);
+
+ // Expect all input starting outside the physical frame to result in NotifyMotionArgs being
+ // produced.
+ const std::array<Point, 6> outsidePoints = {
+ {{0, 0}, {19, 605}, {31, 605}, {25, 599}, {25, 611}, {DISPLAY_WIDTH, DISPLAY_HEIGHT}}};
+ for (const auto& p : outsidePoints) {
+ processMove(mapper, toRawX(p.x), toRawY(p.y));
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+ }
+}
+
TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
@@ -6911,7 +7021,7 @@
NotifyMotionArgs motionArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], point.x, point.y,
1, 0, 0, 0, 0, 0, 0, 0));
}
@@ -6983,7 +7093,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_NO_FATAL_FAILURE(
assertPointerCoords(motionArgs.pointerCoords[0], 11, 21, 1, 0, 0, 0, 0, 0, 0, 0));
@@ -7771,7 +7881,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -7790,7 +7900,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7821,7 +7931,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7850,7 +7960,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7873,7 +7983,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -7898,7 +8008,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -7925,7 +8035,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7954,7 +8064,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7977,7 +8087,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8000,7 +8110,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8088,7 +8198,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8096,7 +8206,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8118,7 +8228,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8137,7 +8247,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8149,7 +8259,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8164,7 +8274,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8182,7 +8292,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_0_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8201,7 +8311,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8213,7 +8323,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8225,7 +8335,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8258,7 +8368,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8266,7 +8376,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8286,7 +8396,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8306,7 +8416,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8318,7 +8428,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8331,7 +8441,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8347,7 +8457,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_0_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8367,7 +8477,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8379,7 +8489,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8391,7 +8501,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8532,7 +8642,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
- ASSERT_EQ(size_t(2), args.pointerCount);
+ ASSERT_EQ(size_t(2), args.getPointerCount());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
x, y, 1.0f, size, touch, touch, tool, tool, 0, 0));
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[1],
@@ -9352,6 +9462,11 @@
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
}
+/**
+ * When the viewport is deactivated (isActive transitions from true to false),
+ * and touch.enableForInactiveViewport is false, touches prior to the transition
+ * should be cancelled.
+ */
TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "0");
@@ -9403,6 +9518,60 @@
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
}
+/**
+ * When the viewport is deactivated (isActive transitions from true to false),
+ * and touch.enableForInactiveViewport is true, touches prior to the transition
+ * should not be cancelled.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_TouchesNotAborted) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addConfigurationProperty("touch.enableForInactiveViewport", "1");
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ std::optional<DisplayViewport> optionalDisplayViewport =
+ mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
+ ASSERT_TRUE(optionalDisplayViewport.has_value());
+ DisplayViewport displayViewport = *optionalDisplayViewport;
+
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
+
+ // Finger down
+ int32_t x = 100, y = 100;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Deactivate display viewport
+ displayViewport.isActive = false;
+ ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // The ongoing touch should not be canceled
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Finger move is not ignored
+ x += 10, y += 10;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+ // Reactivate display viewport
+ displayViewport.isActive = true;
+ ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // Finger move continues and does not start new gesture
+ x += 10, y += 10;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+}
+
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
// Setup the first touch screen device.
prepareAxes(POSITION | ID | SLOT);
@@ -9791,7 +9960,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// First finger up. It used to be in palm mode, and we already generated ACTION_POINTER_UP for
// it. Second finger receive move.
@@ -9800,7 +9969,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// Second finger keeps moving.
processSlot(mapper, SECOND_SLOT);
@@ -9809,7 +9978,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// Second finger up.
processId(mapper, INVALID_TRACKING_ID);
@@ -9883,7 +10052,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// third finger move
processId(mapper, THIRD_TRACKING_ID);
@@ -9898,7 +10067,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// second finger up, third finger receive move.
processSlot(mapper, SECOND_SLOT);
@@ -9906,7 +10075,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// third finger up.
processSlot(mapper, THIRD_SLOT);
@@ -9963,7 +10132,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// second finger up.
processSlot(mapper, SECOND_SLOT);
@@ -10009,7 +10178,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// First finger move.
processId(mapper, FIRST_TRACKING_ID);
@@ -10018,7 +10187,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// Second finger down.
processSlot(mapper, SECOND_SLOT);
@@ -10028,7 +10197,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
- ASSERT_EQ(uint32_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(2), motionArgs.getPointerCount());
// second finger up with some unexpected data.
processSlot(mapper, SECOND_SLOT);
@@ -10037,7 +10206,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
- ASSERT_EQ(uint32_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(2), motionArgs.getPointerCount());
// first finger up with some unexpected data.
processSlot(mapper, FIRST_SLOT);
@@ -10047,7 +10216,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
}
TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
@@ -10302,7 +10471,7 @@
NotifyMotionArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(1U, args.pointerCount);
+ ASSERT_EQ(1U, args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, args.source);
ASSERT_NO_FATAL_FAILURE(
@@ -10317,7 +10486,7 @@
// expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
- ASSERT_EQ(2U, args.pointerCount);
+ ASSERT_EQ(2U, args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(1, args.pointerProperties[1].id);
ASSERT_NO_FATAL_FAILURE(
@@ -10385,7 +10554,7 @@
// expect coord[0] to contain new location of touch 1, and properties[0].id to contain 1
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_EQ(1U, args.pointerCount);
+ ASSERT_EQ(1U, args.getPointerCount());
ASSERT_EQ(1, args.pointerProperties[0].id);
ASSERT_NO_FATAL_FAILURE(
assertPointerCoords(args.pointerCoords[0], 320, 900, 1, 0, 0, 0, 0, 0, 0, 0));
@@ -10605,7 +10774,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10627,7 +10796,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
@@ -10665,7 +10834,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10687,7 +10856,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
@@ -10721,7 +10890,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10746,16 +10915,16 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
// The previous PRESS gesture is cancelled, because it is transformed to freeform
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
- ASSERT_EQ(2U, motionArgs.pointerCount);
+ ASSERT_EQ(2U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN, motionArgs.action & AMOTION_EVENT_ACTION_MASK);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10785,7 +10954,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(2U, motionArgs.pointerCount);
+ ASSERT_EQ(2U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10814,7 +10983,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET));
@@ -10836,7 +11005,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
ASSERT_LT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET), 0);
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index 7f8d556..fef58ec 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -103,12 +103,16 @@
mExternalStylusDevices = devices;
}
+ void setPreventingTouchpadTaps(bool prevent) override { mPreventingTouchpadTaps = prevent; }
+ bool isPreventingTouchpadTaps() override { return mPreventingTouchpadTaps; }
+
private:
int32_t mGlobalMetaState;
bool mUpdateGlobalMetaStateWasCalled;
int32_t mGeneration;
std::optional<nsecs_t> mRequestedTimeout;
std::vector<InputDeviceInfo> mExternalStylusDevices;
+ bool mPreventingTouchpadTaps{false};
} mFakeContext;
friend class InputReaderTest;
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index d720a90..b6720c5 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -49,6 +49,9 @@
MOCK_METHOD(void, updateLedMetaState, (int32_t metaState), (override));
MOCK_METHOD(int32_t, getLedMetaState, (), (override));
+
+ MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override));
+ MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override));
};
class MockEventHubInterface : public EventHubInterface {
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
new file mode 100644
index 0000000..08a5559
--- /dev/null
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyboardInputMapper.h"
+
+#include <gtest/gtest.h>
+
+#include "InputMapperTest.h"
+#include "InterfaceMocks.h"
+
+#define TAG "KeyboardInputMapper_test"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+/**
+ * Unit tests for KeyboardInputMapper.
+ */
+class KeyboardInputMapperUnitTest : public InputMapperUnitTest {
+protected:
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ const std::unordered_map<int32_t, int32_t> mKeyCodeMap{{KEY_0, AKEYCODE_0},
+ {KEY_A, AKEYCODE_A},
+ {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT},
+ {KEY_LEFTALT, AKEYCODE_ALT_LEFT},
+ {KEY_RIGHTALT, AKEYCODE_ALT_RIGHT},
+ {KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT},
+ {KEY_RIGHTSHIFT, AKEYCODE_SHIFT_RIGHT},
+ {KEY_FN, AKEYCODE_FUNCTION},
+ {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT},
+ {KEY_RIGHTCTRL, AKEYCODE_CTRL_RIGHT},
+ {KEY_LEFTMETA, AKEYCODE_META_LEFT},
+ {KEY_RIGHTMETA, AKEYCODE_META_RIGHT},
+ {KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK},
+ {KEY_NUMLOCK, AKEYCODE_NUM_LOCK},
+ {KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK}};
+
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+
+ // set key-codes expected in tests
+ for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, scanCode, _, _, _, _, _))
+ .WillRepeatedly(DoAll(SetArgPointee<4>(outKeycode), Return(NO_ERROR)));
+ }
+
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ 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) {
+ EXPECT_CALL(mMockInputReaderContext, fadePointer)
+ .Times(expectVisible ? 0 : keyCodes.size());
+ 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);
+ }
+ }
+
+ void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes,
+ const bool expectPrevent) {
+ EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).Times(keyCodes.size());
+ if (expectPrevent) {
+ EXPECT_CALL(mMockInputReaderContext, setPreventingTouchpadTaps(true))
+ .Times(keyCodes.size());
+ }
+ 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);
+ }
+ }
+};
+
+/**
+ * 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 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) {
+ testTouchpadTapStateForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectPrevent= */ false);
+}
+
+/**
+ * Touchpad tap should be disabled if there is a active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionDisableTouchpadTap) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ testTouchpadTapStateForKeys({KEY_0, KEY_A}, /* expectPrevent= */ true);
+}
+
+/**
+ * Touchpad tap should not be disabled by meta keys even if Input Method Connection is active
+ */
+TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTouchpadTap) {
+ 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};
+ testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/SlopController_test.cpp b/services/inputflinger/tests/SlopController_test.cpp
new file mode 100644
index 0000000..f524acd
--- /dev/null
+++ b/services/inputflinger/tests/SlopController_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../reader/mapper/SlopController.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+// --- SlopControllerTest ---
+
+TEST(SlopControllerTest, PositiveValues) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller.consumeEvent(1000, 1));
+ ASSERT_EQ(0, controller.consumeEvent(1003, 3));
+ ASSERT_EQ(2, controller.consumeEvent(1005, 3));
+ ASSERT_EQ(4, controller.consumeEvent(1009, 4));
+
+ SlopController controller2 = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller2.consumeEvent(1000, 5));
+ ASSERT_EQ(3, controller2.consumeEvent(1003, 3));
+ ASSERT_EQ(4, controller2.consumeEvent(1005, 4));
+}
+
+TEST(SlopControllerTest, NegativeValues) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller.consumeEvent(1000, -1));
+ ASSERT_EQ(0, controller.consumeEvent(1003, -3));
+ ASSERT_EQ(-2, controller.consumeEvent(1005, -3));
+ ASSERT_EQ(-4, controller.consumeEvent(1009, -4));
+
+ SlopController controller2 = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller2.consumeEvent(1000, -5));
+ ASSERT_EQ(-3, controller2.consumeEvent(1003, -3));
+ ASSERT_EQ(-4, controller2.consumeEvent(1005, -4));
+}
+
+TEST(SlopControllerTest, ZeroDoesNotResetSlop) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+ ASSERT_EQ(0, controller.consumeEvent(1006, 0));
+ ASSERT_EQ(2, controller.consumeEvent(1008, 2));
+}
+
+TEST(SlopControllerTest, SignChange_ResetsSlop) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller.consumeEvent(1000, 2));
+ ASSERT_EQ(0, controller.consumeEvent(1001, -4));
+ ASSERT_EQ(0, controller.consumeEvent(1002, 3));
+ ASSERT_EQ(0, controller.consumeEvent(1003, -2));
+
+ ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+ ASSERT_EQ(0, controller.consumeEvent(1006, 0));
+ ASSERT_EQ(2, controller.consumeEvent(1008, 2));
+
+ ASSERT_EQ(0, controller.consumeEvent(1010, -4));
+ ASSERT_EQ(-1, controller.consumeEvent(1011, -2));
+
+ ASSERT_EQ(0, controller.consumeEvent(1015, 5));
+ ASSERT_EQ(2, controller.consumeEvent(1016, 2));
+
+ ASSERT_EQ(0, controller.consumeEvent(1017, -5));
+ ASSERT_EQ(-2, controller.consumeEvent(1018, -2));
+}
+
+TEST(SlopControllerTest, OldAge_ResetsSlop) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+ ASSERT_EQ(0, controller.consumeEvent(1108, 2)); // age exceeds slop duration
+
+ ASSERT_EQ(1, controller.consumeEvent(1110, 4));
+ ASSERT_EQ(0, controller.consumeEvent(1210, 2)); // age equals slop duration
+
+ ASSERT_EQ(0, controller.consumeEvent(1215, -3));
+ ASSERT_EQ(-2, controller.consumeEvent(1216, -4));
+ ASSERT_EQ(-5, controller.consumeEvent(1315, -5));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/SyncQueue_test.cpp b/services/inputflinger/tests/SyncQueue_test.cpp
index af2f961..b57ccc2 100644
--- a/services/inputflinger/tests/SyncQueue_test.cpp
+++ b/services/inputflinger/tests/SyncQueue_test.cpp
@@ -50,6 +50,18 @@
}
}
+// Make sure the queue has strict capacity limits.
+TEST(SyncQueueTest, QueueReachesCapacity) {
+ constexpr size_t capacity = 3;
+ SyncQueue<int> queue(capacity);
+
+ // First 3 elements should be added successfully
+ ASSERT_TRUE(queue.push(1));
+ ASSERT_TRUE(queue.push(2));
+ ASSERT_TRUE(queue.push(3));
+ ASSERT_FALSE(queue.push(4)) << "Queue should reach capacity at size " << capacity;
+}
+
TEST(SyncQueueTest, AllowsMultipleThreads) {
SyncQueue<int> queue;
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.cpp b/services/inputflinger/tests/TestInputListenerMatchers.cpp
new file mode 100644
index 0000000..1464e60
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListenerMatchers.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestInputListenerMatchers.h"
+
+namespace android {
+
+WithKeyActionMatcher WithKeyAction(int32_t action) {
+ return WithKeyActionMatcher(action);
+}
+
+WithMotionActionMatcher WithMotionAction(int32_t action) {
+ return WithMotionActionMatcher(action);
+}
+
+WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
+ return WithDisplayIdMatcher(displayId);
+}
+
+WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
+ return WithDeviceIdMatcher(deviceId);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 01b79ca..020ea86 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -23,56 +23,148 @@
#include <gtest/gtest.h>
#include <input/Input.h>
+#include "NotifyArgs.h"
#include "TestConstants.h"
namespace android {
-MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
- bool matches = action == arg.action;
- if (!matches) {
- *result_listener << "expected action " << MotionEvent::actionToString(action)
- << ", but got " << MotionEvent::actionToString(arg.action);
- }
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
- matches &= (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
- }
- return matches;
-}
-
-MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") {
- *result_listener << "expected action " << KeyEvent::actionToString(action) << ", but got "
- << KeyEvent::actionToString(arg.action);
- return arg.action == action;
-}
-
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
<< inputEventSourceToString(arg.source);
return arg.source == source;
}
-MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
- *result_listener << "expected displayId " << displayId << ", but got " << arg.displayId;
- return arg.displayId == displayId;
-}
+/// Key action
+class WithKeyActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithKeyActionMatcher(int32_t action) : mAction(action) {}
-MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") {
- *result_listener << "expected deviceId " << deviceId << ", but got " << arg.deviceId;
- return arg.deviceId == deviceId;
-}
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mAction == args.action;
+ }
+
+ bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+ return mAction == event.getAction();
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with key action " << KeyEvent::actionToString(mAction);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+WithKeyActionMatcher WithKeyAction(int32_t action);
+
+/// Motion action
+class WithMotionActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ bool matches = mAction == args.action;
+ if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+ bool matches = mAction == event.getAction();
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with motion action " << MotionEvent::actionToString(mAction);
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ *os << " and FLAG_CANCELED";
+ }
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+WithMotionActionMatcher WithMotionAction(int32_t action);
+
+/// Display Id
+class WithDisplayIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDisplayId == args.displayId;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDisplayId == args.displayId;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDisplayId == event.getDisplayId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with display id " << mDisplayId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
+
+private:
+ const int32_t mDisplayId;
+};
+
+WithDisplayIdMatcher WithDisplayId(int32_t displayId);
+
+/// Device Id
+class WithDeviceIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDeviceIdMatcher(int32_t deviceId) : mDeviceId(deviceId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDeviceId == event.getDeviceId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
+
+private:
+ const int32_t mDeviceId;
+};
+
+WithDeviceIdMatcher WithDeviceId(int32_t deviceId);
MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
*result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
return arg.keyCode == keyCode;
}
+MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
+ return arg.getRepeatCount() == repeatCount;
+}
+
MATCHER_P(WithPointerCount, count, "MotionEvent with specified number of pointers") {
- *result_listener << "expected " << count << " pointer(s), but got " << arg.pointerCount;
- return arg.pointerCount == count;
+ *result_listener << "expected " << count << " pointer(s), but got " << arg.getPointerCount();
+ return arg.getPointerCount() == count;
}
MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 92cd462..02abf9f 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -139,7 +139,8 @@
VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)),
VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
// Liftoff
args.clear();
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 1fff2c7..7cfcf71 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -88,14 +88,13 @@
}
// Define a valid motion event.
- NotifyMotionArgs args(/* id */ 0, eventTime, /*readTime=*/0, DEVICE_ID,
- AINPUT_SOURCE_TOUCHSCREEN, /*displayId=*/0, POLICY_FLAG_PASS_TO_USER,
- action, /* actionButton */ 0,
- /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
- pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+ NotifyMotionArgs args(/*id=*/0, eventTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ /*displayId=*/0, POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0,
+ /*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
+ pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /* videoFrames */ {});
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /*videoFrames=*/{});
return args;
}
@@ -104,15 +103,15 @@
InputDeviceIdentifier identifier;
auto info = InputDeviceInfo();
- info.initialize(DEVICE_ID, /*generation*/ 1, /*controllerNumber*/ 1, identifier, "alias",
- /*isExternal*/ false, /*hasMic*/ false, ADISPLAY_ID_NONE);
+ info.initialize(DEVICE_ID, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias",
+ /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
info.addSource(AINPUT_SOURCE_TOUCHSCREEN);
- info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat*/ 0,
- /*fuzz*/ 0, X_RESOLUTION);
- info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat*/ 0,
- /*fuzz*/ 0, Y_RESOLUTION);
+ info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat=*/0,
+ /*fuzz=*/0, X_RESOLUTION);
+ info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat=*/0,
+ /*fuzz=*/0, Y_RESOLUTION);
info.addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN, 0, 255,
- /*flat*/ 0, /*fuzz*/ 0, MAJOR_RESOLUTION);
+ /*flat=*/0, /*fuzz=*/0, MAJOR_RESOLUTION);
return info;
}
@@ -138,9 +137,10 @@
static void assertArgs(const NotifyMotionArgs& args, int32_t action,
const std::vector<std::pair<int32_t /*pointerId*/, PointerData>>& pointers) {
- ASSERT_EQ(action, args.action);
- ASSERT_EQ(pointers.size(), args.pointerCount);
- for (size_t i = 0; i < args.pointerCount; i++) {
+ ASSERT_EQ(action, args.action)
+ << "Expected " << MotionEvent::actionToString(action) << " but got " << args.action;
+ ASSERT_EQ(pointers.size(), args.getPointerCount());
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
const auto& [pointerId, pointerData] = pointers[i];
ASSERT_EQ(pointerId, args.pointerProperties[i].id);
ASSERT_EQ(pointerData.x, args.pointerCoords[i].getX());
@@ -151,7 +151,7 @@
}
TEST(RemovePointerIdsTest, RemoveOnePointer) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0,
AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
NotifyMotionArgs pointer1Only = removePointerIds(args, {0});
@@ -166,7 +166,7 @@
*/
TEST(RemovePointerIdsTest, RemoveTwoPointers) {
NotifyMotionArgs args =
- generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, AMOTION_EVENT_ACTION_MOVE,
+ generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, AMOTION_EVENT_ACTION_MOVE,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
NotifyMotionArgs pointer1Only = removePointerIds(args, {0, 2});
@@ -178,7 +178,7 @@
* pointer during a POINTER_DOWN event.
*/
TEST(RemovePointerIdsTest, ActionPointerDown) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
NotifyMotionArgs pointers0And2 = removePointerIds(args, {1});
@@ -192,11 +192,11 @@
* Remove all pointers during a MOVE event.
*/
TEST(RemovePointerIdsTest, RemoveAllPointersDuringMove) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0,
AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
NotifyMotionArgs noPointers = removePointerIds(args, {0, 1});
- ASSERT_EQ(0u, noPointers.pointerCount);
+ ASSERT_EQ(0u, noPointers.getPointerCount());
}
/**
@@ -204,7 +204,7 @@
* then we should just have ACTION_DOWN. Likewise, a POINTER_UP event should become an UP event.
*/
TEST(RemovePointerIdsTest, PointerDownBecomesDown) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
NotifyMotionArgs pointer1 = removePointerIds(args, {0, 2});
@@ -219,11 +219,11 @@
* If a pointer that is now going down is canceled, then we can just drop the POINTER_DOWN event.
*/
TEST(CancelSuppressedPointersTest, CanceledPointerDownIsDropped) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_TRUE(result.empty());
}
@@ -231,11 +231,11 @@
* If a pointer is already suppressed, the POINTER_UP event for this pointer should be dropped
*/
TEST(CancelSuppressedPointersTest, SuppressedPointerUpIsDropped) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_TRUE(result.empty());
}
@@ -243,11 +243,11 @@
* If a pointer is already suppressed, it should be removed from a MOVE event.
*/
TEST(CancelSuppressedPointersTest, SuppressedPointerIsRemovedDuringMove) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
}
@@ -258,11 +258,11 @@
* 2) A MOVE event without this pointer
*/
TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_EQ(2u, result.size());
assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -274,10 +274,10 @@
* should be canceled with ACTION_CANCEL.
*/
TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}});
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE, {{1, 2, 3}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {0});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{0});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -288,11 +288,11 @@
* but this event should also have FLAG_CANCELED to indicate that this pointer was unintentional.
*/
TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -303,11 +303,11 @@
* errors with handling pointer index inside the action.
*/
TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_0_UP,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_0_UP,
{{1, 2, 3}, {4, 5, 6}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {0});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{0});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -319,10 +319,10 @@
*/
TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) {
NotifyMotionArgs args =
- generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}, {4, 5, 6}});
+ generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE, {{1, 2, 3}, {4, 5, 6}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {0, 1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -334,11 +334,11 @@
* would undo the entire gesture.
*/
TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
{{1, 2, 3}, {4, 5, 6}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
- /*newSuppressedPointerIds*/ {0, 1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+ /*newSuppressedPointerIds=*/{0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -349,11 +349,11 @@
* this should become a regular DOWN event because it's the only pointer that will be valid now.
*/
TEST(CancelSuppressedPointersTest, NewPointerDownBecomesDown) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_2_DOWN,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_2_DOWN,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {0, 1},
- /*newSuppressedPointerIds*/ {0, 1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{0, 1},
+ /*newSuppressedPointerIds=*/{0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], DOWN, {{2, {7, 8, 9}}});
ASSERT_EQ(0, result[0].flags);
@@ -364,7 +364,7 @@
* struct is populated as expected.
*/
TEST(GetTouchesTest, ConvertDownEvent) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, DOWN, {{1, 2, 3}});
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
AndroidPalmFilterDeviceInfo deviceInfo = generatePalmFilterDeviceInfo();
SlotState slotState;
SlotState oldSlotState = slotState;
@@ -771,7 +771,7 @@
ASSERT_EQ(POINTER_0_UP, argsList[0].action);
ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
ASSERT_EQ(MOVE, argsList[1].action);
- ASSERT_EQ(1u, argsList[1].pointerCount);
+ ASSERT_EQ(1u, argsList[1].getPointerCount());
ASSERT_EQ(0, argsList[1].flags);
mPalmRejector->processMotion(
@@ -958,7 +958,7 @@
{{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(MOVE, argsList[0].action);
- ASSERT_EQ(1u, argsList[0].pointerCount);
+ ASSERT_EQ(1u, argsList[0].getPointerCount());
ASSERT_EQ(1433, argsList[0].pointerCoords[0].getX());
ASSERT_EQ(751, argsList[0].pointerCoords[0].getY());
}
@@ -986,7 +986,7 @@
ASSERT_EQ(1u, argsList.size());
// Cancel all
ASSERT_EQ(CANCEL, argsList[0].action);
- ASSERT_EQ(2u, argsList[0].pointerCount);
+ ASSERT_EQ(2u, argsList[0].getPointerCount());
ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
// Future move events are ignored
@@ -1001,7 +1001,7 @@
{{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}, {1000, 700, 10}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(DOWN, argsList[0].action);
- ASSERT_EQ(1u, argsList[0].pointerCount);
+ ASSERT_EQ(1u, argsList[0].getPointerCount());
ASSERT_EQ(2, argsList[0].pointerProperties[0].id);
}
@@ -1023,7 +1023,7 @@
mPalmRejector->processMotion(
generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_DOWN,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
// Suppress both pointers!!
suppressPointerAtPosition(1414, 702);
@@ -1057,13 +1057,13 @@
mPalmRejector->processMotion(
generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_DOWN,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
// Suppress second pointer (pointer 1)
suppressPointerAtPosition(1060, 700);
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, MOVE,
+ generateMotionArgs(downTime, /*eventTime=*/1, MOVE,
{{1417.0, 685.0, 41.0}, {1060, 700, 10.0}}));
ASSERT_EQ(2u, argsList.size());
ASSERT_EQ(POINTER_1_UP, argsList[0].action);
@@ -1075,20 +1075,20 @@
// A new pointer goes down and gets suppressed right away. It should just be dropped
suppressPointerAtPosition(1001, 601);
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_DOWN,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_2_DOWN,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}}));
ASSERT_EQ(0u, argsList.size());
// Likewise, pointer that's already canceled should be ignored
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_UP,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_2_UP,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}}));
ASSERT_EQ(0u, argsList.size());
// Cancel all pointers when pointer 1 goes up. Pointer 1 was already canceled earlier.
suppressPointerAtPosition(1417, 685);
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_UP,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_UP,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(CANCEL, argsList[0].action);
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 55c2db6..d7980f5 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -21,52 +21,35 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_fuzz {
- name: "inputflinger_latencytracker_fuzzer",
- defaults: [
- "inputflinger_defaults",
- ],
- include_dirs: [
- "frameworks/native/services/inputflinger",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "liblog",
- "libutils",
- "libinput",
- "libinputflinger",
- ],
- srcs: [
- "LatencyTrackerFuzzer.cpp",
- ],
- fuzz_config: {
- cc: ["android-framework-input@google.com"],
- },
-}
-
cc_defaults {
name: "inputflinger_fuzz_defaults",
defaults: [
"inputflinger_defaults",
+ "libinputflinger_defaults",
],
+ host_supported: true,
include_dirs: [
"frameworks/native/services/inputflinger",
],
shared_libs: [
- "android.hardware.input.classifier@1.0",
- "android.hardware.input.processor-V1-ndk",
- "libbase",
- "libbinder",
- "libcutils",
- "liblog",
- "libutils",
- "libinput",
- "libinputflinger",
"libinputreader",
"libinputflinger_base",
- "libstatslog",
],
+ sanitize: {
+ hwaddress: true,
+ undefined: true,
+ all_undefined: true,
+ diag: {
+ undefined: true,
+ },
+ },
+ target: {
+ host: {
+ sanitize: {
+ address: true,
+ },
+ },
+ },
header_libs: [
"libbatteryservice_headers",
"libinputreader_headers",
@@ -117,6 +100,22 @@
}
cc_fuzz {
+ name: "inputflinger_touchpad_input_fuzzer",
+ defaults: [
+ "inputflinger_fuzz_defaults",
+ ],
+ srcs: [
+ "TouchpadInputFuzzer.cpp",
+ ],
+ static_libs: [
+ "libchrome-gestures",
+ ],
+ header_libs: [
+ "libchrome-gestures_headers",
+ ],
+}
+
+cc_fuzz {
name: "inputflinger_input_reader_fuzzer",
defaults: [
"inputflinger_fuzz_defaults",
@@ -145,3 +144,17 @@
"InputClassifierFuzzer.cpp",
],
}
+
+cc_fuzz {
+ name: "inputflinger_latencytracker_fuzzer",
+ defaults: [
+ "inputflinger_fuzz_defaults",
+ "libinputdispatcher_defaults",
+ ],
+ shared_libs: [
+ "libinputreporter",
+ ],
+ srcs: [
+ "LatencyTrackerFuzzer.cpp",
+ ],
+}
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index 8098ef2..af20a27 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -15,36 +15,47 @@
*/
#include <CursorInputMapper.h>
-#include <FuzzContainer.h>
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
namespace android {
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
+static void addProperty(FuzzEventHub& eventHub, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void { fuzzer.addProperty("cursor.mode", "pointer"); },
- [&]() -> void { fuzzer.addProperty("cursor.mode", "navigation"); },
+ {[&]() -> void { eventHub.addProperty("cursor.mode", "pointer"); },
+ [&]() -> void { eventHub.addProperty("cursor.mode", "navigation"); },
[&]() -> void {
- fuzzer.addProperty("cursor.mode", fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("cursor.mode", fdp->ConsumeRandomLengthString(100).data());
},
[&]() -> void {
- fuzzer.addProperty("cursor.orientationAware",
- fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("cursor.orientationAware",
+ fdp->ConsumeRandomLengthString(100).data());
}})();
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
- FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>(policyConfig);
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ InputReaderConfiguration policyConfig;
+ CursorInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider, CursorInputMapper>(*fdp.get(), device,
+ policyConfig);
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
+ [&]() -> void {
+ addProperty(*eventHub.get(), fdp);
+ configureAndResetDevice(*fdp, device);
+ },
[&]() -> void {
std::string dump;
mapper.dump(dump);
@@ -65,22 +76,11 @@
mapper.populateDeviceInfo(info);
},
[&]() -> void {
- int32_t type, code;
- type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
- : fdp->ConsumeIntegral<int32_t>();
- code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
- : fdp->ConsumeIntegral<int32_t>();
-
// Need to reconfigure with 0 or you risk a NPE.
std::list<NotifyArgs> unused =
mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
InputReaderConfiguration::Change(0));
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- type,
- code,
- fdp->ConsumeIntegral<int32_t>()};
+ RawEvent rawEvent = getFuzzedRawEvent(*fdp);
unused += mapper.process(&rawEvent);
},
[&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
deleted file mode 100644
index b992928..0000000
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <InputDevice.h>
-#include <InputMapper.h>
-#include <InputReader.h>
-#include <MapperHelpers.h>
-
-namespace android {
-
-class FuzzContainer {
- std::shared_ptr<FuzzEventHub> mFuzzEventHub;
- sp<FuzzInputReaderPolicy> mFuzzPolicy;
- FuzzInputListener mFuzzListener;
- std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
- std::unique_ptr<InputDevice> mFuzzDevice;
- InputReaderConfiguration mPolicyConfig;
- std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
-
-public:
- FuzzContainer(std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) : mFdp(fdp) {
- // Setup parameters.
- std::string deviceName = mFdp->ConsumeRandomLengthString(16);
- std::string deviceLocation = mFdp->ConsumeRandomLengthString(12);
- int32_t deviceID = mFdp->ConsumeIntegralInRange<int32_t>(0, 5);
- int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from*/ 0, /*to*/ 5);
-
- // Create mocked objects.
- mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp);
- mFuzzPolicy = sp<FuzzInputReaderPolicy>::make(mFdp);
- mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy,
- mFuzzListener, mFdp);
-
- InputDeviceIdentifier identifier;
- identifier.name = deviceName;
- identifier.location = deviceLocation;
- mFuzzDevice = std::make_unique<InputDevice>(mFuzzContext.get(), deviceID, deviceGeneration,
- identifier);
- mFuzzPolicy->getReaderConfiguration(&mPolicyConfig);
- }
-
- ~FuzzContainer() {}
-
- void configureDevice() {
- nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
- std::list<NotifyArgs> out;
- out += mFuzzDevice->configure(arbitraryTime, mPolicyConfig, /*changes=*/{});
- out += mFuzzDevice->reset(arbitraryTime);
- for (const NotifyArgs& args : out) {
- mFuzzListener.notify(args);
- }
- }
-
- void addProperty(std::string key, std::string value) {
- mFuzzEventHub->addProperty(key, value);
- configureDevice();
- }
-
- InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; }
-
- template <class T, typename... Args>
- T& getMapper(Args... args) {
- int32_t eventhubId = mFdp->ConsumeIntegral<int32_t>();
- // ensure a device entry exists for this eventHubId
- mFuzzDevice->addEmptyEventHubDevice(eventhubId);
- configureDevice();
-
- return mFuzzDevice->template constructAndAddMapper<T>(eventhubId, args...);
- }
-};
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 616e870..922cbdf 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -14,45 +14,52 @@
* limitations under the License.
*/
-#include <FuzzContainer.h>
+#include <InputDevice.h>
+#include <InputReaderBase.h>
#include <KeyboardInputMapper.h>
+#include <MapperHelpers.h>
namespace android {
const int32_t kMaxKeycodes = 100;
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
+static void addProperty(FuzzEventHub& eventHub, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void { fuzzer.addProperty("keyboard.orientationAware", "1"); },
+ {[&]() -> void { eventHub.addProperty("keyboard.orientationAware", "1"); },
[&]() -> void {
- fuzzer.addProperty("keyboard.orientationAware",
- fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("keyboard.orientationAware",
+ fdp->ConsumeRandomLengthString(100).data());
},
[&]() -> void {
- fuzzer.addProperty("keyboard.doNotWakeByDefault",
- fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("keyboard.doNotWakeByDefault",
+ fdp->ConsumeRandomLengthString(100).data());
},
[&]() -> void {
- fuzzer.addProperty("keyboard.handlesKeyRepeat",
- fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("keyboard.handlesKeyRepeat",
+ fdp->ConsumeRandomLengthString(100).data());
}})();
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
- FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- KeyboardInputMapper& mapper =
- fuzzer.getMapper<KeyboardInputMapper>(policyConfig, fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ 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>());
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
+ [&]() -> void { addProperty(*eventHub.get(), fdp); },
[&]() -> void {
std::string dump;
mapper.dump(dump);
@@ -64,7 +71,7 @@
[&]() -> void { mapper.getSources(); },
[&]() -> void {
std::list<NotifyArgs> unused =
- mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+ mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), /*readerConfig=*/{},
InputReaderConfiguration::Change(
fdp->ConsumeIntegral<uint32_t>()));
},
@@ -72,17 +79,7 @@
std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
},
[&]() -> void {
- int32_t type, code;
- type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
- : fdp->ConsumeIntegral<int32_t>();
- code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
- : fdp->ConsumeIntegral<int32_t>();
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- type,
- code,
- fdp->ConsumeIntegral<int32_t>()};
+ RawEvent rawEvent = getFuzzedRawEvent(*fdp);
std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 1e44e0f..bdedfdf 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -15,6 +15,10 @@
*/
#pragma once
+#include <map>
+#include <memory>
+
+#include <EventHub.h>
#include <InputDevice.h>
#include <InputMapper.h>
#include <InputReader.h>
@@ -22,7 +26,6 @@
constexpr size_t kValidTypes[] = {EV_SW,
EV_SYN,
- SYN_REPORT,
EV_ABS,
EV_KEY,
EV_MSC,
@@ -46,7 +49,6 @@
ABS_MT_PRESSURE,
ABS_MT_DISTANCE,
ABS_MT_TOOL_TYPE,
- SYN_MT_REPORT,
MSC_SCAN,
REL_X,
REL_Y,
@@ -74,10 +76,27 @@
return static_cast<ToolType>(toolType);
}
+template <class Fdp>
+RawEvent getFuzzedRawEvent(Fdp& fdp) {
+ const int32_t type = fdp.ConsumeBool() ? fdp.PickValueInArray(kValidTypes)
+ : fdp.template ConsumeIntegral<int32_t>();
+ const int32_t code = fdp.ConsumeBool() ? fdp.PickValueInArray(kValidCodes)
+ : fdp.template ConsumeIntegral<int32_t>();
+ return RawEvent{
+ .when = fdp.template ConsumeIntegral<nsecs_t>(),
+ .readTime = fdp.template ConsumeIntegral<nsecs_t>(),
+ .deviceId = fdp.template ConsumeIntegral<int32_t>(),
+ .type = type,
+ .code = code,
+ .value = fdp.template ConsumeIntegral<int32_t>(),
+ };
+}
+
class FuzzEventHub : public EventHubInterface {
InputDeviceIdentifier mIdentifier;
std::vector<TouchVideoFrame> mVideoFrames;
PropertyMap mFuzzConfig;
+ std::map<int32_t /* deviceId */, std::map<int /* axis */, RawAbsoluteAxisInfo>> mAxes;
std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
public:
@@ -97,8 +116,18 @@
std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override {
return mFuzzConfig;
}
+ 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 {
+ 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 mFdp->ConsumeIntegral<status_t>();
}
bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); }
@@ -118,18 +147,7 @@
std::vector<RawEvent> events;
const size_t count = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
for (size_t i = 0; i < count; ++i) {
- int32_t type = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidTypes)
- : mFdp->ConsumeIntegral<int32_t>();
- int32_t code = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidCodes)
- : mFdp->ConsumeIntegral<int32_t>();
- events.push_back({
- .when = mFdp->ConsumeIntegral<nsecs_t>(),
- .readTime = mFdp->ConsumeIntegral<nsecs_t>(),
- .deviceId = mFdp->ConsumeIntegral<int32_t>(),
- .type = type,
- .code = code,
- .value = mFdp->ConsumeIntegral<int32_t>(),
- });
+ events.push_back(getFuzzedRawEvent(*mFdp));
}
return events;
}
@@ -277,7 +295,8 @@
}
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) override {
+ const InputDeviceIdentifier& identifier,
+ const std::optional<KeyboardLayoutInfo> layoutInfo) override {
return nullptr;
}
std::string getDeviceAlias(const InputDeviceIdentifier& identifier) {
@@ -289,6 +308,7 @@
}
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+ bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); }
};
class FuzzInputListener : public virtual InputListenerInterface {
@@ -311,10 +331,8 @@
public:
FuzzInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- InputListenerInterface& listener,
- std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp)
- : mEventHub(eventHub), mPolicy(policy), mFdp(mFdp) {}
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp)
+ : mEventHub(eventHub), mPolicy(sp<FuzzInputReaderPolicy>::make(fdp)), mFdp(fdp) {}
~FuzzInputReaderContext() {}
void updateGlobalMetaState() override {}
int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
@@ -339,6 +357,37 @@
void updateLedMetaState(int32_t metaState) override{};
int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+
+ void setPreventingTouchpadTaps(bool prevent) {}
+ bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
};
+template <class Fdp>
+InputDevice getFuzzedInputDevice(Fdp& fdp, FuzzInputReaderContext* context) {
+ InputDeviceIdentifier identifier;
+ identifier.name = fdp.ConsumeRandomLengthString(16);
+ identifier.location = fdp.ConsumeRandomLengthString(12);
+ int32_t deviceID = fdp.ConsumeIntegralInRange(0, 5);
+ int32_t deviceGeneration = fdp.ConsumeIntegralInRange(0, 5);
+ return InputDevice(context, deviceID, deviceGeneration, identifier);
+}
+
+template <class Fdp>
+void configureAndResetDevice(Fdp& fdp, InputDevice& device) {
+ nsecs_t arbitraryTime = fdp.template ConsumeIntegral<nsecs_t>();
+ std::list<NotifyArgs> out;
+ out += device.configure(arbitraryTime, /*readerConfig=*/{}, /*changes=*/{});
+ out += device.reset(arbitraryTime);
+}
+
+template <class Fdp, class T, typename... Args>
+T& getMapperForDevice(Fdp& fdp, InputDevice& device, Args... args) {
+ int32_t eventhubId = fdp.template ConsumeIntegral<int32_t>();
+ // ensure a device entry exists for this eventHubId
+ device.addEmptyEventHubDevice(eventhubId);
+ configureAndResetDevice(fdp, device);
+
+ return device.template constructAndAddMapper<T>(eventhubId, args...);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 212462d..d3f6690 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -14,60 +14,72 @@
* limitations under the License.
*/
-#include <FuzzContainer.h>
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
#include <MultiTouchInputMapper.h>
namespace android {
const int32_t kMaxKeycodes = 100;
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
+static void addProperty(FuzzEventHub& eventHub, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void { fuzzer.addProperty("touch.deviceType", "touchScreen"); },
+ {[&]() -> void { eventHub.addProperty("touch.deviceType", "touchScreen"); },
[&]() -> void {
- fuzzer.addProperty("touch.deviceType", fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.deviceType", fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.scale", fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.size.scale", fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.bias", fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.size.bias", fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.isSummed",
- fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.size.isSummed",
+ fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.calibration",
- fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.size.calibration",
+ fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.pressure.scale",
- fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.pressure.scale",
+ fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.calibration",
- fdp->ConsumeBool() ? "diameter" : "area");
+ eventHub.addProperty("touch.size.calibration",
+ fdp->ConsumeBool() ? "diameter" : "area");
},
[&]() -> void {
- fuzzer.addProperty("touch.pressure.calibration",
- fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.pressure.calibration",
+ fdp->ConsumeRandomLengthString(8).data());
}})();
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
- FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>(policyConfig);
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ InputReaderConfiguration policyConfig;
+ MultiTouchInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider, MultiTouchInputMapper>(*fdp.get(),
+ device,
+ policyConfig);
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
+ [&]() -> void {
+ addProperty(*eventHub.get(), fdp);
+ configureAndResetDevice(*fdp, device);
+ },
[&]() -> void {
std::string dump;
mapper.dump(dump);
@@ -87,16 +99,7 @@
std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
},
[&]() -> void {
- int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
- : fdp->ConsumeIntegral<int32_t>();
- int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
- : fdp->ConsumeIntegral<int32_t>();
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- type,
- code,
- fdp->ConsumeIntegral<int32_t>()};
+ RawEvent rawEvent = getFuzzedRawEvent(*fdp);
std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index 590207e..ac2030a 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-#include <FuzzContainer.h>
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
#include <SwitchInputMapper.h>
namespace android {
@@ -22,10 +24,15 @@
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
- FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>(policyConfig);
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ SwitchInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider,
+ SwitchInputMapper>(*fdp.get(), device, InputReaderConfiguration{});
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
@@ -36,16 +43,7 @@
},
[&]() -> void { mapper.getSources(); },
[&]() -> void {
- int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
- : fdp->ConsumeIntegral<int32_t>();
- int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
- : fdp->ConsumeIntegral<int32_t>();
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- type,
- code,
- fdp->ConsumeIntegral<int32_t>()};
+ RawEvent rawEvent = getFuzzedRawEvent(*fdp);
std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
new file mode 100644
index 0000000..be765cc
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <linux/input-event-codes.h>
+
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
+#include <TouchpadInputMapper.h>
+
+namespace android {
+
+namespace {
+
+void setAxisInfo(ThreadSafeFuzzedDataProvider& fdp, FuzzEventHub& eventHub, int32_t id, int axis) {
+ if (fdp.ConsumeBool()) {
+ eventHub.setAbsoluteAxisInfo(id, axis,
+ RawAbsoluteAxisInfo{
+ .valid = fdp.ConsumeBool(),
+ .minValue = fdp.ConsumeIntegral<int32_t>(),
+ .maxValue = fdp.ConsumeIntegral<int32_t>(),
+ .flat = fdp.ConsumeIntegral<int32_t>(),
+ .fuzz = fdp.ConsumeIntegral<int32_t>(),
+ .resolution = fdp.ConsumeIntegral<int32_t>(),
+ });
+ }
+}
+
+void setAxisInfos(ThreadSafeFuzzedDataProvider& fdp, FuzzEventHub& eventHub, int32_t id) {
+ setAxisInfo(fdp, eventHub, id, ABS_MT_SLOT);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_POSITION_X);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_POSITION_Y);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_PRESSURE);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_ORIENTATION);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_TOUCH_MAJOR);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_TOUCH_MINOR);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_WIDTH_MAJOR);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_WIDTH_MINOR);
+}
+
+const std::vector<std::string> boolPropertiesToFuzz = {
+ "gestureProp.Compute_Surface_Area_from_Pressure",
+ "gestureProp.Drumroll_Suppression_Enable",
+ "gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls",
+ "gestureProp.Stationary_Wiggle_Filter_Enabled",
+};
+const std::vector<std::string> doublePropertiesToFuzz = {
+ "gestureProp.Fake_Timestamp_Delta",
+ "gestureProp.Finger_Moving_Energy",
+ "gestureProp.Finger_Moving_Hysteresis",
+ "gestureProp.IIR_a1",
+ "gestureProp.IIR_a2",
+ "gestureProp.IIR_b0",
+ "gestureProp.IIR_b1",
+ "gestureProp.IIR_b2",
+ "gestureProp.IIR_b3",
+ "gestureProp.Max_Allowed_Pressure_Change_Per_Sec",
+ "gestureProp.Max_Hysteresis_Pressure_Per_Sec",
+ "gestureProp.Max_Stationary_Move_Speed",
+ "gestureProp.Max_Stationary_Move_Speed_Hysteresis",
+ "gestureProp.Max_Stationary_Move_Suppress_Distance",
+ "gestureProp.Multiple_Palm_Width",
+ "gestureProp.Palm_Edge_Zone_Width",
+ "gestureProp.Palm_Eval_Timeout",
+ "gestureProp.Palm_Pressure",
+ "gestureProp.Palm_Width",
+ "gestureProp.Pressure_Calibration_Offset",
+ "gestureProp.Pressure_Calibration_Slope",
+ "gestureProp.Tap_Exclusion_Border_Width",
+ "gestureProp.Touchpad_Device_Output_Bias_on_X-Axis",
+ "gestureProp.Touchpad_Device_Output_Bias_on_Y-Axis",
+ "gestureProp.Two_Finger_Vertical_Close_Distance_Thresh",
+};
+
+void setDeviceSpecificConfig(ThreadSafeFuzzedDataProvider& fdp, FuzzEventHub& eventHub) {
+ // There are a great many gesture properties offered by the Gestures library, all of which could
+ // potentially be set in Input Device Configuration files. Maintaining a complete list is
+ // impractical, so instead we only fuzz properties which are used in at least one IDC file, or
+ // which are likely to be used in future (e.g. ones for controlling palm rejection).
+
+ if (fdp.ConsumeBool()) {
+ eventHub.addProperty("gestureProp.Touchpad_Stack_Version",
+ std::to_string(fdp.ConsumeIntegral<int>()));
+ }
+
+ for (auto& propertyName : boolPropertiesToFuzz) {
+ if (fdp.ConsumeBool()) {
+ eventHub.addProperty(propertyName, fdp.ConsumeBool() ? "1" : "0");
+ }
+ }
+
+ for (auto& propertyName : doublePropertiesToFuzz) {
+ if (fdp.ConsumeBool()) {
+ eventHub.addProperty(propertyName, std::to_string(fdp.ConsumeFloatingPoint<double>()));
+ }
+ }
+
+ if (fdp.ConsumeBool()) {
+ eventHub.addProperty("gestureProp." + fdp.ConsumeRandomLengthString(),
+ std::to_string(fdp.ConsumeIntegral<int>()));
+ }
+}
+
+void setTouchpadSettings(ThreadSafeFuzzedDataProvider& fdp, InputReaderConfiguration& config) {
+ config.touchpadPointerSpeed = fdp.ConsumeIntegralInRange(-7, 7);
+ config.touchpadNaturalScrollingEnabled = fdp.ConsumeBool();
+ config.touchpadTapToClickEnabled = fdp.ConsumeBool();
+ config.touchpadRightClickZoneEnabled = fdp.ConsumeBool();
+}
+
+} // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
+ std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
+
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ setAxisInfos(*fdp, *eventHub.get(), device.getId());
+ setDeviceSpecificConfig(*fdp, *eventHub.get());
+
+ InputReaderConfiguration policyConfig;
+ // Some settings are fuzzed here, as well as in the main loop, to provide randomized data to the
+ // TouchpadInputMapper constructor.
+ setTouchpadSettings(*fdp, policyConfig);
+ policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
+ TouchpadInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider, TouchpadInputMapper>(*fdp, device,
+ policyConfig);
+
+ // Loop through mapper operations until randomness is exhausted.
+ while (fdp->remaining_bytes() > 0) {
+ fdp->PickValueInArray<std::function<void()>>({
+ [&]() -> void {
+ std::string dump;
+ mapper.dump(dump);
+ },
+ [&]() -> void {
+ InputDeviceInfo info;
+ mapper.populateDeviceInfo(info);
+ },
+ [&]() -> void { mapper.getSources(); },
+ [&]() -> void {
+ setTouchpadSettings(*fdp, policyConfig);
+ policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
+ std::list<NotifyArgs> unused =
+ mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+ InputReaderConfiguration::Change(
+ fdp->ConsumeIntegral<uint32_t>()));
+ },
+ [&]() -> void {
+ std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+ },
+ [&]() -> void {
+ RawEvent event = getFuzzedRawEvent(*fdp);
+ std::list<NotifyArgs> unused = mapper.process(&event);
+ },
+ })();
+ }
+
+ return 0;
+}
+
+} // namespace android
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index b34e54f..2523f3b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -33,6 +33,7 @@
shared_libs: [
"libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libutils",
@@ -40,7 +41,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
],
export_shared_lib_headers: [
@@ -48,7 +49,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
],
cflags: [
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index f89035f..9a23c84 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -15,11 +15,11 @@
*/
#define LOG_TAG "PowerHalController"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
#include <powermanager/PowerHalController.h>
#include <powermanager/PowerHalLoader.h>
#include <utils/Log.h>
@@ -33,7 +33,8 @@
// -------------------------------------------------------------------------------------------------
std::unique_ptr<HalWrapper> HalConnector::connect() {
- if (sp<IPower> halAidl = PowerHalLoader::loadAidl()) {
+ if (std::shared_ptr<aidl::android::hardware::power::IPower> halAidl =
+ PowerHalLoader::loadAidl()) {
return std::make_unique<AidlHalWrapper>(halAidl);
}
// If V1_0 isn't defined, none of them are
@@ -90,20 +91,24 @@
return result;
}
-HalResult<void> PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> PowerHalController::setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) {
std::shared_ptr<HalWrapper> handle = initHal();
auto result = handle->setBoost(boost, durationMs);
return processHalResult(result, "setBoost");
}
-HalResult<void> PowerHalController::setMode(Mode mode, bool enabled) {
+HalResult<void> PowerHalController::setMode(aidl::android::hardware::power::Mode mode,
+ bool enabled) {
std::shared_ptr<HalWrapper> handle = initHal();
auto result = handle->setMode(mode, enabled);
return processHalResult(result, "setMode");
}
-HalResult<sp<IPowerHintSession>> PowerHalController::createHintSession(
- int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+PowerHalController::createHintSession(int32_t tgid, int32_t uid,
+ const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) {
std::shared_ptr<HalWrapper> handle = initHal();
auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos);
return processHalResult(result, "createHintSession");
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
index 6bd40f8..2214461 100644
--- a/services/powermanager/PowerHalLoader.cpp
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -16,10 +16,11 @@
#define LOG_TAG "PowerHalLoader"
+#include <aidl/android/hardware/power/IPower.h>
+#include <android/binder_manager.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/IPower.h>
#include <binder/IServiceManager.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
@@ -54,7 +55,7 @@
// -------------------------------------------------------------------------------------------------
std::mutex PowerHalLoader::gHalMutex;
-sp<IPower> PowerHalLoader::gHalAidl = nullptr;
+std::shared_ptr<aidl::android::hardware::power::IPower> PowerHalLoader::gHalAidl = nullptr;
sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr;
@@ -69,11 +70,30 @@
gHalHidlV1_3 = nullptr;
}
-sp<IPower> PowerHalLoader::loadAidl() {
+std::shared_ptr<aidl::android::hardware::power::IPower> PowerHalLoader::loadAidl() {
std::lock_guard<std::mutex> lock(gHalMutex);
static bool gHalExists = true;
- static auto loadFn = []() { return waitForVintfService<IPower>(); };
- return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
+ if (!gHalExists) {
+ return nullptr;
+ }
+ if (gHalAidl) {
+ return gHalAidl;
+ }
+ auto aidlServiceName =
+ std::string(aidl::android::hardware::power::IPower::descriptor) + "/default";
+ if (!AServiceManager_isDeclared(aidlServiceName.c_str())) {
+ gHalExists = false;
+ return nullptr;
+ }
+ gHalAidl = aidl::android::hardware::power::IPower::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(aidlServiceName.c_str())));
+ if (gHalAidl) {
+ ALOGI("Successfully connected to Power HAL AIDL service.");
+ } else {
+ ALOGI("Power HAL AIDL service not available.");
+ gHalExists = false;
+ }
+ return gHalAidl;
}
sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 9e7adf8..76afbfc 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -15,86 +15,49 @@
*/
#define LOG_TAG "HalWrapper"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
#include <cinttypes>
using namespace android::hardware::power;
-namespace Aidl = android::hardware::power;
+namespace Aidl = aidl::android::hardware::power;
namespace android {
namespace power {
// -------------------------------------------------------------------------------------------------
-
-inline HalResult<void> toHalResult(const binder::Status& result) {
+inline HalResult<void> toHalResult(const ndk::ScopedAStatus& result) {
if (result.isOk()) {
return HalResult<void>::ok();
}
- ALOGE("Power HAL request failed: %s", result.toString8().c_str());
- return HalResult<void>::fromStatus(result);
-}
-
-template <typename T>
-template <typename R>
-HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
- return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
-}
-
-template <typename T>
-template <typename R>
-HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
- return ret.isOk() ? HalResult<T>::fromStatus(status, data)
- : HalResult<T>::failed(ret.description());
+ ALOGE("Power HAL request failed: %s", result.getDescription().c_str());
+ return HalResult<void>::failed(result.getDescription());
}
// -------------------------------------------------------------------------------------------------
-HalResult<void> HalResult<void>::fromStatus(status_t status) {
- if (status == android::OK) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(statusToString(status));
-}
-
-HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<void>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(std::string(status.toString8().c_str()));
-}
-
-template <typename R>
-HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
- return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
-}
-// -------------------------------------------------------------------------------------------------
-
-HalResult<void> EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
toString(boost).c_str(), durationMs);
return HalResult<void>::unsupported();
}
-HalResult<void> EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> EmptyHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
enabled ? "true" : "false");
return HalResult<void>::unsupported();
}
-HalResult<sp<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
threadIds.size());
- return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+ return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
}
HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
@@ -104,8 +67,8 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
- if (boost == Boost::INTERACTION) {
+HalResult<void> HidlHalWrapperV1_0::setBoost(Aidl::Boost boost, int32_t durationMs) {
+ if (boost == Aidl::Boost::INTERACTION) {
return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs);
} else {
ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
@@ -113,20 +76,20 @@
}
}
-HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_0::setMode(Aidl::Mode mode, bool enabled) {
uint32_t data = enabled ? 1 : 0;
switch (mode) {
- case Mode::LAUNCH:
+ case Aidl::Mode::LAUNCH:
return sendPowerHint(V1_3::PowerHint::LAUNCH, data);
- case Mode::LOW_POWER:
+ case Aidl::Mode::LOW_POWER:
return sendPowerHint(V1_3::PowerHint::LOW_POWER, data);
- case Mode::SUSTAINED_PERFORMANCE:
+ case Aidl::Mode::SUSTAINED_PERFORMANCE:
return sendPowerHint(V1_3::PowerHint::SUSTAINED_PERFORMANCE, data);
- case Mode::VR:
+ case Aidl::Mode::VR:
return sendPowerHint(V1_3::PowerHint::VR_MODE, data);
- case Mode::INTERACTIVE:
+ case Aidl::Mode::INTERACTIVE:
return setInteractive(enabled);
- case Mode::DOUBLE_TAP_TO_WAKE:
+ case Aidl::Mode::DOUBLE_TAP_TO_WAKE:
return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
default:
ALOGV("Skipped setMode %s because Power HAL AIDL not available",
@@ -150,11 +113,11 @@
return HalResult<void>::fromReturn(ret);
}
-HalResult<sp<hardware::power::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
threadIds.size());
- return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+ return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
}
HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() {
@@ -178,26 +141,26 @@
return HalResult<void>::fromReturn(ret);
}
-HalResult<void> HidlHalWrapperV1_2::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> HidlHalWrapperV1_2::setBoost(Aidl::Boost boost, int32_t durationMs) {
switch (boost) {
- case Boost::CAMERA_SHOT:
+ case Aidl::Boost::CAMERA_SHOT:
return sendPowerHint(V1_3::PowerHint::CAMERA_SHOT, durationMs);
- case Boost::CAMERA_LAUNCH:
+ case Aidl::Boost::CAMERA_LAUNCH:
return sendPowerHint(V1_3::PowerHint::CAMERA_LAUNCH, durationMs);
default:
return HidlHalWrapperV1_1::setBoost(boost, durationMs);
}
}
-HalResult<void> HidlHalWrapperV1_2::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_2::setMode(Aidl::Mode mode, bool enabled) {
uint32_t data = enabled ? 1 : 0;
switch (mode) {
- case Mode::CAMERA_STREAMING_SECURE:
- case Mode::CAMERA_STREAMING_LOW:
- case Mode::CAMERA_STREAMING_MID:
- case Mode::CAMERA_STREAMING_HIGH:
+ case Aidl::Mode::CAMERA_STREAMING_SECURE:
+ case Aidl::Mode::CAMERA_STREAMING_LOW:
+ case Aidl::Mode::CAMERA_STREAMING_MID:
+ case Aidl::Mode::CAMERA_STREAMING_HIGH:
return sendPowerHint(V1_3::PowerHint::CAMERA_STREAMING, data);
- case Mode::AUDIO_STREAMING_LOW_LATENCY:
+ case Aidl::Mode::AUDIO_STREAMING_LOW_LATENCY:
return sendPowerHint(V1_3::PowerHint::AUDIO_LOW_LATENCY, data);
default:
return HidlHalWrapperV1_1::setMode(mode, enabled);
@@ -206,9 +169,9 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> HidlHalWrapperV1_3::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_3::setMode(Aidl::Mode mode, bool enabled) {
uint32_t data = enabled ? 1 : 0;
- if (mode == Mode::EXPENSIVE_RENDERING) {
+ if (mode == Aidl::Mode::EXPENSIVE_RENDERING) {
return sendPowerHint(V1_3::PowerHint::EXPENSIVE_RENDERING, data);
}
return HidlHalWrapperV1_2::setMode(mode, enabled);
@@ -222,7 +185,7 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> AidlHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
std::unique_lock<std::mutex> lock(mBoostMutex);
size_t idx = static_cast<size_t>(boost);
@@ -237,7 +200,7 @@
auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
if (!isSupportedRet.isOk()) {
ALOGE("Skipped setBoost %s because check support failed with: %s",
- toString(boost).c_str(), isSupportedRet.toString8().c_str());
+ toString(boost).c_str(), isSupportedRet.getDescription().c_str());
// return HalResult::FAILED;
return HalResult<void>::fromStatus(isSupportedRet);
}
@@ -254,7 +217,7 @@
return toHalResult(mHandle->setBoost(boost, durationMs));
}
-HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
std::unique_lock<std::mutex> lock(mModeMutex);
size_t idx = static_cast<size_t>(mode);
@@ -268,7 +231,7 @@
bool isSupported = false;
auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
if (!isSupportedRet.isOk()) {
- return HalResult<void>::failed(isSupportedRet.toString8().c_str());
+ return HalResult<void>::failed(isSupportedRet.getDescription());
}
mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
@@ -283,10 +246,10 @@
return toHalResult(mHandle->setMode(mode, enabled));
}
-HalResult<sp<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
- sp<IPowerHintSession> appSession;
- return HalResult<sp<Aidl::IPowerHintSession>>::
+ std::shared_ptr<Aidl::IPowerHintSession> appSession;
+ return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::
fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
appSession);
}
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index 4343aec..03fc38d 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -32,6 +32,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libpowermanager",
@@ -40,7 +41,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
],
static_libs: [
"libtestUtil",
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
index 6e5e14d..759485f 100644
--- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -16,21 +16,24 @@
#define LOG_TAG "PowerHalAidlBenchmarks"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
-#include <android/hardware/power/WorkDuration.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
#include <benchmark/benchmark.h>
#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <powermanager/PowerHalLoader.h>
#include <testUtil.h>
#include <chrono>
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
-using android::hardware::power::WorkDuration;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
+using aidl::android::hardware::power::WorkDuration;
+using android::power::PowerHalLoader;
using std::chrono::microseconds;
using namespace android;
@@ -63,15 +66,15 @@
template <class R, class... Args0, class... Args1>
static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower::*fn)(Args0...),
Args1&&... args1) {
- sp<IPower> hal = waitForVintfService<IPower>();
+ std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
if (hal == nullptr) {
ALOGV("Power HAL not available, skipping test...");
return;
}
- binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
- if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ndk::ScopedAStatus ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
ALOGV("Power HAL does not support this operation, skipping test...");
return;
}
@@ -79,7 +82,7 @@
while (state.KeepRunning()) {
ret = (*hal.*fn)(std::forward<Args1>(args1)...);
state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
if (delay > 0us) {
testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
}
@@ -90,9 +93,9 @@
template <class R, class... Args0, class... Args1>
static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::*fn)(Args0...),
Args1&&... args1) {
- sp<IPower> pwHal = waitForVintfService<IPower>();
+ std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
- if (pwHal == nullptr) {
+ if (hal == nullptr) {
ALOGV("Power HAL not available, skipping test...");
return;
}
@@ -100,32 +103,32 @@
// do not use tid from the benchmark process, use 1 for init
std::vector<int32_t> threadIds{1};
int64_t durationNanos = 16666666L;
- sp<IPowerHintSession> hal;
+ std::shared_ptr<IPowerHintSession> session;
- auto status = pwHal->createHintSession(1, 0, threadIds, durationNanos, &hal);
+ auto status = hal->createHintSession(1, 0, threadIds, durationNanos, &session);
- if (hal == nullptr) {
+ if (session == nullptr) {
ALOGV("Power HAL doesn't support session, skipping test...");
return;
}
- binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
- if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ndk::ScopedAStatus ret = (*session.*fn)(std::forward<Args1>(args1)...);
+ if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
ALOGV("Power HAL does not support this operation, skipping test...");
return;
}
while (state.KeepRunning()) {
- ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ ret = (*session.*fn)(std::forward<Args1>(args1)...);
state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
if (ONEWAY_API_DELAY > 0us) {
testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
.count());
}
state.ResumeTiming();
}
- hal->close();
+ session->close();
}
static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
@@ -155,16 +158,17 @@
int64_t durationNanos = 16666666L;
int32_t tgid = 999;
int32_t uid = 1001;
- sp<IPowerHintSession> appSession;
- sp<IPower> hal = waitForVintfService<IPower>();
+ std::shared_ptr<IPowerHintSession> appSession;
+ std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
if (hal == nullptr) {
ALOGV("Power HAL not available, skipping test...");
return;
}
- binder::Status ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
- if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ndk::ScopedAStatus ret =
+ hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+ if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
ALOGV("Power HAL does not support this operation, skipping test...");
return;
}
@@ -172,7 +176,7 @@
while (state.KeepRunning()) {
ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
appSession->close();
state.ResumeTiming();
}
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
index f8abc7a..effddda 100644
--- a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -16,15 +16,15 @@
#define LOG_TAG "PowerHalControllerBenchmarks"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <benchmark/benchmark.h>
#include <powermanager/PowerHalController.h>
#include <testUtil.h>
#include <chrono>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::power::HalResult;
using android::power::PowerHalController;
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
index 167f3a6..111b5d7 100644
--- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -16,10 +16,10 @@
#define LOG_TAG "PowerHalHidlBenchmarks"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
#include <benchmark/benchmark.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
@@ -27,8 +27,6 @@
#include <chrono>
using android::hardware::Return;
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::PowerHint;
using std::chrono::microseconds;
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 54dffcf..08fcdc8 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -43,6 +43,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libpowermanager",
@@ -51,9 +52,10 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
],
static_libs: [
"libgmock",
],
+ require_root: true,
}
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
index 6cc7a6f..01270ce 100644
--- a/services/powermanager/tests/PowerHalControllerTest.cpp
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -16,9 +16,9 @@
#define LOG_TAG "PowerHalControllerTest"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalController.h>
@@ -26,8 +26,8 @@
#include <thread>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::IPower;
using android::hardware::power::V1_0::PowerHint;
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
index e36deed..7d97354 100644
--- a/services/powermanager/tests/PowerHalLoaderTest.cpp
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -16,9 +16,8 @@
#define LOG_TAG "PowerHalLoaderTest"
-#include <android-base/logging.h>
+#include <aidl/android/hardware/power/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/IPower.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalLoader.h>
@@ -28,7 +27,7 @@
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_2 = android::hardware::power::V1_2::IPower;
using IPowerV1_3 = android::hardware::power::V1_3::IPower;
-using IPowerAidl = android::hardware::power::IPower;
+using IPowerAidl = aidl::android::hardware::power::IPower;
using namespace android;
using namespace android::power;
@@ -37,30 +36,30 @@
// -------------------------------------------------------------------------------------------------
template <typename T>
-sp<T> loadHal();
+T loadHal();
template <>
-sp<IPowerAidl> loadHal<IPowerAidl>() {
+std::shared_ptr<IPowerAidl> loadHal<std::shared_ptr<IPowerAidl>>() {
return PowerHalLoader::loadAidl();
}
template <>
-sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+sp<IPowerV1_0> loadHal<sp<IPowerV1_0>>() {
return PowerHalLoader::loadHidlV1_0();
}
template <>
-sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+sp<IPowerV1_1> loadHal<sp<IPowerV1_1>>() {
return PowerHalLoader::loadHidlV1_1();
}
template <>
-sp<IPowerV1_2> loadHal<IPowerV1_2>() {
+sp<IPowerV1_2> loadHal<sp<IPowerV1_2>>() {
return PowerHalLoader::loadHidlV1_2();
}
template <>
-sp<IPowerV1_3> loadHal<IPowerV1_3>() {
+sp<IPowerV1_3> loadHal<sp<IPowerV1_3>>() {
return PowerHalLoader::loadHidlV1_3();
}
@@ -69,46 +68,47 @@
template <typename T>
class PowerHalLoaderTest : public Test {
public:
- sp<T> load() { return ::loadHal<T>(); }
+ T load() { return ::loadHal<T>(); }
void unload() { PowerHalLoader::unloadAll(); }
};
// -------------------------------------------------------------------------------------------------
-
-typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1, IPowerV1_2, IPowerV1_3> PowerHalTypes;
+typedef ::testing::Types<std::shared_ptr<IPowerAidl>, sp<IPowerV1_0>, sp<IPowerV1_1>,
+ sp<IPowerV1_2>, sp<IPowerV1_3>>
+ PowerHalTypes;
TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
- sp<TypeParam> firstHal = this->load();
+ TypeParam firstHal = this->load();
if (firstHal == nullptr) {
ALOGE("Power HAL not available. Skipping test.");
return;
}
- sp<TypeParam> secondHal = this->load();
+ TypeParam secondHal = this->load();
ASSERT_EQ(firstHal, secondHal);
}
TYPED_TEST(PowerHalLoaderTest, TestUnload) {
- sp<TypeParam> firstHal = this->load();
+ TypeParam firstHal = this->load();
if (firstHal == nullptr) {
ALOGE("Power HAL not available. Skipping test.");
return;
}
this->unload();
- sp<TypeParam> secondHal = this->load();
+ TypeParam secondHal = this->load();
ASSERT_NE(secondHal, nullptr);
ASSERT_NE(firstHal, secondHal);
}
TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
- std::vector<std::future<sp<TypeParam>>> futures;
+ std::vector<std::future<TypeParam>> futures;
for (int i = 0; i < 10; i++) {
futures.push_back(
std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
}
futures[0].wait();
- sp<TypeParam> firstHal = futures[0].get();
+ TypeParam firstHal = futures[0].get();
if (firstHal == nullptr) {
ALOGE("Power HAL not available. Skipping test.");
return;
@@ -116,7 +116,7 @@
for (int i = 1; i < 10; i++) {
futures[i].wait();
- sp<TypeParam> currentHal = futures[i].get();
+ TypeParam currentHal = futures[i].get();
ASSERT_EQ(firstHal, currentHal);
}
}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index cb1a77a..641ba9f 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -16,9 +16,9 @@
#define LOG_TAG "PowerHalWrapperAidlTest"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -28,11 +28,11 @@
#include <unistd.h>
#include <thread>
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
using android::binder::Status;
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
using namespace android;
using namespace android::power;
@@ -43,18 +43,21 @@
class MockIPower : public IPower {
public:
- MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
- MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
- MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
- MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
- MOCK_METHOD(Status, createHintSession,
+ MockIPower() = default;
+
+ MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos, sp<IPowerHintSession>* session),
+ int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
(override));
- MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
};
// -------------------------------------------------------------------------------------------------
@@ -65,13 +68,13 @@
protected:
std::unique_ptr<HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIPower>> mMockHal = nullptr;
+ std::shared_ptr<StrictMock<MockIPower>> mMockHal = nullptr;
};
// -------------------------------------------------------------------------------------------------
void PowerHalWrapperAidlTest::SetUp() {
- mMockHal = new StrictMock<MockIPower>();
+ mMockHal = ndk::SharedRefBase::make<StrictMock<MockIPower>>();
mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
ASSERT_NE(nullptr, mWrapper);
}
@@ -83,9 +86,11 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100)))
- .Times(Exactly(1));
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
@@ -97,13 +102,14 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
}
auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
@@ -115,7 +121,8 @@
TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(false),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
ASSERT_TRUE(result.isUnsupported());
@@ -128,8 +135,13 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
- EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
+ auto& exp = EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
+ .Times(Exactly(10));
+ for (int i = 0; i < 10; i++) {
+ exp.WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ }
}
std::vector<std::thread> threads;
@@ -147,9 +159,11 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
- .Times(Exactly(1));
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
@@ -161,13 +175,14 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
}
auto result = mWrapper->setMode(Mode::LAUNCH, true);
@@ -179,14 +194,16 @@
TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(false),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
auto result = mWrapper->setMode(Mode::LAUNCH, true);
ASSERT_TRUE(result.isUnsupported());
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::CAMERA_STREAMING_HIGH), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(false),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
ASSERT_TRUE(result.isUnsupported());
}
@@ -196,8 +213,13 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
- EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
+ auto& exp = EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false)))
+ .Times(Exactly(10));
+ for (int i = 0; i < 10; i++) {
+ exp.WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ }
}
std::vector<std::thread> threads;
@@ -217,7 +239,8 @@
int64_t durationNanos = 16666666L;
EXPECT_CALL(*mMockHal.get(),
createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
- .Times(Exactly(1));
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
ASSERT_TRUE(result.isOk());
}
@@ -230,13 +253,16 @@
EXPECT_CALL(*mMockHal.get(),
createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT)));
+ .WillOnce(Return(testing::ByMove(
+ ndk::ScopedAStatus::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT))));
auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
ASSERT_TRUE(result.isFailed());
}
TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) {
- EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_))
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
auto result = mWrapper->getHintSessionPreferredRate();
ASSERT_TRUE(result.isOk());
int64_t rate = result.value();
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
index 0cd2e22..461143b 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -16,17 +16,17 @@
#define LOG_TAG "PowerHalWrapperHidlV1_0Test"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::IPower;
using android::hardware::power::V1_0::PowerHint;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
index 32f84e2..79dd996 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -16,18 +16,18 @@
#define LOG_TAG "PowerHalWrapperHidlV1_1Test"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::PowerHint;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
index cf48409..aa6d6c7 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
@@ -16,18 +16,18 @@
#define LOG_TAG "PowerHalWrapperHidlV1_2Test"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.2/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
index 2c48537..a995afd 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
@@ -16,18 +16,18 @@
#define LOG_TAG "PowerHalWrapperHidlV1_3Test"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index f5b360f..e60db93 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -308,8 +308,12 @@
}
int32_t token;
- mSensors->configDirectReport(sensorHandle, channelHandle, rate, &token);
- return token;
+ status_t status = convertToStatus(
+ mSensors->configDirectReport(sensorHandle, channelHandle, rate, &token));
+ if (status == OK && rate != ISensors::RateLevel::STOP) {
+ status = static_cast<status_t>(token);
+ }
+ return status;
}
void AidlSensorHalWrapper::writeWakeLockHandled(uint32_t count) {
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index e27b52b..16088ca 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -19,6 +19,7 @@
#include "SensorService.h"
#include <android/util/ProtoOutputStream.h>
+#include <cutils/properties.h>
#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
namespace android {
@@ -41,17 +42,20 @@
if (count > 0) {
for (size_t i=0 ; i<size_t(count) ; i++) {
- if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
- mAcc = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
- mMag = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
- mGyro = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) {
- uncalibratedGyro = Sensor(list + i);
+ // Only use non-wakeup sensors
+ if ((list[i].flags & SENSOR_FLAG_WAKE_UP) == 0) {
+ if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
+ mAcc = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
+ mMag = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
+ mGyro = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) {
+ uncalibratedGyro = Sensor(list + i);
+ }
}
}
@@ -60,10 +64,12 @@
mGyro = uncalibratedGyro;
}
- // 200 Hz for gyro events is a good compromise between precision
- // and power/cpu usage.
- mEstimatedGyroRate = 200;
- mTargetDelayNs = 1000000000LL/mEstimatedGyroRate;
+ // Wearable devices will want to tune this parameter
+ // to 100 (Hz) in device.mk to save some power.
+ int32_t value = property_get_int32(
+ "sensors.aosp_low_power_sensor_fusion.maximum_rate", 200);
+ mEstimatedGyroRate = static_cast<float>(value);
+ mTargetDelayNs = 1000000000LL / mEstimatedGyroRate;
for (int i = 0; i<NUM_FUSION_MODE; ++i) {
mFusions[i].init(i);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 0aa1bcb..545f6c2 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -61,7 +61,7 @@
// For older HALs which don't support batching, use a smaller socket buffer size.
#define SOCKET_BUFFER_SIZE_NON_BATCHED (4 * 1024)
-#define SENSOR_REGISTRATIONS_BUF_SIZE 200
+#define SENSOR_REGISTRATIONS_BUF_SIZE 500
// Apps that targets S+ and do not have HIGH_SAMPLING_RATE_SENSORS permission will be capped
// at 200 Hz. The cap also applies to all requests when the mic toggle is flipped to on, regardless
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index b00d1a7..e939d51 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -141,7 +141,7 @@
printf("ALOOPER_POLL_TIMEOUT\n");
break;
case ALOOPER_POLL_ERROR:
- printf("ALOOPER_POLL_TIMEOUT\n");
+ printf("ALOOPER_POLL_ERROR\n");
break;
default:
printf("ugh? poll returned %d\n", ret);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b355221..326645e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -48,7 +48,7 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
"libbase",
"libbinder",
"libbinder_ndk",
@@ -168,6 +168,7 @@
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"HdrLayerInfoReporter.cpp",
+ "HdrSdrRatioOverlay.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerFE.cpp",
diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp
index 6ddf790..5a1ec6f 100644
--- a/services/surfaceflinger/BackgroundExecutor.cpp
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -20,6 +20,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <utils/Log.h>
+#include <mutex>
#include "BackgroundExecutor.h"
@@ -60,4 +61,17 @@
LOG_ALWAYS_FATAL_IF(sem_post(&mSemaphore), "sem_post failed");
}
+void BackgroundExecutor::flushQueue() {
+ std::mutex mutex;
+ std::condition_variable cv;
+ bool flushComplete = false;
+ sendCallbacks({[&]() {
+ std::scoped_lock lock{mutex};
+ flushComplete = true;
+ cv.notify_one();
+ }});
+ std::unique_lock<std::mutex> lock{mutex};
+ cv.wait(lock, [&]() { return flushComplete; });
+}
+
} // namespace android
diff --git a/services/surfaceflinger/BackgroundExecutor.h b/services/surfaceflinger/BackgroundExecutor.h
index 0fae5a5..66b7d7a 100644
--- a/services/surfaceflinger/BackgroundExecutor.h
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -34,6 +34,7 @@
// Queues callbacks onto a work queue to be executed by a background thread.
// This is safe to call from multiple threads.
void sendCallbacks(Callbacks&& tasks);
+ void flushQueue();
private:
sem_t mSemaphore;
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 702bd33..3426495 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -27,7 +27,7 @@
"android.hardware.graphics.composer@2.4",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
"libbase",
"libcutils",
"libgui",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index d93e25e..1a8644e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -37,6 +37,15 @@
half4 color;
std::vector<int32_t> layerIds;
};
+
+// Interface of composition engine power hint callback.
+struct ICEPowerCallback {
+ virtual void notifyCpuLoadUp() = 0;
+
+protected:
+ ~ICEPowerCallback() = default;
+};
+
/**
* A parameter object for refreshing a set of outputs
*/
@@ -58,9 +67,6 @@
// Controls how the color mode is chosen for an output
OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
- // If not Dataspace::UNKNOWN, overrides the dataspace on each output
- ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
-
// Forces a color mode on the outputs being refreshed
ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
@@ -96,6 +102,8 @@
std::vector<BorderRenderInfo> borderInfoList;
bool hasTrustedPresentationListener = false;
+
+ ICEPowerCallback* powerCallback = nullptr;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index df44e75..9c80cac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -87,10 +87,6 @@
// Returns true if HWC for this profile supports the dataspace
virtual bool isDataspaceSupported(ui::Dataspace) const = 0;
- // Returns the target dataspace for picked color mode and dataspace
- virtual ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace,
- ui::Dataspace colorSpaceAgnosticDataspace) const = 0;
-
// Debugging
virtual void dump(std::string&) const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index a3d8639..370c7cf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -136,7 +136,6 @@
ui::ColorMode mode{ui::ColorMode::NATIVE};
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
- ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
};
// Use internally to incrementally compute visibility/coverage
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
index 9bc0e68..3aad04c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -55,7 +55,6 @@
const HdrCapabilities& getHdrCapabilities() const override;
bool isDataspaceSupported(ui::Dataspace) const override;
- ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace) const override;
void dump(std::string&) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index a3fda61..6cb1e7e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -32,6 +32,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/ProjectionSpace.h>
#include <renderengine/BorderRenderInfo.h>
#include <ui/LayerStack.h>
@@ -115,9 +116,6 @@
// Current active dataspace
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
- // Current target dataspace
- ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
-
std::optional<android::HWComposer::DeviceRequestedChanges> previousDeviceRequestedChanges{};
bool previousDeviceRequestedSuccess = false;
@@ -167,6 +165,8 @@
uint64_t lastOutputLayerHash = 0;
uint64_t outputLayerHash = 0;
+ ICEPowerCallback* powerCallback = nullptr;
+
// Debugging
void dump(std::string& result) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
index d607c75..9f6141a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
@@ -66,7 +66,7 @@
TexturePool(renderengine::RenderEngine& renderEngine)
: mRenderEngine(renderEngine), mEnabled(false) {}
- virtual ~TexturePool() = default;
+ virtual ~TexturePool();
// Sets the display size for the texture pool.
// This will trigger a reallocation for all remaining textures in the pool.
@@ -83,11 +83,10 @@
// be held by the pool. This is useful when the active display changes.
void setEnabled(bool enable);
- void dump(std::string& out) const;
+ void dump(std::string& out) const EXCLUDES(mMutex);
protected:
// Proteted visibility so that they can be used for testing
- const static constexpr size_t kMinPoolSize = 3;
const static constexpr size_t kMaxPoolSize = 4;
struct Entry {
@@ -96,16 +95,20 @@
};
std::deque<Entry> mPool;
+ std::future<std::shared_ptr<renderengine::ExternalTexture>> mGenTextureFuture;
private:
- std::shared_ptr<renderengine::ExternalTexture> genTexture();
+ std::shared_ptr<renderengine::ExternalTexture> genTexture(ui::Size size);
// Returns a previously borrowed texture to the pool.
void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
const sp<Fence>& fence);
- void allocatePool();
- renderengine::RenderEngine& mRenderEngine;
- ui::Size mSize;
+ void genTextureAsyncIfNeeded() REQUIRES(mMutex);
+ void resetPool() REQUIRES(mMutex);
+ renderengine::RenderEngine& mRenderEngine GUARDED_BY(mRenderEngineMutex);
+ ui::Size mSize GUARDED_BY(mMutex);
bool mEnabled;
+ mutable std::mutex mMutex;
+ mutable std::mutex mRenderEngineMutex;
};
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
index 1aaebea..a904521 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -43,8 +43,6 @@
MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
MOCK_CONST_METHOD1(isDataspaceSupported, bool(ui::Dataspace));
- MOCK_CONST_METHOD3(getTargetDataspace,
- ui::Dataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 85fc095..f2acfc9 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -107,14 +107,9 @@
}
void Display::setColorProfile(const ColorProfile& colorProfile) {
- const ui::Dataspace targetDataspace =
- getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
- colorProfile.colorSpaceAgnosticDataspace);
-
if (colorProfile.mode == getState().colorMode &&
colorProfile.dataspace == getState().dataspace &&
- colorProfile.renderIntent == getState().renderIntent &&
- targetDataspace == getState().targetDataspace) {
+ colorProfile.renderIntent == getState().renderIntent) {
return;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index a7c4512..8f67f36 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -391,17 +391,6 @@
}
}
-ui::Dataspace DisplayColorProfile::getTargetDataspace(ColorMode mode, Dataspace dataspace,
- Dataspace colorSpaceAgnosticDataspace) const {
- if (isHdrColorMode(mode)) {
- return Dataspace::UNKNOWN;
- }
- if (colorSpaceAgnosticDataspace != ui::Dataspace::UNKNOWN) {
- return colorSpaceAgnosticDataspace;
- }
- return dataspace;
-}
-
void DisplayColorProfile::dump(std::string& out) const {
out.append(" Composition Display Color State:");
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 793959c..d4230f5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -261,22 +261,16 @@
}
void Output::setColorProfile(const ColorProfile& colorProfile) {
- ui::Dataspace targetDataspace =
- getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
- colorProfile.colorSpaceAgnosticDataspace);
-
auto& outputState = editState();
if (outputState.colorMode == colorProfile.mode &&
outputState.dataspace == colorProfile.dataspace &&
- outputState.renderIntent == colorProfile.renderIntent &&
- outputState.targetDataspace == targetDataspace) {
+ outputState.renderIntent == colorProfile.renderIntent) {
return;
}
outputState.colorMode = colorProfile.mode;
outputState.dataspace = colorProfile.dataspace;
outputState.renderIntent = colorProfile.renderIntent;
- outputState.targetDataspace = targetDataspace;
mRenderSurface->setBufferDataspace(colorProfile.dataspace);
@@ -469,15 +463,14 @@
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& layerFESet) {
- ATRACE_CALL();
- ALOGV(__FUNCTION__);
-
auto& outputState = editState();
// Do nothing if this output is not enabled or there is no need to perform this update
if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
return;
}
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
// Process the layers to determine visibility and coverage
compositionengine::Output::CoverageState coverage{layerFESet};
@@ -843,6 +836,7 @@
editState().earliestPresentTime = refreshArgs.earliestPresentTime;
editState().expectedPresentTime = refreshArgs.expectedPresentTime;
+ editState().powerCallback = refreshArgs.powerCallback;
compositionengine::OutputLayer* peekThroughLayer = nullptr;
sp<GraphicBuffer> previousOverride = nullptr;
@@ -984,8 +978,7 @@
const compositionengine::CompositionRefreshArgs& refreshArgs) const {
if (refreshArgs.outputColorSetting == OutputColorSetting::kUnmanaged) {
return ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC,
- refreshArgs.colorSpaceAgnosticDataspace};
+ ui::RenderIntent::COLORIMETRIC};
}
ui::Dataspace hdrDataSpace;
@@ -1031,8 +1024,7 @@
mDisplayColorProfile->getBestColorMode(bestDataSpace, intent, &outDataSpace, &outMode,
&outRenderIntent);
- return ColorProfile{outMode, outDataSpace, outRenderIntent,
- refreshArgs.colorSpaceAgnosticDataspace};
+ return ColorProfile{outMode, outDataSpace, outRenderIntent};
}
void Output::beginFrame() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index c512a1e..39cf671 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -52,7 +52,6 @@
dumpVal(out, "colorMode", toString(colorMode), colorMode);
dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
dumpVal(out, "dataspace", toString(dataspace), dataspace);
- dumpVal(out, "targetDataspace", toString(targetDataspace), targetDataspace);
out.append("\n ");
dumpVal(out, "colorTransformMatrix", colorTransformMatrix);
@@ -67,7 +66,7 @@
out.append("\n ");
out.append("\n ");
- dumpVal(out, "treate170mAsSrgb", treat170mAsSrgb);
+ dumpVal(out, "treat170mAsSrgb", treat170mAsSrgb);
out.append("\n");
for (const auto& borderRenderInfo : borderInfoList) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0ac0ecb..b492b6a 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -315,9 +315,10 @@
// Determine the output dependent dataspace for this layer. If it is
// colorspace agnostic, it just uses the dataspace chosen for the output to
// avoid the need for color conversion.
- state.dataspace = layerFEState->isColorspaceAgnostic &&
- outputState.targetDataspace != ui::Dataspace::UNKNOWN
- ? outputState.targetDataspace
+ // For now, also respect the colorspace agnostic flag if we're drawing to HDR, to avoid drastic
+ // luminance shift. TODO(b/292162273): we should check if that's true though.
+ state.dataspace = layerFEState->isColorspaceAgnostic && !isHdrDataspace(outputState.dataspace)
+ ? outputState.dataspace
: layerFEState->dataspace;
// Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 8ced0ac..7547be9 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -162,6 +162,9 @@
const OutputCompositionState& outputState,
bool deviceHandlesColorTransform) {
ATRACE_CALL();
+ if (outputState.powerCallback) {
+ outputState.powerCallback->notifyCpuLoadUp();
+ }
const Rect& viewport = outputState.layerStackSpace.getContent();
const ui::Dataspace& outputDataspace = outputState.dataspace;
const ui::Transform::RotationFlags orientation =
@@ -177,18 +180,19 @@
.targetLuminanceNits = outputState.displayBrightnessNits,
};
- LayerFE::ClientCompositionTargetSettings targetSettings{
- .clip = Region(viewport),
- .needsFiltering = false,
- .isSecure = outputState.isSecure,
- .supportsProtectedContent = false,
- .viewport = viewport,
- .dataspace = outputDataspace,
- .realContentIsVisible = true,
- .clearContent = false,
- .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
- .whitePointNits = outputState.displayBrightnessNits,
- };
+ LayerFE::ClientCompositionTargetSettings
+ targetSettings{.clip = Region(viewport),
+ .needsFiltering = false,
+ .isSecure = outputState.isSecure,
+ .supportsProtectedContent = false,
+ .viewport = viewport,
+ .dataspace = outputDataspace,
+ .realContentIsVisible = true,
+ .clearContent = false,
+ .blurSetting =
+ LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ .whitePointNits = outputState.displayBrightnessNits,
+ .treat170mAsSrgb = outputState.treat170mAsSrgb};
std::vector<renderengine::LayerSettings> layerSettings;
renderengine::LayerSettings highlight;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
index 54ecb56..10f58ce 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -25,31 +25,61 @@
namespace android::compositionengine::impl::planner {
-void TexturePool::allocatePool() {
+TexturePool::~TexturePool() {
+ if (mGenTextureFuture.valid()) {
+ mGenTextureFuture.get();
+ }
+}
+
+void TexturePool::resetPool() {
+ if (mGenTextureFuture.valid()) {
+ mGenTextureFuture.get();
+ }
mPool.clear();
- if (mEnabled && mSize.isValid()) {
- mPool.resize(kMinPoolSize);
- std::generate_n(mPool.begin(), kMinPoolSize, [&]() {
- return Entry{genTexture(), nullptr};
- });
+ genTextureAsyncIfNeeded();
+}
+
+// Generate a new texture asynchronously so it will not require allocation on the main
+// thread.
+void TexturePool::genTextureAsyncIfNeeded() {
+ if (mEnabled && mSize.isValid() && !mGenTextureFuture.valid()) {
+ mGenTextureFuture = std::async(
+ std::launch::async, [&](ui::Size size) { return genTexture(size); }, mSize);
}
}
void TexturePool::setDisplaySize(ui::Size size) {
+ std::lock_guard lock(mMutex);
if (mSize == size) {
return;
}
mSize = size;
- allocatePool();
+ resetPool();
}
std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
if (mPool.empty()) {
- return std::make_shared<AutoTexture>(*this, genTexture(), nullptr);
+ std::lock_guard lock(mMutex);
+ std::shared_ptr<TexturePool::AutoTexture> tex;
+ if (mGenTextureFuture.valid()) {
+ tex = std::make_shared<AutoTexture>(*this, mGenTextureFuture.get(), nullptr);
+ } else {
+ tex = std::make_shared<AutoTexture>(*this, genTexture(mSize), nullptr);
+ }
+ // Speculatively generate a new texture, so that the next call does not need
+ // to wait for allocation.
+ genTextureAsyncIfNeeded();
+ return tex;
}
const auto entry = mPool.front();
mPool.pop_front();
+ if (mPool.empty()) {
+ std::lock_guard lock(mMutex);
+ // Similiarly generate a new texture when lending out the last entry, so that
+ // the next call does not need to wait for allocation.
+ genTextureAsyncIfNeeded();
+ }
return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence);
}
@@ -60,6 +90,8 @@
return;
}
+ std::lock_guard lock(mMutex);
+
// Or the texture on the floor if the pool is no longer tracking textures of the same size.
if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
@@ -80,13 +112,14 @@
mPool.push_back({std::move(texture), fence});
}
-std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() {
- LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size");
+std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size size) {
+ std::lock_guard lock(mRenderEngineMutex);
+ LOG_ALWAYS_FATAL_IF(!size.isValid(), "Attempted to generate texture with invalid size");
return std::make_shared<
renderengine::impl::
ExternalTexture>(sp<GraphicBuffer>::
- make(static_cast<uint32_t>(mSize.getWidth()),
- static_cast<uint32_t>(mSize.getHeight()),
+ make(static_cast<uint32_t>(size.getWidth()),
+ static_cast<uint32_t>(size.getHeight()),
HAL_PIXEL_FORMAT_RGBA_8888, 1U,
static_cast<uint64_t>(
GraphicBuffer::USAGE_HW_RENDER |
@@ -100,13 +133,16 @@
void TexturePool::setEnabled(bool enabled) {
mEnabled = enabled;
- allocatePool();
+
+ std::lock_guard lock(mMutex);
+ resetPool();
}
void TexturePool::dump(std::string& out) const {
+ std::lock_guard lock(mMutex);
base::StringAppendF(&out,
"TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n",
mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height);
}
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index 21b9aa9..b3ff2ec 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -646,29 +646,5 @@
EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
}
-/*
- * RenderSurface::getTargetDataspace()
- */
-
-TEST_F(DisplayColorProfileTest, getTargetDataspaceWorks) {
- auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
-
- // For a non-HDR colorspace with no colorSpaceAgnosticDataspace override,
- // the input dataspace should be returned.
- EXPECT_EQ(Dataspace::DISPLAY_P3,
- profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
- Dataspace::UNKNOWN));
-
- // If colorSpaceAgnosticDataspace is set, its value should be returned
- EXPECT_EQ(Dataspace::V0_SRGB,
- profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
- Dataspace::V0_SRGB));
-
- // For an HDR colorspace, Dataspace::UNKNOWN should be returned.
- EXPECT_EQ(Dataspace::UNKNOWN,
- profile.getTargetDataspace(ColorMode::BT2100_PQ, Dataspace::BT2020_PQ,
- Dataspace::UNKNOWN));
-}
-
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 9be6bc2..027004a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -403,23 +403,18 @@
mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
mDisplay->setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile));
- EXPECT_CALL(*colorProfile, getTargetDataspace(_, _, _))
- .WillRepeatedly(Return(ui::Dataspace::UNKNOWN));
-
// These values are expected to be the initial state.
ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
- ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
// Otherwise if the values are unchanged, nothing happens
mDisplay->setColorProfile(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
// Otherwise if the values are different, updates happen
EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
@@ -429,13 +424,11 @@
.Times(1);
mDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
}
TEST_F(DisplaySetColorModeTest, doesNothingForVirtualDisplay) {
@@ -448,19 +441,13 @@
virtualDisplay->setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(colorProfile));
- EXPECT_CALL(*colorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
-
- virtualDisplay->setColorProfile(
- ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN});
+ virtualDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3,
+ ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().targetDataspace);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 67b94ee..892bb8f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -94,7 +94,8 @@
MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
- MOCK_CONST_METHOD1(getModes, std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId));
+ MOCK_CONST_METHOD2(getModes,
+ std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t));
MOCK_CONST_METHOD1(getActiveMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index 961ec80..f74ef4c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -34,6 +34,7 @@
MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
(override));
MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
+ MOCK_METHOD(void, notifyCpuLoadUp, (), (override));
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index aa83883..9039d16 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -642,7 +642,7 @@
TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly) {
mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
- mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
+ mOutputState.dataspace = ui::Dataspace::V0_SCRGB;
// If the layer is not colorspace agnostic, the output layer dataspace
// should use the layers requested colorspace.
@@ -659,11 +659,18 @@
mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
+
+ // If the output is HDR, then don't blind the user with a colorspace agnostic dataspace
+ // drawing all white
+ mOutputState.dataspace = ui::Dataspace::BT2020_PQ;
+
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
}
TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceWith170mReplacement) {
mLayerFEState.dataspace = ui::Dataspace::TRANSFER_SMPTE_170M;
- mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
mOutputState.treat170mAsSrgb = false;
mLayerFEState.isColorspaceAgnostic = false;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 9e0e7b5..ebf9a2b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -175,12 +175,10 @@
using ColorProfile = compositionengine::Output::ColorProfile;
void dumpColorProfile(ColorProfile profile, std::string& result, const char* name) {
- android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d] %s[%d]) ", name,
+ android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d]) ", name,
toString(profile.mode).c_str(), profile.mode,
toString(profile.dataspace).c_str(), profile.dataspace,
- toString(profile.renderIntent).c_str(), profile.renderIntent,
- toString(profile.colorSpaceAgnosticDataspace).c_str(),
- profile.colorSpaceAgnosticDataspace);
+ toString(profile.renderIntent).c_str(), profile.renderIntent);
}
// Checks for a ColorProfile match
@@ -192,8 +190,7 @@
*result_listener << buf;
return (expected.mode == arg.mode) && (expected.dataspace == arg.dataspace) &&
- (expected.renderIntent == arg.renderIntent) &&
- (expected.colorSpaceAgnosticDataspace == arg.colorSpaceAgnosticDataspace);
+ (expected.renderIntent == arg.renderIntent);
}
/*
@@ -540,20 +537,14 @@
TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) {
using ColorProfile = Output::ColorProfile;
- EXPECT_CALL(*mDisplayColorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput->getState().colorMode);
EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput->getState().targetDataspace);
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
@@ -561,19 +552,12 @@
TEST_F(OutputSetColorProfileTest, doesNothingIfNoChange) {
using ColorProfile = Output::ColorProfile;
- EXPECT_CALL(*mDisplayColorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
-
mOutput->editState().colorMode = ui::ColorMode::DISPLAY_P3;
mOutput->editState().dataspace = ui::Dataspace::DISPLAY_P3;
mOutput->editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
- mOutput->editState().targetDataspace = ui::Dataspace::UNKNOWN;
mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
}
@@ -2133,12 +2117,11 @@
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput,
- setColorProfile(ColorProfileEq(
- ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN})));
+ setColorProfile(
+ ColorProfileEq(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC})));
mRefreshArgs.outputColorSetting = OutputColorSetting::kUnmanaged;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mOutput.updateColorProfile(mRefreshArgs);
}
@@ -2148,7 +2131,6 @@
OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() {
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
}
struct ExpectBestColorModeCallResultUsedToSetColorProfileState
@@ -2163,8 +2145,7 @@
SetArgPointee<4>(renderIntent)));
EXPECT_CALL(getInstance()->mOutput,
setColorProfile(
- ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent,
- ui::Dataspace::UNKNOWN})));
+ ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent})));
return nextState<ExecuteState>();
}
};
@@ -2191,55 +2172,6 @@
.execute();
}
-struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile
- : public OutputUpdateColorProfileTest {
- OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() {
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
- EXPECT_CALL(*mDisplayColorProfile,
- getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _))
- .WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN),
- SetArgPointee<3>(ui::ColorMode::NATIVE),
- SetArgPointee<4>(ui::RenderIntent::COLORIMETRIC)));
- mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- }
-
- struct IfColorSpaceAgnosticDataspaceSetToState
- : public CallOrderStateMachineHelper<TestType, IfColorSpaceAgnosticDataspaceSetToState> {
- [[nodiscard]] auto ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace dataspace) {
- getInstance()->mRefreshArgs.colorSpaceAgnosticDataspace = dataspace;
- return nextState<ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState>();
- }
- };
-
- struct ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState
- : public CallOrderStateMachineHelper<
- TestType, ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState> {
- [[nodiscard]] auto thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(
- ui::Dataspace dataspace) {
- EXPECT_CALL(getInstance()->mOutput,
- setColorProfile(ColorProfileEq(
- ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, dataspace})));
- return nextState<ExecuteState>();
- }
- };
-
- // Call this member function to start using the mini-DSL defined above.
- [[nodiscard]] auto verify() { return IfColorSpaceAgnosticDataspaceSetToState::make(this); }
-};
-
-TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, DisplayP3) {
- verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::DISPLAY_P3)
- .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::DISPLAY_P3)
- .execute();
-}
-
-TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, V0_SRGB) {
- verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::V0_SRGB)
- .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::V0_SRGB)
- .execute();
-}
-
struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference
: public OutputUpdateColorProfileTest {
// Internally the implementation looks through the dataspaces of all the
@@ -2248,7 +2180,6 @@
OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
@@ -2368,7 +2299,6 @@
OutputUpdateColorProfileTest_ForceOutputColorOverrides() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020;
@@ -2424,7 +2354,6 @@
struct OutputUpdateColorProfileTest_Hdr : public OutputUpdateColorProfileTest {
OutputUpdateColorProfileTest_Hdr() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
}
@@ -2703,7 +2632,6 @@
OutputUpdateColorProfile_AffectsChosenRenderIntentTest() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
index 6fc90fe..494a9f4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
@@ -32,9 +32,9 @@
public:
TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {}
- size_t getMinPoolSize() const { return kMinPoolSize; }
size_t getMaxPoolSize() const { return kMaxPoolSize; }
size_t getPoolSize() const { return mPool.size(); }
+ size_t isGenTextureFutureValid() const { return mGenTextureFuture.valid(); }
};
struct TexturePoolTest : public testing::Test {
@@ -56,16 +56,8 @@
TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine);
};
-TEST_F(TexturePoolTest, preallocatesMinPool) {
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
-}
-
-TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) {
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) {
- auto texture = mTexturePool.borrowTexture();
- }
-
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+TEST_F(TexturePoolTest, preallocatesZeroSizePool) {
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) {
@@ -119,10 +111,10 @@
static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
mTexturePool.setDisplaySize(kDisplaySizeTwo);
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
texture.reset();
// When the texture is returned to the pool, the pool now destroys it.
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
texture = mTexturePool.borrowTexture();
EXPECT_EQ(kDisplaySizeTwo.getWidth(),
@@ -132,14 +124,11 @@
}
TEST_F(TexturePoolTest, freesBuffersWhenDisabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
-
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+ for (size_t i = 0; i < 2; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
- EXPECT_EQ(mTexturePool.getPoolSize(), 1u);
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
@@ -148,12 +137,11 @@
}
TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+ for (size_t i = 0; i < 2; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
@@ -162,12 +150,13 @@
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
-TEST_F(TexturePoolTest, reallocatesWhenReEnabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+TEST_F(TexturePoolTest, genFutureWhenReEnabled) {
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_FALSE(mTexturePool.isGenTextureFutureValid());
mTexturePool.setEnabled(true);
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_TRUE(mTexturePool.isGenTextureFutureValid());
}
} // namespace
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f6ca9e2..32bd890 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -37,11 +37,11 @@
#include <configstore/Utils.h>
#include <log/log.h>
#include <system/window.h>
-#include <ui/GraphicTypes.h>
#include "Display/DisplaySnapshot.h"
#include "DisplayDevice.h"
#include "FrontEnd/DisplayInfo.h"
+#include "HdrSdrRatioOverlay.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
#include "SurfaceFlinger.h"
@@ -261,6 +261,9 @@
if (mRefreshRateOverlay) {
mRefreshRateOverlay->setLayerStack(filter.layerStack);
}
+ if (mHdrSdrRatioOverlay) {
+ mHdrSdrRatioOverlay->setLayerStack(filter.layerStack);
+ }
}
void DisplayDevice::setFlags(uint32_t flags) {
@@ -274,10 +277,14 @@
if (mRefreshRateOverlay) {
mRefreshRateOverlay->setViewport(size);
}
+ if (mHdrSdrRatioOverlay) {
+ mHdrSdrRatioOverlay->setViewport(size);
+ }
}
void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
Rect orientedDisplaySpaceRect) {
+ mIsOrientationChanged = mOrientation != orientation;
mOrientation = orientation;
// We need to take care of display rotation for globalTransform for case if the panel is not
@@ -411,6 +418,26 @@
capabilities.getDesiredMinLuminance());
}
+void DisplayDevice::enableHdrSdrRatioOverlay(bool enable) {
+ if (!enable) {
+ mHdrSdrRatioOverlay.reset();
+ return;
+ }
+
+ mHdrSdrRatioOverlay = std::make_unique<HdrSdrRatioOverlay>();
+ mHdrSdrRatioOverlay->setLayerStack(getLayerStack());
+ mHdrSdrRatioOverlay->setViewport(getSize());
+ updateHdrSdrRatioOverlayRatio(mHdrSdrRatio);
+}
+
+void DisplayDevice::updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio) {
+ ATRACE_CALL();
+ mHdrSdrRatio = currentHdrSdrRatio;
+ if (mHdrSdrRatioOverlay) {
+ mHdrSdrRatioOverlay->changeHdrSdrRatio(currentHdrSdrRatio);
+ }
+}
+
void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
bool showRenderRate, bool showInMiddle) {
if (!enable) {
@@ -463,10 +490,23 @@
return false;
}
-void DisplayDevice::animateRefreshRateOverlay() {
+void DisplayDevice::animateOverlay() {
if (mRefreshRateOverlay) {
mRefreshRateOverlay->animate();
}
+ if (mHdrSdrRatioOverlay) {
+ // hdr sdr ratio is designed to be on the top right of the screen,
+ // therefore, we need to re-calculate the display's width and height
+ if (mIsOrientationChanged) {
+ auto width = getWidth();
+ auto height = getHeight();
+ if (mOrientation == ui::ROTATION_90 || mOrientation == ui::ROTATION_270) {
+ std::swap(width, height);
+ }
+ mHdrSdrRatioOverlay->setViewport({width, height});
+ }
+ mHdrSdrRatioOverlay->animate();
+ }
}
auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index dc5f8a8..e92125a 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -55,6 +55,7 @@
class Fence;
class HWComposer;
+class HdrSdrRatioOverlay;
class IGraphicBufferProducer;
class Layer;
class RefreshRateOverlay;
@@ -235,13 +236,19 @@
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);
void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
- void animateRefreshRateOverlay();
+
+ // Enables an overlay to be display with the hdr/sdr ratio
+ void enableHdrSdrRatioOverlay(bool enable) REQUIRES(kMainThreadContext);
+ void updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio);
+ bool isHdrSdrRatioOverlayEnabled() const { return mHdrSdrRatioOverlay != nullptr; }
nsecs_t getVsyncPeriodFromHWC() const;
@@ -271,6 +278,7 @@
const ui::Rotation mPhysicalOrientation;
ui::Rotation mOrientation = ui::ROTATION_0;
+ bool mIsOrientationChanged = false;
// Allow nullopt as initial power mode.
using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>;
@@ -297,6 +305,9 @@
std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+ std::unique_ptr<HdrSdrRatioOverlay> mHdrSdrRatioOverlay;
+ // This parameter is only used for hdr/sdr ratio overlay
+ float mHdrSdrRatio = 1.0f;
mutable std::mutex mActiveModeLock;
ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index f7049b9..1f409c6 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -20,6 +20,7 @@
#include "AidlComposerHal.h"
+#include <SurfaceFlingerProperties.h>
#include <android-base/file.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
@@ -243,21 +244,23 @@
addReader(translate<Display>(kSingleReaderKey));
// If unable to read interface version, then become backwards compatible.
- int32_t version = 1;
- const auto status = mAidlComposerClient->getInterfaceVersion(&version);
+ const auto status = mAidlComposerClient->getInterfaceVersion(&mComposerInterfaceVersion);
if (!status.isOk()) {
ALOGE("getInterfaceVersion for AidlComposer constructor failed %s",
status.getDescription().c_str());
}
- if (version == 1) {
- mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
- GraphicBuffer::USAGE_HW_COMPOSER |
- GraphicBuffer::USAGE_SW_READ_OFTEN |
- GraphicBuffer::USAGE_SW_WRITE_OFTEN,
- "AidlComposer");
- if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) {
- LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
- return;
+
+ if (mComposerInterfaceVersion <= 1) {
+ if (sysprop::clear_slots_with_set_layer_buffer(false)) {
+ mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_SW_READ_OFTEN |
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ "AidlComposer");
+ if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) {
+ LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
+ return;
+ }
}
}
@@ -277,6 +280,10 @@
}
}
+bool AidlComposer::getDisplayConfigurationsSupported() const {
+ return mComposerInterfaceVersion >= 3;
+}
+
std::vector<Capability> AidlComposer::getCapabilities() {
std::vector<Capability> capabilities;
const auto status = mAidlComposer->getCapabilities(&capabilities);
@@ -485,6 +492,19 @@
return Error::NONE;
}
+Error AidlComposer::getDisplayConfigurations(Display display, int32_t maxFrameIntervalNs,
+ std::vector<DisplayConfiguration>* outConfigs) {
+ const auto status =
+ mAidlComposerClient->getDisplayConfigurations(translate<int64_t>(display),
+ maxFrameIntervalNs, outConfigs);
+ if (!status.isOk()) {
+ ALOGE("getDisplayConfigurations failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ return Error::NONE;
+}
+
Error AidlComposer::getDisplayName(Display display, std::string* outName) {
const auto status = mAidlComposerClient->getDisplayName(translate<int64_t>(display), outName);
if (!status.isOk()) {
@@ -844,12 +864,12 @@
Error error = Error::NONE;
mMutex.lock_shared();
if (auto writer = getWriter(display)) {
- // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
- // buffer, using the slot that needs to cleared... tricky.
- if (mClearSlotBuffer == nullptr) {
+ if (mComposerInterfaceVersion > 1) {
writer->get().setLayerBufferSlotsToClear(translate<int64_t>(display),
translate<int64_t>(layer), slotsToClear);
- } else {
+ // Backwards compatible way of clearing buffer slots is to set the layer buffer with a
+ // placeholder buffer, using the slot that needs to cleared... tricky.
+ } else if (mClearSlotBuffer != nullptr) {
for (uint32_t slot : slotsToClear) {
// Don't clear the active buffer slot because we need to restore the active buffer
// after clearing the requested buffer slots with a placeholder buffer.
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index ded91be..b1b57a4 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -66,6 +66,7 @@
~AidlComposer() override;
bool isSupported(OptionalFeature) const;
+ bool getDisplayConfigurationsSupported() const;
std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
override;
@@ -95,6 +96,8 @@
Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
int32_t* outValue) override;
Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+ Error getDisplayConfigurations(Display, int32_t maxFrameIntervalNs,
+ std::vector<DisplayConfiguration>*);
Error getDisplayName(Display display, std::string* outName) override;
Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
@@ -285,6 +288,8 @@
// threading annotations.
ftl::SharedMutex mMutex;
+ int32_t mComposerInterfaceVersion = 1;
+
// Buffer slots for layers are cleared by setting the slot buffer to this buffer.
sp<GraphicBuffer> mClearSlotBuffer;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index cf67795..e942587 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -39,6 +39,7 @@
#include <aidl/android/hardware/graphics/composer3/Color.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
@@ -85,6 +86,7 @@
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using DisplayConfiguration = V3_0::DisplayConfiguration;
using aidl::android::hardware::graphics::common::Hdr;
class Composer {
@@ -103,6 +105,7 @@
};
virtual bool isSupported(OptionalFeature) const = 0;
+ virtual bool getDisplayConfigurationsSupported() const = 0;
virtual std::vector<aidl::android::hardware::graphics::composer3::Capability>
getCapabilities() = 0;
@@ -130,6 +133,10 @@
virtual Error getDisplayAttribute(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) = 0;
virtual Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs) = 0;
+
+ virtual Error getDisplayConfigurations(Display, int32_t maxFrameIntervalNs,
+ std::vector<DisplayConfiguration>*) = 0;
+
virtual Error getDisplayName(Display display, std::string* outName) = 0;
virtual Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 61a9a08..1810925 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -80,20 +80,20 @@
return *this;
}
- Builder& setDpiX(int32_t dpiX) {
- if (dpiX == -1) {
+ Builder& setDpiX(float dpiX) {
+ if (dpiX == -1.f) {
mDisplayMode->mDpi.x = getDefaultDensity();
} else {
- mDisplayMode->mDpi.x = dpiX / 1000.f;
+ mDisplayMode->mDpi.x = dpiX;
}
return *this;
}
- Builder& setDpiY(int32_t dpiY) {
- if (dpiY == -1) {
+ Builder& setDpiY(float dpiY) {
+ if (dpiY == -1.f) {
mDisplayMode->mDpi.y = getDefaultDensity();
} else {
- mDisplayMode->mDpi.y = dpiY / 1000.f;
+ mDisplayMode->mDpi.y = dpiY;
}
return *this;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index f350eba..3177b33 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -261,10 +261,51 @@
return mDisplayData.count(displayId) && mDisplayData.at(displayId).hwcDisplay->isConnected();
}
-std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const {
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId,
+ int32_t maxFrameIntervalNs) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId();
+
+ if (mComposer->getDisplayConfigurationsSupported()) {
+ return getModesFromDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs);
+ }
+
+ return getModesFromLegacyDisplayConfigs(hwcDisplayId);
+}
+
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigurations(
+ uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const {
+ std::vector<hal::DisplayConfiguration> configs;
+ auto error = static_cast<hal::Error>(
+ mComposer->getDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs, &configs));
+ RETURN_IF_HWC_ERROR_FOR("getDisplayConfigurations", error, *toPhysicalDisplayId(hwcDisplayId),
+ {});
+
+ std::vector<HWCDisplayMode> modes;
+ modes.reserve(configs.size());
+ for (auto config : configs) {
+ auto hwcMode = HWCDisplayMode{
+ .hwcId = static_cast<hal::HWConfigId>(config.configId),
+ .width = config.width,
+ .height = config.height,
+ .vsyncPeriod = config.vsyncPeriod,
+ .configGroup = config.configGroup,
+ };
+
+ if (config.dpi) {
+ hwcMode.dpiX = config.dpi->x;
+ hwcMode.dpiY = config.dpi->y;
+ }
+
+ modes.push_back(hwcMode);
+ }
+
+ return modes;
+}
+
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayConfigs(
+ uint64_t hwcDisplayId) const {
std::vector<hal::HWConfigId> configIds;
auto error = static_cast<hal::Error>(mComposer->getDisplayConfigs(hwcDisplayId, &configIds));
RETURN_IF_HWC_ERROR_FOR("getDisplayConfigs", error, *toPhysicalDisplayId(hwcDisplayId), {});
@@ -272,17 +313,25 @@
std::vector<HWCDisplayMode> modes;
modes.reserve(configIds.size());
for (auto configId : configIds) {
- modes.push_back(HWCDisplayMode{
+ auto hwcMode = HWCDisplayMode{
.hwcId = configId,
.width = getAttribute(hwcDisplayId, configId, hal::Attribute::WIDTH),
.height = getAttribute(hwcDisplayId, configId, hal::Attribute::HEIGHT),
.vsyncPeriod = getAttribute(hwcDisplayId, configId, hal::Attribute::VSYNC_PERIOD),
- .dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X),
- .dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y),
.configGroup = getAttribute(hwcDisplayId, configId, hal::Attribute::CONFIG_GROUP),
- });
- }
+ };
+ const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X);
+ const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y);
+ if (dpiX != -1) {
+ hwcMode.dpiX = static_cast<float>(dpiX) / 1000.f;
+ }
+ if (dpiY != -1) {
+ hwcMode.dpiY = static_cast<float>(dpiY) / 1000.f;
+ }
+
+ modes.push_back(hwcMode);
+ }
return modes;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 3702c62..86f3825 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -99,8 +99,8 @@
int32_t width = -1;
int32_t height = -1;
nsecs_t vsyncPeriod = -1;
- int32_t dpiX = -1;
- int32_t dpiY = -1;
+ float dpiX = -1.f;
+ float dpiY = -1.f;
int32_t configGroup = -1;
friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
@@ -229,7 +229,8 @@
virtual bool isConnected(PhysicalDisplayId) const = 0;
- virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId) const = 0;
+ virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId,
+ int32_t maxFrameIntervalNs) const = 0;
virtual std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const = 0;
@@ -412,7 +413,8 @@
bool isConnected(PhysicalDisplayId) const override;
- std::vector<HWCDisplayMode> getModes(PhysicalDisplayId) const override;
+ std::vector<HWCDisplayMode> getModes(PhysicalDisplayId,
+ int32_t maxFrameIntervalNs) const override;
std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const override;
@@ -501,6 +503,10 @@
std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
+ std::vector<HWCDisplayMode> getModesFromDisplayConfigurations(uint64_t hwcDisplayId,
+ int32_t maxFrameIntervalNs) const;
+ std::vector<HWCDisplayMode> getModesFromLegacyDisplayConfigs(uint64_t hwcDisplayId) const;
+
int32_t getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
hal::Attribute attribute) const;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index bf3089f..e95ae89 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
#define ERROR_HAS_CHANGES 5
@@ -34,6 +35,7 @@
namespace V2_2 = android::hardware::graphics::composer::V2_2;
namespace V2_3 = android::hardware::graphics::composer::V2_3;
namespace V2_4 = android::hardware::graphics::composer::V2_4;
+namespace V3_0 = ::aidl::android::hardware::graphics::composer3;
using types::V1_0::ColorTransform;
using types::V1_0::Transform;
@@ -70,6 +72,7 @@
using Vsync = IComposerClient::Vsync;
using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
using Hdr = aidl::android::hardware::graphics::common::Hdr;
+using DisplayConfiguration = V3_0::DisplayConfiguration;
} // namespace hardware::graphics::composer::hal
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index e0f6c45..70d48de 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -24,12 +24,14 @@
#include "HidlComposerHal.h"
+#include <SurfaceFlingerProperties.h>
#include <android/binder_manager.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
+
#include "HWC2.h"
#include "Hal.h"
@@ -189,6 +191,9 @@
}
sp<GraphicBuffer> allocateClearSlotBuffer() {
+ if (!sysprop::clear_slots_with_set_layer_buffer(false)) {
+ return nullptr;
+ }
sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
GraphicBuffer::USAGE_HW_COMPOSER |
GraphicBuffer::USAGE_SW_READ_OFTEN |
@@ -246,7 +251,7 @@
LOG_ALWAYS_FATAL("failed to create composer client");
}
- if (!mClearSlotBuffer) {
+ if (!mClearSlotBuffer && sysprop::clear_slots_with_set_layer_buffer(false)) {
LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
return;
}
@@ -264,6 +269,11 @@
}
}
+bool HidlComposer::getDisplayConfigurationsSupported() const {
+ // getDisplayConfigurations is not supported on the HIDL composer.
+ return false;
+};
+
std::vector<Capability> HidlComposer::getCapabilities() {
std::vector<Capability> capabilities;
mComposer->getCapabilities([&](const auto& tmpCapabilities) {
@@ -472,6 +482,12 @@
return error;
}
+Error HidlComposer::getDisplayConfigurations(Display, int32_t /*maxFrameIntervalNs*/,
+ std::vector<DisplayConfiguration>*) {
+ LOG_ALWAYS_FATAL("getDisplayConfigurations should not have been called on this, as "
+ "it's a HWC3 interface version 3 feature");
+}
+
Error HidlComposer::getDisplayName(Display display, std::string* outName) {
Error error = kDefaultError;
mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
@@ -716,7 +732,11 @@
if (slotsToClear.empty()) {
return Error::NONE;
}
- // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
+ // This can be null when the HAL hasn't explicitly enabled this feature.
+ if (mClearSlotBuffer == nullptr) {
+ return Error::NONE;
+ }
+ // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
// buffer, using the slot that needs to cleared... tricky.
for (uint32_t slot : slotsToClear) {
// Don't clear the active buffer slot because we need to restore the active buffer after
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 0521acf..26d2222 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -167,6 +167,7 @@
~HidlComposer() override;
bool isSupported(OptionalFeature) const;
+ bool getDisplayConfigurationsSupported() const;
std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
override;
@@ -196,6 +197,8 @@
Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
int32_t* outValue) override;
Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+ Error getDisplayConfigurations(Display, int32_t maxFrameIntervalNs,
+ std::vector<DisplayConfiguration>*);
Error getDisplayName(Display display, std::string* outName) override;
Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 37b68c8..f00ef67 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -31,9 +31,9 @@
#include <utils/Mutex.h>
#include <utils/Trace.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/WorkDuration.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
#include <binder/IServiceManager.h>
@@ -49,11 +49,11 @@
namespace impl {
-using android::hardware::power::Boost;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
-using android::hardware::power::SessionHint;
-using android::hardware::power::WorkDuration;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
+using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::WorkDuration;
PowerAdvisor::~PowerAdvisor() = default;
@@ -138,6 +138,21 @@
}
}
+void PowerAdvisor::notifyCpuLoadUp() {
+ // Only start sending this notification once the system has booted so we don't introduce an
+ // early-boot dependency on Power HAL
+ if (!mBootFinished.load()) {
+ return;
+ }
+ if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+ std::lock_guard lock(mHintSessionMutex);
+ auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
+ if (!ret.isOk()) {
+ mHintSessionRunning = false;
+ }
+ }
+}
+
void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
// Only start sending this notification once the system has booted so we don't introduce an
// early-boot dependency on Power HAL
@@ -215,7 +230,7 @@
auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
- ret.exceptionMessage().c_str());
+ ret.getDescription().c_str());
mHintSessionRunning = false;
}
}
@@ -223,7 +238,7 @@
}
void PowerAdvisor::reportActualWorkDuration() {
- if (!mBootFinished || !usePowerHintSession()) {
+ if (!mBootFinished || !sUseReportActualDuration || !usePowerHintSession()) {
ALOGV("Actual work duration power hint cannot be sent, skipping");
return;
}
@@ -259,7 +274,7 @@
auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
if (!ret.isOk()) {
ALOGW("Failed to report actual work durations with error: %s",
- ret.exceptionMessage().c_str());
+ ret.getDescription().c_str());
mHintSessionRunning = false;
return;
}
@@ -564,6 +579,9 @@
base::GetIntProperty<int64_t>("debug.sf.hint_margin_us",
ticks<std::micro>(PowerAdvisor::kDefaultTargetSafetyMargin)));
+const bool PowerAdvisor::sUseReportActualDuration =
+ base::GetBoolProperty(std::string("debug.adpf.use_report_actual_duration"), true);
+
power::PowerHalController& PowerAdvisor::getPowerHal() {
static std::once_flag halFlag;
std::call_once(halFlag, [this] { mPowerHal->init(); });
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 7a0d426..05e4c8b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -25,7 +25,7 @@
#include <ui/FenceTime.h>
#include <utils/Mutex.h>
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <powermanager/PowerHalController.h>
#include <scheduler/Time.h>
@@ -49,6 +49,7 @@
virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
+ virtual void notifyCpuLoadUp() = 0;
virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
// Checks both if it supports and if it's enabled
virtual bool usePowerHintSession() = 0;
@@ -108,6 +109,7 @@
void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
+ void notifyCpuLoadUp() override;
void notifyDisplayUpdateImminentAndCpuReset() override;
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
@@ -245,13 +247,14 @@
bool mHintSessionRunning = false;
std::mutex mHintSessionMutex;
- sp<hardware::power::IPowerHintSession> mHintSession GUARDED_BY(mHintSessionMutex) = nullptr;
+ std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession
+ GUARDED_BY(mHintSessionMutex) = nullptr;
// Initialize to true so we try to call, to check if it's supported
bool mHasExpensiveRendering = true;
bool mHasDisplayUpdateImminent = true;
// Queue of actual durations saved to report
- std::vector<hardware::power::WorkDuration> mHintSessionQueue;
+ std::vector<aidl::android::hardware::power::WorkDuration> mHintSessionQueue;
// The latest values we have received for target and actual
Duration mTargetDuration = kDefaultTargetDuration;
std::optional<Duration> mActualDuration;
@@ -269,6 +272,9 @@
static const Duration sTargetSafetyMargin;
static constexpr const Duration kDefaultTargetSafetyMargin{1ms};
+ // Whether we should send reportActualWorkDuration calls
+ static const bool sUseReportActualDuration;
+
// How long we expect hwc to run after the present call until it waits for the fence
static constexpr const Duration kFenceWaitStartDelayValidated{150us};
static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us};
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 5913d4b..ab4c15d 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -16,7 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "LayerHierarchy"
+#define LOG_TAG "SurfaceFlinger"
#include "LayerHierarchy.h"
#include "LayerLog.h"
@@ -149,13 +149,33 @@
return debug + "}";
}
-std::string LayerHierarchy::getDebugString(const char* prefix) const {
- std::string debug = prefix + getDebugStringShort();
- for (auto& [child, childVariant] : mChildren) {
- std::string childPrefix = " " + std::string(prefix) + " " + std::to_string(childVariant);
- debug += "\n" + child->getDebugString(childPrefix.c_str());
+void LayerHierarchy::dump(std::ostream& out, const std::string& prefix,
+ LayerHierarchy::Variant variant, bool isLastChild) const {
+ if (!mLayer) {
+ out << " ROOT";
+ } else {
+ out << prefix + (isLastChild ? "└─ " : "├─ ");
+ if (variant == LayerHierarchy::Variant::Relative) {
+ out << "(Relative) ";
+ } else if (variant == LayerHierarchy::Variant::Mirror) {
+ out << "(Mirroring) " << *mLayer << "\n" + prefix + " └─ ...";
+ return;
+ }
+ out << *mLayer;
}
- return debug;
+
+ for (size_t i = 0; i < mChildren.size(); i++) {
+ auto& [child, childVariant] = mChildren[i];
+ if (childVariant == LayerHierarchy::Variant::Detached) continue;
+ const bool lastChild = i == (mChildren.size() - 1);
+ std::string childPrefix = prefix;
+ if (mLayer) {
+ childPrefix += (isLastChild ? " " : "│ ");
+ }
+ out << "\n";
+ child->dump(out, childPrefix, childVariant, lastChild);
+ }
+ return;
}
bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index b25b731..1e48387 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -42,10 +42,10 @@
class LayerHierarchy {
public:
enum Variant : uint32_t {
- Attached,
- Detached,
- Relative,
- Mirror,
+ Attached, // child of the parent
+ Detached, // child of the parent but currently relative parented to another layer
+ Relative, // relative child of the parent
+ Mirror, // mirrored from another layer
ftl_first = Attached,
ftl_last = Mirror,
};
@@ -156,7 +156,12 @@
const RequestedLayerState* getLayer() const;
const LayerHierarchy* getRelativeParent() const;
const LayerHierarchy* getParent() const;
- std::string getDebugString(const char* prefix = "") const;
+ friend std::ostream& operator<<(std::ostream& os, const LayerHierarchy& obj) {
+ std::string prefix = " ";
+ obj.dump(os, prefix, LayerHierarchy::Variant::Attached, /*isLastChild=*/false);
+ return os;
+ }
+
std::string getDebugStringShort() const;
// Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
// will contain the first relative root that was visited twice in a traversal.
@@ -172,6 +177,8 @@
void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+ void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant,
+ bool isLastChild) const;
const RequestedLayerState* mLayer;
LayerHierarchy* mParent = nullptr;
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index cd9515c..1712137 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -17,7 +17,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "LayerLifecycleManager"
+#define LOG_TAG "SurfaceFlinger"
#include "LayerLifecycleManager.h"
#include "Client.h" // temporarily needed for LayerCreationArgs
@@ -28,6 +28,14 @@
using namespace ftl::flag_operators;
+namespace {
+// Returns true if the layer is root of a display and can be mirrored by mirroringLayer
+bool canMirrorRootLayer(RequestedLayerState& mirroringLayer, RequestedLayerState& rootLayer) {
+ return rootLayer.isRoot() && rootLayer.layerStack == mirroringLayer.layerStackToMirror &&
+ rootLayer.id != mirroringLayer.id;
+}
+} // namespace
+
void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayerState>> newLayers) {
if (newLayers.empty()) {
return;
@@ -43,14 +51,19 @@
it->second.owner.getDebugString().c_str());
}
mAddedLayers.push_back(newLayer.get());
+ mChangedLayers.push_back(newLayer.get());
layer.parentId = linkLayer(layer.parentId, layer.id);
layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+ // Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a
+ // display accidentally.
+ layer.layerStack = ui::INVALID_LAYER_STACK;
+
// if this layer is mirroring a display, then walk though all the existing root layers
// for the layer stack and add them as children to be mirrored.
mDisplayMirroringLayers.emplace_back(layer.id);
for (auto& rootLayer : mLayers) {
- if (rootLayer->isRoot() && rootLayer->layerStack == layer.layerStackToMirror) {
+ if (canMirrorRootLayer(layer, *rootLayer)) {
layer.mirrorIds.emplace_back(rootLayer->id);
linkLayer(rootLayer->id, layer.id);
}
@@ -190,6 +203,10 @@
continue;
}
+ if (layer->changes.get() == 0) {
+ mChangedLayers.push_back(layer);
+ }
+
if (transaction.flags & ISurfaceComposer::eAnimation) {
layer->changes |= RequestedLayerState::Changes::Animation;
}
@@ -232,6 +249,7 @@
bgColorLayer->what |= layer_state_t::eColorChanged |
layer_state_t::eDataspaceChanged | layer_state_t::eAlphaChanged;
bgColorLayer->changes |= RequestedLayerState::Changes::Content;
+ mChangedLayers.push_back(bgColorLayer);
mGlobalChanges |= RequestedLayerState::Changes::Content;
}
}
@@ -278,6 +296,7 @@
}
}
mDestroyedLayers.clear();
+ mChangedLayers.clear();
mGlobalChanges.clear();
}
@@ -298,10 +317,25 @@
return mDestroyedLayers;
}
+const std::vector<RequestedLayerState*>& LayerLifecycleManager::getChangedLayers() const {
+ return mChangedLayers;
+}
+
const ftl::Flags<RequestedLayerState::Changes> LayerLifecycleManager::getGlobalChanges() const {
return mGlobalChanges;
}
+const RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) const {
+ if (id == UNASSIGNED_LAYER_ID) {
+ return nullptr;
+ }
+ auto it = mIdToLayer.find(id);
+ if (it == mIdToLayer.end()) {
+ return nullptr;
+ }
+ return &it->second.owner;
+}
+
RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) {
if (id == UNASSIGNED_LAYER_ID) {
return nullptr;
@@ -383,10 +417,9 @@
// and updates its list of layers that its mirroring. This function should be called when a new
// root layer is added, removed or moved to another display.
void LayerLifecycleManager::updateDisplayMirrorLayers(RequestedLayerState& rootLayer) {
- for (uint32_t mirrorLayerId : mDisplayMirroringLayers) {
- RequestedLayerState* mirrorLayer = getLayerFromId(mirrorLayerId);
- bool canBeMirrored =
- rootLayer.isRoot() && rootLayer.layerStack == mirrorLayer->layerStackToMirror;
+ for (uint32_t mirroringLayerId : mDisplayMirroringLayers) {
+ RequestedLayerState* mirrorLayer = getLayerFromId(mirroringLayerId);
+ bool canBeMirrored = canMirrorRootLayer(*mirrorLayer, rootLayer);
bool currentlyMirrored =
std::find(mirrorLayer->mirrorIds.begin(), mirrorLayer->mirrorIds.end(),
rootLayer.id) != mirrorLayer->mirrorIds.end();
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index f0d2c22..48571bf 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -76,7 +76,9 @@
void removeLifecycleListener(std::shared_ptr<ILifecycleListener>);
const std::vector<std::unique_ptr<RequestedLayerState>>& getLayers() const;
const std::vector<std::unique_ptr<RequestedLayerState>>& getDestroyedLayers() const;
+ const std::vector<RequestedLayerState*>& getChangedLayers() const;
const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const;
+ const RequestedLayerState* getLayerFromId(uint32_t) const;
private:
friend class LayerLifecycleManagerTest;
@@ -111,6 +113,8 @@
// Keeps track of all the layers that were added in order. Changes will be cleared once
// committed.
std::vector<RequestedLayerState*> mAddedLayers;
+ // Keeps track of new and layers with states changes since last commit.
+ std::vector<RequestedLayerState*> mChangedLayers;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index a992584..d389a79 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -16,7 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "LayerSnapshot"
+#define LOG_TAG "SurfaceFlinger"
#include "LayerSnapshot.h"
@@ -24,6 +24,80 @@
using namespace ftl::flag_operators;
+namespace {
+
+void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
+ bool forceFullDamage, Region& outSurfaceDamageRegion) {
+ if (!hasReadyFrame) {
+ outSurfaceDamageRegion.clear();
+ return;
+ }
+ if (forceFullDamage) {
+ outSurfaceDamageRegion = Region::INVALID_REGION;
+ } else {
+ outSurfaceDamageRegion = requested.surfaceDamageRegion;
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const ui::Transform& transform) {
+ const uint32_t type = transform.getType();
+ const uint32_t orientation = transform.getOrientation();
+ if (type == ui::Transform::IDENTITY) {
+ return os;
+ }
+
+ if (type & ui::Transform::UNKNOWN) {
+ std::string out;
+ transform.dump(out, "", "");
+ os << out;
+ return os;
+ }
+
+ if (type & ui::Transform::ROTATE) {
+ switch (orientation) {
+ case ui::Transform::ROT_0:
+ os << "ROT_0";
+ break;
+ case ui::Transform::FLIP_H:
+ os << "FLIP_H";
+ break;
+ case ui::Transform::FLIP_V:
+ os << "FLIP_V";
+ break;
+ case ui::Transform::ROT_90:
+ os << "ROT_90";
+ break;
+ case ui::Transform::ROT_180:
+ os << "ROT_180";
+ break;
+ case ui::Transform::ROT_270:
+ os << "ROT_270";
+ break;
+ case ui::Transform::ROT_INVALID:
+ default:
+ os << "ROT_INVALID";
+ break;
+ }
+ }
+
+ if (type & ui::Transform::SCALE) {
+ std::string out;
+ android::base::StringAppendF(&out, " scale x=%.4f y=%.4f ", transform.getScaleX(),
+ transform.getScaleY());
+ os << out;
+ }
+
+ if (type & ui::Transform::TRANSLATE) {
+ std::string out;
+ android::base::StringAppendF(&out, " tx=%.4f ty=%.4f ", transform.tx(), transform.ty());
+ os << out;
+ }
+
+ return os;
+}
+
+} // namespace
+
LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
const LayerHierarchy::TraversalPath& path)
: path(path) {
@@ -42,18 +116,23 @@
}
sequence = static_cast<int32_t>(state.id);
name = state.name;
+ debugName = state.debugName;
textureName = state.textureName;
premultipliedAlpha = state.premultipliedAlpha;
inputInfo.name = state.name;
inputInfo.id = static_cast<int32_t>(uniqueSequence);
- inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid);
- inputInfo.ownerPid = state.ownerPid;
+ inputInfo.ownerUid = gui::Uid{state.ownerUid};
+ inputInfo.ownerPid = gui::Pid{state.ownerPid};
uid = state.ownerUid;
pid = state.ownerPid;
changes = RequestedLayerState::Changes::Created;
+ clientChanges = 0;
mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror
? path
: LayerHierarchy::TraversalPath::ROOT;
+ reachablilty = LayerSnapshot::Reachablilty::Unreachable;
+ frameRateSelectionPriority = state.frameRateSelectionPriority;
+ layerMetadata = state.metadata;
}
// As documented in libhardware header, formats in the range
@@ -131,6 +210,10 @@
}
bool LayerSnapshot::getIsVisible() const {
+ if (reachablilty != LayerSnapshot::Reachablilty::Reachable) {
+ return false;
+ }
+
if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) {
return false;
}
@@ -148,18 +231,22 @@
std::string LayerSnapshot::getIsVisibleReason() const {
// not visible
- if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
- if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
- if (invalidTransform) return "invalidTransform";
+ if (reachablilty == LayerSnapshot::Reachablilty::Unreachable)
+ return "layer not reachable from root";
+ if (reachablilty == LayerSnapshot::Reachablilty::ReachableByRelativeParent)
+ return "layer only reachable via relative parent";
if (isHiddenByPolicyFromParent) return "hidden by parent or layer flag";
if (isHiddenByPolicyFromRelativeParent) return "hidden by relative parent";
+ if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
+ if (invalidTransform) return "invalidTransform";
if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur";
+ if (!hasSomethingToDraw()) return "nothing to draw";
// visible
std::stringstream reason;
if (sidebandStream != nullptr) reason << " sidebandStream";
if (externalTexture != nullptr)
- reason << " buffer:" << externalTexture->getId() << " frame:" << frameNumber;
+ reason << " buffer=" << externalTexture->getId() << " frame=" << frameNumber;
if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}";
if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
@@ -177,8 +264,9 @@
}
bool LayerSnapshot::hasInputInfo() const {
- return inputInfo.token != nullptr ||
- inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+ return (inputInfo.token != nullptr ||
+ inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) &&
+ reachablilty == Reachablilty::Reachable;
}
std::string LayerSnapshot::getDebugString() const {
@@ -191,11 +279,49 @@
<< " geomLayerTransform={tx=" << geomLayerTransform.tx()
<< ",ty=" << geomLayerTransform.ty() << "}"
<< "}";
- debug << " input{ touchCropId=" << touchCropId
- << " replaceTouchableRegionWithCrop=" << inputInfo.replaceTouchableRegionWithCrop << "}";
+ if (hasInputInfo()) {
+ debug << " input{"
+ << "(" << inputInfo.inputConfig.string() << ")";
+ if (touchCropId != UNASSIGNED_LAYER_ID) debug << " touchCropId=" << touchCropId;
+ if (inputInfo.replaceTouchableRegionWithCrop) debug << " replaceTouchableRegionWithCrop";
+ auto touchableRegion = inputInfo.touchableRegion.getBounds();
+ debug << " touchableRegion={" << touchableRegion.left << "," << touchableRegion.top << ","
+ << touchableRegion.bottom << "," << touchableRegion.right << "}"
+ << "}";
+ }
return debug.str();
}
+std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) {
+ out << "Layer [" << obj.path.id;
+ if (obj.path.mirrorRootId != UNASSIGNED_LAYER_ID) {
+ out << " mirrored from " << obj.path.mirrorRootId;
+ }
+ out << "] " << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible")
+ << " reason=" << obj.getIsVisibleReason();
+
+ if (!obj.geomLayerBounds.isEmpty()) {
+ out << "\n bounds={" << obj.transformedBounds.left << "," << obj.transformedBounds.top
+ << "," << obj.transformedBounds.bottom << "," << obj.transformedBounds.right << "}";
+ }
+
+ if (obj.geomLayerTransform.getType() != ui::Transform::IDENTITY) {
+ out << " toDisplayTransform={" << obj.geomLayerTransform << "}";
+ }
+
+ if (obj.hasInputInfo()) {
+ out << "\n input{"
+ << "(" << obj.inputInfo.inputConfig.string() << ")";
+ if (obj.touchCropId != UNASSIGNED_LAYER_ID) out << " touchCropId=" << obj.touchCropId;
+ if (obj.inputInfo.replaceTouchableRegionWithCrop) out << " replaceTouchableRegionWithCrop";
+ auto touchableRegion = obj.inputInfo.touchableRegion.getBounds();
+ out << " touchableRegion={" << touchableRegion.left << "," << touchableRegion.top << ","
+ << touchableRegion.bottom << "," << touchableRegion.right << "}"
+ << "}";
+ }
+ return out;
+}
+
FloatRect LayerSnapshot::sourceBounds() const {
if (!externalTexture) {
return geomLayerBounds;
@@ -203,4 +329,172 @@
return geomBufferSize.toFloatRect();
}
+Hwc2::IComposerClient::BlendMode LayerSnapshot::getBlendMode(
+ const RequestedLayerState& requested) const {
+ auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
+ if (alpha != 1.0f || !contentOpaque) {
+ blendMode = requested.premultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
+ : Hwc2::IComposerClient::BlendMode::COVERAGE;
+ }
+ return blendMode;
+}
+
+void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate,
+ bool displayChanges, bool forceFullDamage,
+ uint32_t displayRotationFlags) {
+ clientChanges = requested.what;
+ changes = requested.changes;
+ contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
+ // TODO(b/238781169) scope down the changes to only buffer updates.
+ hasReadyFrame = requested.hasReadyFrame();
+ sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
+ updateSurfaceDamage(requested, hasReadyFrame, forceFullDamage, surfaceDamage);
+
+ if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) {
+ transparentRegionHint = requested.transparentRegion;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eFlagsChanged) {
+ layerOpaqueFlagSet =
+ (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eBufferTransformChanged) {
+ geomBufferTransform = requested.bufferTransform;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eTransformToDisplayInverseChanged) {
+ geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eDataspaceChanged) {
+ dataspace = requested.dataspace;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eExtendedRangeBrightnessChanged) {
+ currentHdrSdrRatio = requested.currentHdrSdrRatio;
+ desiredHdrSdrRatio = requested.desiredHdrSdrRatio;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eCachingHintChanged) {
+ cachingHint = requested.cachingHint;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eHdrMetadataChanged) {
+ hdrMetadata = requested.hdrMetadata;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eSidebandStreamChanged) {
+ sidebandStream = requested.sidebandStream;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
+ shadowRadius = requested.shadowRadius;
+ shadowSettings.length = requested.shadowRadius;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
+ frameRateSelectionPriority = requested.frameRateSelectionPriority;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eColorSpaceAgnosticChanged) {
+ isColorspaceAgnostic = requested.colorSpaceAgnostic;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eDimmingEnabledChanged) {
+ dimmingEnabled = requested.dimmingEnabled;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eCropChanged) {
+ geomCrop = requested.crop;
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
+ layer_state_t::eSidebandStreamChanged)) {
+ compositionType = requested.getCompositionType();
+ }
+
+ if (forceUpdate || requested.what & layer_state_t::eInputInfoChanged) {
+ if (requested.windowInfoHandle) {
+ inputInfo = *requested.windowInfoHandle->getInfo();
+ } else {
+ inputInfo = {};
+ // b/271132344 revisit this and see if we can always use the layers uid/pid
+ inputInfo.name = requested.name;
+ inputInfo.ownerUid = requested.ownerUid;
+ inputInfo.ownerPid = requested.ownerPid;
+ }
+ inputInfo.id = static_cast<int32_t>(uniqueSequence);
+ touchCropId = requested.touchCropId;
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eColorChanged | layer_state_t::eBufferChanged |
+ layer_state_t::eSidebandStreamChanged)) {
+ color.rgb = requested.getColor().rgb;
+ }
+
+ if (forceUpdate || requested.what & layer_state_t::eBufferChanged) {
+ acquireFence =
+ (requested.externalTexture &&
+ requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
+ ? requested.bufferData->acquireFence
+ : Fence::NO_FENCE;
+ buffer = requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
+ externalTexture = requested.externalTexture;
+ frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
+ hasProtectedContent = requested.externalTexture &&
+ requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
+ geomUsesSourceCrop = hasBufferOrSidebandStream();
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eCropChanged | layer_state_t::eBufferCropChanged |
+ layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged) ||
+ requested.changes.test(RequestedLayerState::Changes::BufferSize) || displayChanges) {
+ bufferSize = requested.getBufferSize(displayRotationFlags);
+ geomBufferSize = bufferSize;
+ croppedBufferSize = requested.getCroppedBufferSize(bufferSize);
+ geomContentCrop = requested.getBufferCrop();
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eFlagsChanged | layer_state_t::eDestinationFrameChanged |
+ layer_state_t::ePositionChanged | layer_state_t::eMatrixChanged |
+ layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged) ||
+ requested.changes.test(RequestedLayerState::Changes::BufferSize) || displayChanges) {
+ localTransform = requested.getTransform(displayRotationFlags);
+ localTransformInverse = localTransform.inverse();
+ }
+
+ if (forceUpdate || requested.what & (layer_state_t::eColorChanged) ||
+ requested.changes.test(RequestedLayerState::Changes::BufferSize)) {
+ color.rgb = requested.getColor().rgb;
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+ layer_state_t::eApiChanged)) {
+ isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
+ requested.api == NATIVE_WINDOW_API_MEDIA &&
+ requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+ layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged |
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged)) {
+ forceClientComposition = isHdrY410 || shadowSettings.length > 0 ||
+ requested.blurRegions.size() > 0 || stretchEffect.hasEffect();
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eColorChanged | layer_state_t::eShadowRadiusChanged |
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eBackgroundBlurRadiusChanged |
+ layer_state_t::eCornerRadiusChanged | layer_state_t::eAlphaChanged |
+ layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
+ layer_state_t::eSidebandStreamChanged)) {
+ contentOpaque = isContentOpaque();
+ isOpaque = contentOpaque && !roundedCorner.hasRoundedCorners() && color.a == 1.f;
+ blendMode = getBlendMode(requested);
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b167d3e..1afcef9 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -18,6 +18,7 @@
#include <compositionengine/LayerFECompositionState.h>
#include <renderengine/LayerSettings.h>
+#include "DisplayHardware/ComposerHal.h"
#include "LayerHierarchy.h"
#include "RequestedLayerState.h"
#include "Scheduler/LayerInfo.h"
@@ -40,10 +41,6 @@
}
};
-struct ChildState {
- bool hasValidFrameRate = false;
-};
-
// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition
// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
// passed to Render Engine are created using properties stored on this struct.
@@ -57,6 +54,7 @@
bool isHiddenByPolicyFromParent = false;
bool isHiddenByPolicyFromRelativeParent = false;
ftl::Flags<RequestedLayerState::Changes> changes;
+ uint64_t clientChanges = 0;
// Some consumers of this snapshot (input, layer traces) rely on each snapshot to be unique.
// For mirrored layers, snapshots will have the same sequence so this unique id provides
// an alternative identifier when needed.
@@ -65,6 +63,7 @@
// generated from the same layer, for example when mirroring.
int32_t sequence;
std::string name;
+ std::string debugName;
uint32_t textureName;
bool contentOpaque;
bool layerOpaqueFlagSet;
@@ -91,13 +90,38 @@
ui::Transform::RotationFlags fixedTransformHint;
std::optional<ui::Transform::RotationFlags> transformHint;
bool handleSkipScreenshotFlag = false;
- int32_t frameRateSelectionPriority;
+ int32_t frameRateSelectionPriority = -1;
LayerHierarchy::TraversalPath mirrorRootPath;
- bool unreachable = true;
uint32_t touchCropId;
- uid_t uid;
- pid_t pid;
- ChildState childState;
+ gui::Uid uid = gui::Uid::INVALID;
+ gui::Pid pid = gui::Pid::INVALID;
+ enum class Reachablilty : uint32_t {
+ // Can traverse the hierarchy from a root node and reach this snapshot
+ Reachable,
+ // Cannot traverse the hierarchy from a root node and reach this snapshot
+ Unreachable,
+ // Can only reach this node from a relative parent. This means the nodes parents are
+ // not reachable.
+ // See example scenario:
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ └ - 111 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 12 (mirroring)
+ // └── 2
+ // 111 will create two snapshots, first when visited from 1 -> 12 or 1 -> 11 and the
+ // second when visited from 1 -> 14 -> 12. Because its parent 11 doesn't exist in the
+ // mirrored hierarchy, the second snapshot will be marked as ReachableByRelativeParent.
+ // This snapshot doesn't have any valid properties because it cannot inherit from its
+ // parent. Therefore, snapshots that are not reachable will be ignored for composition
+ // and input.
+ ReachableByRelativeParent
+ };
+ Reachablilty reachablilty;
static bool isOpaqueFormat(PixelFormat format);
static bool isTransformValid(const ui::Transform& t);
@@ -116,6 +140,10 @@
std::string getIsVisibleReason() const;
bool hasInputInfo() const;
FloatRect sourceBounds() const;
+ Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const;
+ friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj);
+ void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
+ bool forceFullDamage, uint32_t displayRotationFlags);
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 96ff70c..23cfe928 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -17,20 +17,23 @@
// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "LayerSnapshotBuilder"
+#define LOG_TAG "SurfaceFlinger"
#include <numeric>
#include <optional>
#include <ftl/small_map.h>
#include <gui/TraceUtils.h>
+#include <ui/DisplayMap.h>
#include <ui/FloatRect.h>
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/Hal.h"
+#include "Layer.h" // eFrameRateSelectionPriority constants
#include "LayerLog.h"
#include "LayerSnapshotBuilder.h"
#include "TimeStats/TimeStats.h"
+#include "Tracing/TransactionTracing.h"
namespace android::surfaceflinger::frontend {
@@ -256,19 +259,6 @@
return blendMode;
}
-void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
- bool forceFullDamage, Region& outSurfaceDamageRegion) {
- if (!hasReadyFrame) {
- outSurfaceDamageRegion.clear();
- return;
- }
- if (forceFullDamage) {
- outSurfaceDamageRegion = Region::INVALID_REGION;
- } else {
- outSurfaceDamageRegion = requested.surfaceDamageRegion;
- }
-}
-
void updateVisibility(LayerSnapshot& snapshot, bool visible) {
snapshot.isVisible = visible;
@@ -286,6 +276,8 @@
const bool visibleForInput =
snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
+ LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false",
+ snapshot.getDebugString().c_str());
}
bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
@@ -328,18 +320,31 @@
void clearChanges(LayerSnapshot& snapshot) {
snapshot.changes.clear();
+ snapshot.clientChanges = 0;
snapshot.contentDirty = false;
snapshot.hasReadyFrame = false;
snapshot.sidebandStreamHasFrame = false;
snapshot.surfaceDamage.clear();
}
+// TODO (b/259407931): Remove.
+uint32_t getPrimaryDisplayRotationFlags(
+ const ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
+ for (auto& [_, display] : displays) {
+ if (display.isPrimary) {
+ return display.rotationFlags;
+ }
+ }
+ return 0;
+}
+
} // namespace
LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() {
LayerSnapshot snapshot;
snapshot.path = LayerHierarchy::TraversalPath::ROOT;
snapshot.changes = ftl::Flags<RequestedLayerState::Changes>();
+ snapshot.clientChanges = 0;
snapshot.isHiddenByPolicyFromParent = false;
snapshot.isHiddenByPolicyFromRelativeParent = false;
snapshot.parentTransform.reset();
@@ -373,43 +378,44 @@
}
bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) {
- if (args.forceUpdate != ForceUpdateFlags::NONE || args.displayChanges) {
- // force update requested, or we have display changes, so skip the fast path
- return false;
- }
+ const bool forceUpdate = args.forceUpdate != ForceUpdateFlags::NONE;
- if (args.layerLifecycleManager.getGlobalChanges().get() == 0) {
+ if (args.layerLifecycleManager.getGlobalChanges().get() == 0 && !forceUpdate &&
+ !args.displayChanges) {
return true;
}
- if (args.layerLifecycleManager.getGlobalChanges() != RequestedLayerState::Changes::Content) {
- // We have changes that require us to walk the hierarchy and update child layers.
- // No fast path for you.
- return false;
- }
-
// There are only content changes which do not require any child layer snapshots to be updated.
ALOGV("%s", __func__);
ATRACE_NAME("FastPath");
- // Collect layers with changes
- ftl::SmallMap<uint32_t, RequestedLayerState*, 10> layersWithChanges;
- for (auto& layer : args.layerLifecycleManager.getLayers()) {
- if (layer->changes.test(RequestedLayerState::Changes::Content)) {
- layersWithChanges.emplace_or_replace(layer->id, layer.get());
+ uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
+ if (forceUpdate || args.displayChanges) {
+ for (auto& snapshot : mSnapshots) {
+ const RequestedLayerState* requested =
+ args.layerLifecycleManager.getLayerFromId(snapshot->path.id);
+ if (!requested) continue;
+ snapshot->merge(*requested, forceUpdate, args.displayChanges, args.forceFullDamage,
+ primaryDisplayRotationFlags);
+ }
+ return false;
+ }
+
+ // Walk through all the updated requested layer states and update the corresponding snapshots.
+ for (const RequestedLayerState* requested : args.layerLifecycleManager.getChangedLayers()) {
+ auto range = mIdToSnapshots.equal_range(requested->id);
+ for (auto it = range.first; it != range.second; it++) {
+ it->second->merge(*requested, forceUpdate, args.displayChanges, args.forceFullDamage,
+ primaryDisplayRotationFlags);
}
}
- // Walk through the snapshots, clearing previous change flags and updating the snapshots
- // if needed.
- for (auto& snapshot : mSnapshots) {
- auto it = layersWithChanges.find(snapshot->path.id);
- if (it != layersWithChanges.end()) {
- ALOGV("%s fast path snapshot changes = %s", __func__,
- mRootSnapshot.changes.string().c_str());
- LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
- updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root);
- }
+ if ((args.layerLifecycleManager.getGlobalChanges().get() &
+ ~(RequestedLayerState::Changes::Content | RequestedLayerState::Changes::Buffer).get()) !=
+ 0) {
+ // We have changes that require us to walk the hierarchy and update child layers.
+ // No fast path for you.
+ return false;
}
return true;
}
@@ -428,20 +434,28 @@
if (args.forceUpdate == ForceUpdateFlags::HIERARCHY) {
mRootSnapshot.changes |=
RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility;
+ mRootSnapshot.clientChanges |= layer_state_t::eReparent;
}
+
+ for (auto& snapshot : mSnapshots) {
+ if (snapshot->reachablilty == LayerSnapshot::Reachablilty::Reachable) {
+ snapshot->reachablilty = LayerSnapshot::Reachablilty::Unreachable;
+ }
+ }
+
LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
if (args.root.getLayer()) {
// The hierarchy can have a root layer when used for screenshots otherwise, it will have
// multiple children.
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id,
LayerHierarchy::Variant::Attached);
- updateSnapshotsInHierarchy(args, args.root, root, mRootSnapshot);
+ updateSnapshotsInHierarchy(args, args.root, root, mRootSnapshot, /*depth=*/0);
} else {
for (auto& [childHierarchy, variant] : args.root.mChildren) {
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
childHierarchy->getLayer()->id,
variant);
- updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot);
+ updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot, /*depth=*/0);
}
}
@@ -467,13 +481,26 @@
auto it = mSnapshots.begin();
while (it < mSnapshots.end()) {
auto& traversalPath = it->get()->path;
- if (!it->get()->unreachable &&
- destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) {
+ const bool unreachable =
+ it->get()->reachablilty == LayerSnapshot::Reachablilty::Unreachable;
+ const bool isClone = traversalPath.isClone();
+ const bool layerIsDestroyed =
+ destroyedLayerIds.find(traversalPath.id) != destroyedLayerIds.end();
+ const bool destroySnapshot = (unreachable && isClone) || layerIsDestroyed;
+
+ if (!destroySnapshot) {
it++;
continue;
}
- mIdToSnapshot.erase(traversalPath);
+ mPathToSnapshot.erase(traversalPath);
+
+ auto range = mIdToSnapshots.equal_range(traversalPath.id);
+ auto matchingSnapshot =
+ std::find_if(range.first, range.second, [&traversalPath](auto& snapshotWithId) {
+ return snapshotWithId.second->path == traversalPath;
+ });
+ mIdToSnapshots.erase(matchingSnapshot);
mNeedsTouchableRegionCrop.erase(traversalPath);
mSnapshots.back()->globalZ = it->get()->globalZ;
std::iter_swap(it, mSnapshots.end() - 1);
@@ -494,14 +521,26 @@
const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy(
const Args& args, const LayerHierarchy& hierarchy,
- LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot) {
+ LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
+ int depth) {
+ if (depth > 50) {
+ TransactionTraceWriter::getInstance().invoke("layer_builder_stack_overflow_",
+ /*overwrite=*/false);
+ LOG_ALWAYS_FATAL("Cycle detected in LayerSnapshotBuilder. See "
+ "builder_stack_overflow_transactions.winscope");
+ }
+
const RequestedLayerState* layer = hierarchy.getLayer();
LayerSnapshot* snapshot = getSnapshot(traversalPath);
const bool newSnapshot = snapshot == nullptr;
+ uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
if (newSnapshot) {
snapshot = createSnapshot(traversalPath, *layer, parentSnapshot);
+ snapshot->merge(*layer, /*forceUpdate=*/true, /*displayChanges=*/true, args.forceFullDamage,
+ primaryDisplayRotationFlags);
+ snapshot->changes |= RequestedLayerState::Changes::Created;
}
- scheduler::LayerInfo::FrameRate oldFrameRate = snapshot->frameRate;
+
if (traversalPath.isRelative()) {
bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative;
updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args);
@@ -517,13 +556,11 @@
childHierarchy->getLayer()->id,
variant);
const LayerSnapshot& childSnapshot =
- updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
- updateChildState(*snapshot, childSnapshot, args);
+ updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot,
+ depth + 1);
+ updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, args);
}
- if (oldFrameRate == snapshot->frameRate) {
- snapshot->changes.clear(RequestedLayerState::Changes::FrameRate);
- }
return *snapshot;
}
@@ -536,8 +573,8 @@
}
LayerSnapshot* LayerSnapshotBuilder::getSnapshot(const LayerHierarchy::TraversalPath& id) const {
- auto it = mIdToSnapshot.find(id);
- return it == mIdToSnapshot.end() ? nullptr : it->second;
+ auto it = mPathToSnapshot.find(id);
+ return it == mPathToSnapshot.end() ? nullptr : it->second;
}
LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& path,
@@ -549,7 +586,9 @@
if (path.isClone() && path.variant != LayerHierarchy::Variant::Mirror) {
snapshot->mirrorRootPath = parentSnapshot.mirrorRootPath;
}
- mIdToSnapshot[path] = snapshot;
+ mPathToSnapshot[path] = snapshot;
+
+ mIdToSnapshots.emplace(path.id, snapshot);
return snapshot;
}
@@ -564,20 +603,15 @@
}
mResortSnapshots = false;
- for (auto& snapshot : mSnapshots) {
- snapshot->unreachable = snapshot->path.isClone();
- }
-
size_t globalZ = 0;
args.root.traverseInZOrder(
[this, &globalZ](const LayerHierarchy&,
const LayerHierarchy::TraversalPath& traversalPath) -> bool {
LayerSnapshot* snapshot = getSnapshot(traversalPath);
if (!snapshot) {
- return false;
+ return true;
}
- snapshot->unreachable = false;
if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
updateVisibility(*snapshot, snapshot->getIsVisible());
size_t oldZ = snapshot->globalZ;
@@ -600,7 +634,7 @@
mSnapshots[globalZ]->globalZ = globalZ;
/* mark unreachable snapshots as explicitly invisible */
updateVisibility(*mSnapshots[globalZ], false);
- if (mSnapshots[globalZ]->unreachable) {
+ if (mSnapshots[globalZ]->reachablilty == LayerSnapshot::Reachablilty::Unreachable) {
hasUnreachableSnapshots = true;
}
globalZ++;
@@ -624,39 +658,47 @@
snapshot.relativeLayerMetadata = parentSnapshot.relativeLayerMetadata;
}
}
- snapshot.isVisible = snapshot.getIsVisible();
+ if (snapshot.reachablilty == LayerSnapshot::Reachablilty::Unreachable) {
+ snapshot.reachablilty = LayerSnapshot::Reachablilty::ReachableByRelativeParent;
+ }
}
-void LayerSnapshotBuilder::updateChildState(LayerSnapshot& snapshot,
- const LayerSnapshot& childSnapshot, const Args& args) {
- if (snapshot.childState.hasValidFrameRate) {
+void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
+ const LayerSnapshot& childSnapshot,
+ const Args& args) {
+ if (args.forceUpdate == ForceUpdateFlags::NONE &&
+ !args.layerLifecycleManager.getGlobalChanges().any(
+ RequestedLayerState::Changes::Hierarchy) &&
+ !childSnapshot.changes.any(RequestedLayerState::Changes::FrameRate) &&
+ !snapshot.changes.any(RequestedLayerState::Changes::FrameRate)) {
return;
}
- if (args.forceUpdate == ForceUpdateFlags::ALL ||
- childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) {
- // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes
- // for the same reason we are allowing touch boost for those layers. See
- // RefreshRateSelector::rankFrameRates for details.
- using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
- const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.rate.isValid() &&
- childSnapshot.frameRate.type == FrameRateCompatibility::Default;
- const auto layerVotedWithNoVote =
- childSnapshot.frameRate.type == FrameRateCompatibility::NoVote;
- const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.rate.isValid() &&
- childSnapshot.frameRate.type == FrameRateCompatibility::Exact;
- snapshot.childState.hasValidFrameRate |= layerVotedWithDefaultCompatibility ||
- layerVotedWithNoVote || layerVotedWithExactCompatibility;
+ using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+ if (snapshot.frameRate.rate.isValid() ||
+ snapshot.frameRate.type == FrameRateCompatibility::NoVote) {
+ // we already have a valid framerate.
+ return;
+ }
- // If we don't have a valid frame rate, but the children do, we set this
- // layer as NoVote to allow the children to control the refresh rate
- if (!snapshot.frameRate.rate.isValid() &&
- snapshot.frameRate.type != FrameRateCompatibility::NoVote &&
- snapshot.childState.hasValidFrameRate) {
- snapshot.frameRate =
- scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
- snapshot.changes |= childSnapshot.changes & RequestedLayerState::Changes::FrameRate;
- }
+ // We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes
+ // for the same reason we are allowing touch boost for those layers. See
+ // RefreshRateSelector::rankFrameRates for details.
+ const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.rate.isValid() &&
+ childSnapshot.frameRate.type == FrameRateCompatibility::Default;
+ const auto layerVotedWithNoVote =
+ childSnapshot.frameRate.type == FrameRateCompatibility::NoVote;
+ const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.rate.isValid() &&
+ childSnapshot.frameRate.type == FrameRateCompatibility::Exact;
+
+ bool childHasValidFrameRate = layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
+ layerVotedWithExactCompatibility;
+
+ // If we don't have a valid frame rate, but the children do, we set this
+ // layer as NoVote to allow the children to control the refresh rate
+ if (childHasValidFrameRate) {
+ snapshot.frameRate = scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+ snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
}
@@ -665,16 +707,6 @@
snapshot.relativeLayerMetadata.mMap.clear();
}
-// TODO (b/259407931): Remove.
-uint32_t getPrimaryDisplayRotationFlags(const DisplayInfos& displays) {
- for (auto& [_, display] : displays) {
- if (display.isPrimary) {
- return display.rotationFlags;
- }
- }
- return 0;
-}
-
void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
@@ -684,82 +716,69 @@
(RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
RequestedLayerState::Changes::AffectsChildren |
- RequestedLayerState::Changes::FrameRate);
- snapshot.changes |= parentChanges | requested.changes;
+ RequestedLayerState::Changes::FrameRate | RequestedLayerState::Changes::GameMode);
+ snapshot.changes |= parentChanges;
+ if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry;
+ snapshot.reachablilty = LayerSnapshot::Reachablilty::Reachable;
+ snapshot.clientChanges |= (parentSnapshot.clientChanges & layer_state_t::AFFECTS_CHILDREN);
snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
(args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
- snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
- // TODO(b/238781169) scope down the changes to only buffer updates.
- snapshot.hasReadyFrame = requested.hasReadyFrame();
- snapshot.sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
- updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage,
- snapshot.surfaceDamage);
- snapshot.outputFilter.layerStack = parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
- ? requested.layerStack
- : parentSnapshot.outputFilter.layerStack;
- uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
+ snapshot.clientChanges & layer_state_t::eReparent ||
snapshot.changes.any(RequestedLayerState::Changes::Visibility |
RequestedLayerState::Changes::Created);
- // always update the buffer regardless of visibility
- if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES || args.displayChanges) {
- snapshot.acquireFence =
- (requested.externalTexture &&
- requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
- ? requested.bufferData->acquireFence
- : Fence::NO_FENCE;
- snapshot.buffer =
- requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
- snapshot.bufferSize = requested.getBufferSize(primaryDisplayRotationFlags);
- snapshot.geomBufferSize = snapshot.bufferSize;
- snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
- snapshot.dataspace = requested.dataspace;
- snapshot.externalTexture = requested.externalTexture;
- snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
- snapshot.geomBufferTransform = requested.bufferTransform;
- snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
- snapshot.geomContentCrop = requested.getBufferCrop();
- snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream();
- snapshot.hasProtectedContent = requested.externalTexture &&
- requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
- snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
- requested.api == NATIVE_WINDOW_API_MEDIA &&
- requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
- snapshot.sidebandStream = requested.sidebandStream;
- snapshot.transparentRegionHint = requested.transparentRegion;
- snapshot.color.rgb = requested.getColor().rgb;
- snapshot.currentHdrSdrRatio = requested.currentHdrSdrRatio;
- snapshot.desiredHdrSdrRatio = requested.desiredHdrSdrRatio;
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eLayerStackChanged) {
+ // If root layer, use the layer stack otherwise get the parent's layer stack.
+ snapshot.outputFilter.layerStack =
+ parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
+ ? requested.layerStack
+ : parentSnapshot.outputFilter.layerStack;
}
if (snapshot.isHiddenByPolicyFromParent &&
!snapshot.changes.test(RequestedLayerState::Changes::Created)) {
if (forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
- RequestedLayerState::Changes::Geometry |
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
}
return;
}
- if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
- // If root layer, use the layer stack otherwise get the parent's layer stack.
+ if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Mirror)) {
+ // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
+ // marked as skip capture
+ snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
+ (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eAlphaChanged) {
snapshot.color.a = parentSnapshot.color.a * requested.color.a;
snapshot.alpha = snapshot.color.a;
snapshot.inputInfo.alpha = snapshot.color.a;
+ }
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eFlagsChanged) {
snapshot.isSecure =
parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure);
- snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
snapshot.outputFilter.toInternalDisplay = parentSnapshot.outputFilter.toInternalDisplay ||
(requested.flags & layer_state_t::eLayerSkipScreenshot);
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eTrustedOverlayChanged) {
+ snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eStretchChanged) {
snapshot.stretchEffect = (requested.stretchEffect.hasEffect())
? requested.stretchEffect
: parentSnapshot.stretchEffect;
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eColorTransformChanged) {
if (!parentSnapshot.colorTransformIsIdentity) {
snapshot.colorTransform = parentSnapshot.colorTransform * requested.colorTransform;
snapshot.colorTransformIsIdentity = false;
@@ -767,16 +786,20 @@
snapshot.colorTransform = requested.colorTransform;
snapshot.colorTransformIsIdentity = !requested.hasColorTransform;
}
+ }
+
+ if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::GameMode)) {
snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
? requested.gameMode
: parentSnapshot.gameMode;
- // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
- // marked as skip capture
- snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
- (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
+ updateMetadata(snapshot, requested, args);
+ if (args.includeMetadata) {
+ snapshot.layerMetadata = parentSnapshot.layerMetadata;
+ snapshot.layerMetadata.merge(requested.metadata);
+ }
}
- if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren) ||
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eFixedTransformHintChanged ||
args.displayChanges) {
snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
? requested.fixedTransformHint
@@ -793,6 +816,8 @@
}
if (forceUpdate ||
+ args.layerLifecycleManager.getGlobalChanges().any(
+ RequestedLayerState::Changes::Hierarchy) ||
snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
RequestedLayerState::Changes::Hierarchy)) {
snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
@@ -800,25 +825,20 @@
scheduler::LayerInfo::FrameRateCompatibility::NoVote))
? requested.requestedFrameRate
: parentSnapshot.frameRate;
+ snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
- if (forceUpdate || requested.what & layer_state_t::eMetadataChanged) {
- updateMetadata(snapshot, requested, args);
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) {
+ snapshot.frameRateSelectionPriority =
+ (requested.frameRateSelectionPriority == Layer::PRIORITY_UNSET)
+ ? parentSnapshot.frameRateSelectionPriority
+ : requested.frameRateSelectionPriority;
}
- if (forceUpdate || requested.changes.get() != 0) {
- snapshot.compositionType = requested.getCompositionType();
- snapshot.dimmingEnabled = requested.dimmingEnabled;
- snapshot.layerOpaqueFlagSet =
- (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
- snapshot.cachingHint = requested.cachingHint;
- snapshot.frameRateSelectionPriority = requested.frameRateSelectionPriority;
- }
-
- if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content) ||
- snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
- snapshot.color.rgb = requested.getColor().rgb;
- snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
+ if (forceUpdate ||
+ snapshot.clientChanges &
+ (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
+ layer_state_t::eAlphaChanged)) {
snapshot.backgroundBlurRadius = args.supportsBlur
? static_cast<int>(parentSnapshot.color.a * (float)requested.backgroundBlurRadius)
: 0;
@@ -826,29 +846,30 @@
for (auto& region : snapshot.blurRegions) {
region.alpha = region.alpha * snapshot.color.a;
}
- snapshot.hdrMetadata = requested.hdrMetadata;
}
- if (forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
- RequestedLayerState::Changes::Geometry)) {
+ if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
+ uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
updateLayerBounds(snapshot, requested, parentSnapshot, primaryDisplayRotationFlags);
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
updateRoundedCorner(snapshot, requested, parentSnapshot);
}
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eShadowRadiusChanged ||
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
+ updateShadows(snapshot, requested, args.globalShadowSettings);
+ }
+
if (forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
- RequestedLayerState::Changes::Geometry |
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
}
// computed snapshot properties
- updateShadows(snapshot, requested, args.globalShadowSettings);
- if (args.includeMetadata) {
- snapshot.layerMetadata = parentSnapshot.layerMetadata;
- snapshot.layerMetadata.merge(requested.metadata);
- }
snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
snapshot.contentOpaque = snapshot.isContentOpaque();
@@ -904,10 +925,6 @@
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
uint32_t primaryDisplayRotationFlags) {
- snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
- snapshot.geomCrop = requested.crop;
- snapshot.localTransform = requested.getTransform(primaryDisplayRotationFlags);
- snapshot.localTransformInverse = snapshot.localTransform.inverse();
snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform;
const bool transformWasInvalid = snapshot.invalidTransform;
snapshot.invalidTransform = !LayerSnapshot::isTransformValid(snapshot.geomLayerTransform);
@@ -964,11 +981,8 @@
}
}
-void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot,
- const RequestedLayerState& requested,
+void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot, const RequestedLayerState&,
const renderengine::ShadowSettings& globalShadowSettings) {
- snapshot.shadowRadius = requested.shadowRadius;
- snapshot.shadowSettings.length = requested.shadowRadius;
if (snapshot.shadowRadius > 0.f) {
snapshot.shadowSettings = globalShadowSettings;
@@ -997,8 +1011,8 @@
snapshot.inputInfo = {};
// b/271132344 revisit this and see if we can always use the layers uid/pid
snapshot.inputInfo.name = requested.name;
- snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid);
- snapshot.inputInfo.ownerPid = requested.ownerPid;
+ snapshot.inputInfo.ownerUid = gui::Uid{requested.ownerUid};
+ snapshot.inputInfo.ownerPid = gui::Pid{requested.ownerPid};
}
snapshot.touchCropId = requested.touchCropId;
@@ -1048,10 +1062,11 @@
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT;
}
- auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
- if (cropLayerSnapshot) {
+ if (requested.touchCropId != UNASSIGNED_LAYER_ID || path.isClone()) {
mNeedsTouchableRegionCrop.insert(path);
- } else if (snapshot.inputInfo.replaceTouchableRegionWithCrop) {
+ }
+ auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
+ if (!cropLayerSnapshot && snapshot.inputInfo.replaceTouchableRegionWithCrop) {
FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first;
Rect inputBoundsInDisplaySpace =
getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform);
@@ -1071,8 +1086,6 @@
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
snapshot.inputInfo.inputConfig.clear(gui::WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH);
-
- mNeedsTouchableRegionCrop.insert(path);
}
}
@@ -1129,7 +1142,7 @@
RequestedLayerState::Changes::Input;
if (args.forceUpdate != ForceUpdateFlags::ALL &&
- !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT)) {
+ !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT) && !args.displayChanges) {
return;
}
@@ -1138,6 +1151,8 @@
if (!snapshot) {
continue;
}
+ LLOGV(snapshot->sequence, "updateTouchableRegionCrop=%s",
+ snapshot->getDebugString().c_str());
const std::optional<frontend::DisplayInfo> displayInfoOpt =
args.displays.get(snapshot->outputFilter.layerStack);
static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 3f33ab8..d361605 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -96,7 +96,7 @@
const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
LayerHierarchy::TraversalPath& traversalPath,
- const LayerSnapshot& parentSnapshot);
+ const LayerSnapshot& parentSnapshot, int depth);
void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&);
static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
@@ -116,13 +116,15 @@
LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id,
const RequestedLayerState& layer,
const LayerSnapshot& parentSnapshot);
- void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
- const Args& args);
+ void updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
+ const LayerSnapshot& childSnapshot, const Args& args);
void updateTouchableRegionCrop(const Args& args);
std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
LayerHierarchy::TraversalPathHash>
- mIdToSnapshot;
+ mPathToSnapshot;
+ std::multimap<uint32_t, LayerSnapshot*> mIdToSnapshots;
+
// Track snapshots that needs touchable region crop from other snapshots
std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash>
mNeedsTouchableRegionCrop;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 23bb54c..a4777d1 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "RequestedLayerState"
+#define LOG_TAG "SurfaceFlinger"
+#include <gui/TraceUtils.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <sys/types.h>
@@ -125,18 +127,33 @@
gameMode = gui::GameMode::Unsupported;
requestedFrameRate = {};
cachingHint = gui::CachingHint::Enabled;
+
+ if (name.length() > 77) {
+ std::string shortened;
+ shortened.append(name, 0, 36);
+ shortened.append("[...]");
+ shortened.append(name, name.length() - 36);
+ debugName = std::move(shortened);
+ } else {
+ debugName = name;
+ }
}
void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
const uint32_t oldFlags = flags;
const half oldAlpha = color.a;
const bool hadBuffer = externalTexture != nullptr;
+ uint64_t oldFramenumber = hadBuffer ? bufferData->frameNumber : 0;
+ const ui::Size oldBufferSize = hadBuffer
+ ? ui::Size(externalTexture->getWidth(), externalTexture->getHeight())
+ : ui::Size();
const bool hadSideStream = sidebandStream != nullptr;
const layer_state_t& clientState = resolvedComposerState.state;
const bool hadBlur = hasBlur();
uint64_t clientChanges = what | layer_state_t::diff(clientState);
layer_state_t::merge(clientState);
what = clientChanges;
+ LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
if (clientState.what & layer_state_t::eFlagsChanged) {
if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) {
@@ -147,15 +164,19 @@
changes |= RequestedLayerState::Changes::Geometry;
}
}
+
if (clientState.what & layer_state_t::eBufferChanged) {
externalTexture = resolvedComposerState.externalTexture;
- barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
- barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
- // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
-
const bool hasBuffer = externalTexture != nullptr;
if (hasBuffer || hasBuffer != hadBuffer) {
changes |= RequestedLayerState::Changes::Buffer;
+ const ui::Size newBufferSize = hasBuffer
+ ? ui::Size(externalTexture->getWidth(), externalTexture->getHeight())
+ : ui::Size();
+ if (oldBufferSize != newBufferSize) {
+ changes |= RequestedLayerState::Changes::BufferSize;
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
}
if (hasBuffer != hadBuffer) {
@@ -163,6 +184,28 @@
RequestedLayerState::Changes::VisibleRegion |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
}
+
+ if (hasBuffer) {
+ const bool frameNumberChanged =
+ bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+ const uint64_t frameNumber =
+ frameNumberChanged ? bufferData->frameNumber : oldFramenumber + 1;
+ bufferData->frameNumber = frameNumber;
+
+ if ((barrierProducerId > bufferData->producerId) ||
+ ((barrierProducerId == bufferData->producerId) &&
+ (barrierFrameNumber > bufferData->frameNumber))) {
+ ALOGE("Out of order buffers detected for %s producedId=%d frameNumber=%" PRIu64
+ " -> producedId=%d frameNumber=%" PRIu64,
+ getDebugString().c_str(), bufferData->producerId, bufferData->frameNumber,
+ bufferData->producerId, frameNumber);
+ TransactionTraceWriter::getInstance().invoke("out_of_order_buffers_",
+ /*overwrite=*/false);
+ }
+
+ barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
+ barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
+ }
}
if (clientState.what & layer_state_t::eSidebandStreamChanged) {
@@ -261,7 +304,7 @@
// child layers.
if (static_cast<int32_t>(gameMode) != requestedGameMode) {
gameMode = static_cast<gui::GameMode>(requestedGameMode);
- changes |= RequestedLayerState::Changes::AffectsChildren;
+ changes |= RequestedLayerState::Changes::GameMode;
}
}
}
@@ -338,6 +381,13 @@
return debug.str();
}
+std::ostream& operator<<(std::ostream& out, const RequestedLayerState& obj) {
+ out << obj.debugName;
+ if (obj.relativeParentId != UNASSIGNED_LAYER_ID) out << " parent=" << obj.parentId;
+ if (!obj.handleAlive) out << " handleNotAlive";
+ return out;
+}
+
std::string RequestedLayerState::getDebugStringShort() const {
return "[" + std::to_string(id) + "]" + name;
}
@@ -352,7 +402,7 @@
return (flags & layer_state_t::eLayerHidden) == layer_state_t::eLayerHidden;
};
half4 RequestedLayerState::getColor() const {
- if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+ if (sidebandStream || externalTexture) {
return {0._hf, 0._hf, 0._hf, color.a};
}
return color;
@@ -475,6 +525,51 @@
return changes.test(Changes::Buffer) && !externalTexture;
}
+bool RequestedLayerState::backpressureEnabled() const {
+ return flags & layer_state_t::eEnableBackpressure;
+}
+
+bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const {
+ static constexpr uint64_t requiredFlags = layer_state_t::eBufferChanged;
+ if ((s.what & requiredFlags) != requiredFlags) {
+ ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
+ (s.what | requiredFlags) & ~s.what);
+ return false;
+ }
+
+ static constexpr uint64_t deniedFlags = layer_state_t::eProducerDisconnect |
+ layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
+ layer_state_t::eTransparentRegionChanged | layer_state_t::eFlagsChanged |
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eLayerStackChanged |
+ layer_state_t::eAutoRefreshChanged | layer_state_t::eReparent;
+ if (s.what & deniedFlags) {
+ ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
+ s.what & deniedFlags);
+ return false;
+ }
+
+ bool changedFlags = diff(s);
+ static constexpr auto deniedChanges = layer_state_t::ePositionChanged |
+ layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged |
+ layer_state_t::eBackgroundColorChanged | layer_state_t::eMatrixChanged |
+ layer_state_t::eCornerRadiusChanged | layer_state_t::eBackgroundBlurRadiusChanged |
+ layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
+ layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged |
+ layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged |
+ layer_state_t::eShadowRadiusChanged | layer_state_t::eFixedTransformHintChanged |
+ layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged |
+ layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged |
+ layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged;
+ if (changedFlags & deniedChanges) {
+ ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__,
+ s.what & deniedChanges);
+ return false;
+ }
+
+ return true;
+}
+
void RequestedLayerState::clearChanges() {
what = 0;
changes.clear();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 0ef50bc..1c19d6d 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -54,6 +54,8 @@
Buffer = 1u << 15,
SidebandStream = 1u << 16,
Animation = 1u << 17,
+ BufferSize = 1u << 18,
+ GameMode = 1u << 19,
};
static Rect reduce(const Rect& win, const Region& exclude);
RequestedLayerState(const LayerCreationArgs&);
@@ -72,6 +74,7 @@
Rect getBufferCrop() const;
std::string getDebugString() const;
std::string getDebugStringShort() const;
+ friend std::ostream& operator<<(std::ostream& os, const RequestedLayerState& obj);
aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
bool hasValidRelativeParent() const;
bool hasInputInfo() const;
@@ -80,6 +83,8 @@
bool hasReadyFrame() const;
bool hasSidebandStreamFrame() const;
bool willReleaseBufferOnLatch() const;
+ bool backpressureEnabled() const;
+ bool isSimpleBufferUpdate(const layer_state_t&) const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
@@ -91,10 +96,10 @@
const uint32_t textureName;
// The owner of the layer. If created from a non system process, it will be the calling uid.
// If created from a system process, the value can be passed in.
- const uid_t ownerUid;
+ const gui::Uid ownerUid;
// The owner pid of the layer. If created from a non system process, it will be the calling pid.
// If created from a system process, the value can be passed in.
- const pid_t ownerPid;
+ const gui::Pid ownerPid;
bool dataspaceRequested;
bool hasColorTransform;
bool premultipliedAlpha{true};
@@ -114,6 +119,7 @@
uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
uint64_t barrierFrameNumber = 0;
uint32_t barrierProducerId = 0;
+ std::string debugName;
// book keeping states
bool handleAlive = true;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 9cbe0bb..0d3c6eb 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -16,7 +16,7 @@
// #define LOG_NDEBUG 0
#undef LOG_TAG
-#define LOG_TAG "TransactionHandler"
+#define LOG_TAG "SurfaceFlinger"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <cutils/trace.h>
@@ -33,7 +33,7 @@
ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
}
-std::vector<TransactionState> TransactionHandler::flushTransactions() {
+void TransactionHandler::collectTransactions() {
while (!mLocklessTransactionQueue.isEmpty()) {
auto maybeTransaction = mLocklessTransactionQueue.pop();
if (!maybeTransaction.has_value()) {
@@ -42,7 +42,9 @@
auto transaction = maybeTransaction.value();
mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
}
+}
+std::vector<TransactionState> TransactionHandler::flushTransactions() {
// Collect transaction that are ready to be applied.
std::vector<TransactionState> transactions;
TransactionFlushState flushState;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 865835f..04183bc 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -58,6 +58,8 @@
using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>;
bool hasPendingTransactions();
+ // Moves transactions from the lockless queue.
+ void collectTransactions();
std::vector<TransactionState> flushTransactions();
void addTransactionReadyFilter(TransactionFilter&&);
void queueTransaction(TransactionState&&);
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
new file mode 100644
index 0000000..2c0f518
--- /dev/null
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -0,0 +1,178 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// #define LOG_NDEBUG 0
+#include <algorithm>
+
+#include "HdrSdrRatioOverlay.h"
+
+#include <SkSurface.h>
+
+#undef LOG_TAG
+#define LOG_TAG "HdrSdrRatioOverlay"
+
+namespace android {
+
+void HdrSdrRatioOverlay::drawNumber(float number, int left, SkColor color, SkCanvas& canvas) {
+ if (!isfinite(number) || number >= 10.f) return;
+ // We assume that the number range is [1.f, 10.f)
+ // and the decimal places are 2.
+ int value = static_cast<int>(number * 100);
+ SegmentDrawer::drawDigit(value / 100, left, color, canvas);
+
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::DecimalPoint, left, color, canvas);
+ left += kDigitWidth + kDigitSpace;
+
+ SegmentDrawer::drawDigit((value / 10) % 10, left, color, canvas);
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawDigit(value % 10, left, color, canvas);
+}
+
+sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color,
+ ui::Transform::RotationFlags rotation) {
+ SkMatrix canvasTransform = SkMatrix();
+ const auto [bufferWidth, bufferHeight] = [&]() -> std::pair<int, int> {
+ switch (rotation) {
+ case ui::Transform::ROT_90:
+ canvasTransform.setTranslate(kBufferHeight, 0);
+ canvasTransform.preRotate(90.f);
+ return {kBufferHeight, kBufferWidth};
+ case ui::Transform::ROT_270:
+ canvasTransform.setRotate(270.f, kBufferWidth / 2.f, kBufferWidth / 2.f);
+ return {kBufferHeight, kBufferWidth};
+ default:
+ return {kBufferWidth, kBufferHeight};
+ }
+ }();
+
+ const auto kUsageFlags = static_cast<uint64_t>(
+ GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE);
+ sp<GraphicBuffer> buffer =
+ sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
+ static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888,
+ 1u, kUsageFlags, "HdrSdrRatioOverlay");
+
+ const status_t bufferStatus = buffer->initCheck();
+ LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d",
+ bufferStatus);
+
+ sk_sp<SkSurface> surface =
+ SkSurfaces::Raster(SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
+ SkCanvas* canvas = surface->getCanvas();
+ canvas->setMatrix(canvasTransform);
+
+ drawNumber(currentHdrSdrRatio, 0, color, *canvas);
+
+ void* pixels = nullptr;
+ buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+
+ const SkImageInfo& imageInfo = surface->imageInfo();
+ const size_t dstRowBytes = buffer->getStride() * static_cast<size_t>(imageInfo.bytesPerPixel());
+
+ canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
+ buffer->unlock();
+ return buffer;
+}
+
+HdrSdrRatioOverlay::HdrSdrRatioOverlay()
+ : mSurfaceControl(
+ SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) {
+ if (!mSurfaceControl) {
+ ALOGE("%s: Failed to create buffer state layer", __func__);
+ return;
+ }
+ SurfaceComposerClient::Transaction()
+ .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl->get(), true)
+ .apply();
+}
+
+void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) {
+ mCurrentHdrSdrRatio = currentHdrSdrRatio;
+ animate();
+}
+
+void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
+ SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply();
+}
+
+void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
+ constexpr int32_t kMaxWidth = 1000;
+ const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
+ const auto height = 2 * width;
+ Rect frame((5 * width) >> 4, height >> 5);
+ // set the ratio frame to the top right of the screen
+ frame.offsetBy(viewport.width - frame.width(), height >> 4);
+
+ SurfaceComposerClient::Transaction()
+ .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+ 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+ .setPosition(mSurfaceControl->get(), frame.left, frame.top)
+ .apply();
+}
+
+auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const sp<GraphicBuffer> {
+ static const sp<GraphicBuffer> kNoBuffer;
+ if (!mSurfaceControl) return kNoBuffer;
+
+ const auto transformHint =
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
+
+ // Tell SurfaceFlinger about the pre-rotation on the buffer.
+ const auto transform = [&] {
+ switch (transformHint) {
+ case ui::Transform::ROT_90:
+ return ui::Transform::ROT_270;
+ case ui::Transform::ROT_270:
+ return ui::Transform::ROT_90;
+ default:
+ return ui::Transform::ROT_0;
+ }
+ }();
+
+ SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply();
+
+ constexpr SkColor kMinRatioColor = SK_ColorBLUE;
+ constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
+ constexpr float kAlpha = 0.8f;
+
+ // 9.f is picked here as ratio range, given that we assume that
+ // hdr/sdr ratio is [1.f, 10.f)
+ const float scale = currentHdrSdrRatio / 9.f;
+
+ SkColor4f colorBase = SkColor4f::FromColor(kMaxRatioColor) * scale;
+ const SkColor4f minRatioColor = SkColor4f::FromColor(kMinRatioColor) * (1 - scale);
+
+ colorBase.fR = colorBase.fR + minRatioColor.fR;
+ colorBase.fG = colorBase.fG + minRatioColor.fG;
+ colorBase.fB = colorBase.fB + minRatioColor.fB;
+ colorBase.fA = kAlpha;
+
+ const SkColor color = colorBase.toSkColor();
+
+ auto buffer = draw(currentHdrSdrRatio, color, transformHint);
+ return buffer;
+}
+
+void HdrSdrRatioOverlay::animate() {
+ if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
+
+ SurfaceComposerClient::Transaction()
+ .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
+ .apply();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
new file mode 100644
index 0000000..8a2586e
--- /dev/null
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Utils/OverlayUtils.h"
+
+#include <ui/Size.h>
+#include <utils/StrongPointer.h>
+
+class SkCanvas;
+
+namespace android {
+class HdrSdrRatioOverlay {
+public:
+ HdrSdrRatioOverlay();
+ void setLayerStack(ui::LayerStack);
+ void setViewport(ui::Size);
+ void animate();
+ void changeHdrSdrRatio(float currentRatio);
+
+private:
+ float mCurrentHdrSdrRatio = 1.f;
+
+ static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags);
+ static void drawNumber(float number, int left, SkColor, SkCanvas&);
+
+ const sp<GraphicBuffer> getOrCreateBuffers(float currentHdrSdrRatio);
+
+ const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
+};
+} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f12aab7..3a41f15 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -402,7 +402,7 @@
mLastComputedTrustedPresentationState = false;
if (!leaveState) {
- const auto outputLayer = findOutputLayerForDisplay(display);
+ const auto outputLayer = findOutputLayerForDisplay(display, snapshot->path);
if (outputLayer != nullptr) {
if (outputLayer->getState().coveredRegionExcludingDisplayOverlays) {
Region coveredRegion =
@@ -741,6 +741,11 @@
aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
+ return getCompositionType(outputLayer);
+}
+
+aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
+ const compositionengine::OutputLayer* outputLayer) const {
if (outputLayer == nullptr) {
return aidl::android::hardware::graphics::composer3::Composition::INVALID;
}
@@ -1349,6 +1354,8 @@
mDrawingState.bufferSurfaceFrameTX =
createSurfaceFrameForBuffer(info, postTime, mTransactionName);
}
+
+ setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName);
}
void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
@@ -1380,11 +1387,13 @@
it->second = createSurfaceFrameForTransaction(info, postTime);
}
}
+
+ setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName);
}
void Layer::addSurfaceFrameDroppedForBuffer(
- std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
- surfaceFrame->setDropTime(systemTime());
+ std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t dropTime) {
+ surfaceFrame->setDropTime(dropTime);
surfaceFrame->setPresentState(PresentState::Dropped);
mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
}
@@ -1434,6 +1443,32 @@
return surfaceFrame;
}
+void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
+ std::string debugName) {
+ if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+ return;
+ }
+
+ FrameTimelineInfo skippedFrameTimelineInfo = info;
+ skippedFrameTimelineInfo.vsyncId = info.skippedFrameVsyncId;
+
+ auto surfaceFrame =
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(skippedFrameTimelineInfo,
+ mOwnerPid, mOwnerUid,
+ getSequence(), mName, debugName,
+ /*isBuffer*/ false, getGameMode());
+ surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos);
+ // For Transactions, the post time is considered to be both queue and acquire fence time.
+ surfaceFrame->setActualQueueTime(postTime);
+ surfaceFrame->setAcquireFenceTime(postTime);
+ const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+ if (fps) {
+ surfaceFrame->setRenderRate(*fps);
+ }
+ onSurfaceFrameCreated(surfaceFrame);
+ addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime);
+}
+
bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
@@ -1581,7 +1616,7 @@
result.append("\n");
}
-void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
+void Layer::miniDumpLegacy(std::string& result, const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
if (!outputLayer) {
return;
@@ -1632,6 +1667,41 @@
result.append("\n");
}
+void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot,
+ const DisplayDevice& display) const {
+ const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path);
+ if (!outputLayer) {
+ return;
+ }
+
+ StringAppendF(&result, " %s\n", snapshot.debugName.c_str());
+ StringAppendF(&result, " %10zu | ", snapshot.globalZ);
+ StringAppendF(&result, " %10d | ",
+ snapshot.layerMetadata.getInt32(gui::METADATA_WINDOW_TYPE, 0));
+ StringAppendF(&result, "%10s | ", toString(getCompositionType(outputLayer)).c_str());
+ const auto& outputLayerState = outputLayer->getState();
+ StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
+ const Rect& frame = outputLayerState.displayFrame;
+ StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
+ const FloatRect& crop = outputLayerState.sourceCrop;
+ StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
+ crop.bottom);
+ const auto frameRate = snapshot.frameRate;
+ if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
+ StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
+ ftl::enum_string(frameRate.type).c_str(),
+ ftl::enum_string(frameRate.seamlessness).c_str());
+ } else {
+ result.append(41, ' ');
+ }
+
+ const auto focused = isLayerFocusedBasedOnPriority(snapshot.frameRateSelectionPriority);
+ StringAppendF(&result, " [%s]\n", focused ? "*" : " ");
+
+ result.append(kDumpTableRowLength, '-');
+ result.append("\n");
+}
+
void Layer::dumpFrameStats(std::string& result) const {
mFrameTracker.dumpStats(result);
}
@@ -2422,8 +2492,8 @@
WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
- mDrawingState.inputInfo.ownerUid = mOwnerUid;
- mDrawingState.inputInfo.ownerPid = mOwnerPid;
+ mDrawingState.inputInfo.ownerUid = gui::Uid{mOwnerUid};
+ mDrawingState.inputInfo.ownerPid = gui::Pid{mOwnerPid};
mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL;
mDrawingState.inputInfo.displayId = getLayerStack().id;
}
@@ -2548,6 +2618,24 @@
return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE);
}
+compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
+ const DisplayDevice* display, const frontend::LayerHierarchy::TraversalPath& path) const {
+ if (!display) return nullptr;
+ if (!mFlinger->mLayerLifecycleManagerEnabled) {
+ return display->getCompositionDisplay()->getOutputLayerForLayer(
+ getCompositionEngineLayerFE());
+ }
+ sp<LayerFE> layerFE;
+ for (auto& [p, layer] : mLayerFEs) {
+ if (p == path) {
+ layerFE = layer;
+ }
+ }
+
+ if (!layerFE) return nullptr;
+ return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE);
+}
+
Region Layer::getVisibleRegion(const DisplayDevice* display) const {
const auto outputLayer = findOutputLayerForDisplay(display);
return outputLayer ? outputLayer->getState().visibleRegion : Region();
@@ -3067,7 +3155,7 @@
decrementPendingBufferCount();
if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
- addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
+ addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX, systemTime());
mDrawingState.bufferSurfaceFrameTX.reset();
}
} else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
@@ -3095,6 +3183,16 @@
return true;
}
+ if ((mDrawingState.producerId > bufferData.producerId) ||
+ ((mDrawingState.producerId == bufferData.producerId) &&
+ (mDrawingState.frameNumber > frameNumber))) {
+ ALOGE("Out of order buffers detected for %s producedId=%d frameNumber=%" PRIu64
+ " -> producedId=%d frameNumber=%" PRIu64,
+ getDebugName(), mDrawingState.producerId, mDrawingState.frameNumber,
+ bufferData.producerId, frameNumber);
+ TransactionTraceWriter::getInstance().invoke("out_of_order_buffers_", /*overwrite=*/false);
+ }
+
mDrawingState.producerId = bufferData.producerId;
mDrawingState.barrierProducerId =
std::max(mDrawingState.producerId, mDrawingState.barrierProducerId);
@@ -3102,7 +3200,6 @@
mDrawingState.barrierFrameNumber =
std::max(mDrawingState.frameNumber, mDrawingState.barrierFrameNumber);
- // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = std::move(buffer);
mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
@@ -3599,7 +3696,7 @@
return {inputBounds, inputBoundsValid};
}
-bool Layer::simpleBufferUpdate(const layer_state_t& s) const {
+bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const {
const uint64_t requiredFlags = layer_state_t::eBufferChanged;
const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
@@ -3608,51 +3705,42 @@
layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged |
layer_state_t::eReparent;
- const uint64_t allowedFlags = layer_state_t::eHasListenerCallbacksChanged |
- layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFrameRateChanged |
- layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eApiChanged |
- layer_state_t::eMetadataChanged | layer_state_t::eDropInputModeChanged |
- layer_state_t::eInputInfoChanged;
-
if ((s.what & requiredFlags) != requiredFlags) {
- ALOGV("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
- (s.what | requiredFlags) & ~s.what);
+ ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
+ (s.what | requiredFlags) & ~s.what);
return false;
}
if (s.what & deniedFlags) {
- ALOGV("%s: false [has denied flags 0x%" PRIx64 "]", __func__, s.what & deniedFlags);
+ ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
+ s.what & deniedFlags);
return false;
}
- if (s.what & allowedFlags) {
- ALOGV("%s: [has allowed flags 0x%" PRIx64 "]", __func__, s.what & allowedFlags);
- }
-
if (s.what & layer_state_t::ePositionChanged) {
if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) {
- ALOGV("%s: false [ePositionChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [ePositionChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eAlphaChanged) {
if (mDrawingState.color.a != s.color.a) {
- ALOGV("%s: false [eAlphaChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eAlphaChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eColorTransformChanged) {
if (mDrawingState.colorTransform != s.colorTransform) {
- ALOGV("%s: false [eColorTransformChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eColorTransformChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eBackgroundColorChanged) {
if (mDrawingState.bgColorLayer || s.bgColor.a != 0) {
- ALOGV("%s: false [eBackgroundColorChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eBackgroundColorChanged changed]", __func__);
return false;
}
}
@@ -3662,91 +3750,92 @@
mRequestedTransform.dtdy() != s.matrix.dtdy ||
mRequestedTransform.dtdx() != s.matrix.dtdx ||
mRequestedTransform.dsdy() != s.matrix.dsdy) {
- ALOGV("%s: false [eMatrixChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eMatrixChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eCornerRadiusChanged) {
if (mDrawingState.cornerRadius != s.cornerRadius) {
- ALOGV("%s: false [eCornerRadiusChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eCornerRadiusChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) {
if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) {
- ALOGV("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eBufferTransformChanged) {
if (mDrawingState.bufferTransform != s.bufferTransform) {
- ALOGV("%s: false [eBufferTransformChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eBufferTransformChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eTransformToDisplayInverseChanged) {
if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) {
- ALOGV("%s: false [eTransformToDisplayInverseChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eTransformToDisplayInverseChanged changed]",
+ __func__);
return false;
}
}
if (s.what & layer_state_t::eCropChanged) {
if (mDrawingState.crop != s.crop) {
- ALOGV("%s: false [eCropChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eCropChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eDataspaceChanged) {
if (mDrawingState.dataspace != s.dataspace) {
- ALOGV("%s: false [eDataspaceChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eDataspaceChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eHdrMetadataChanged) {
if (mDrawingState.hdrMetadata != s.hdrMetadata) {
- ALOGV("%s: false [eHdrMetadataChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eHdrMetadataChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eSidebandStreamChanged) {
if (mDrawingState.sidebandStream != s.sidebandStream) {
- ALOGV("%s: false [eSidebandStreamChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eSidebandStreamChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eColorSpaceAgnosticChanged) {
if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) {
- ALOGV("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eShadowRadiusChanged) {
if (mDrawingState.shadowRadius != s.shadowRadius) {
- ALOGV("%s: false [eShadowRadiusChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eShadowRadiusChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eFixedTransformHintChanged) {
if (mDrawingState.fixedTransformHint != s.fixedTransformHint) {
- ALOGV("%s: false [eFixedTransformHintChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eFixedTransformHintChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eTrustedOverlayChanged) {
if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) {
- ALOGV("%s: false [eTrustedOverlayChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eTrustedOverlayChanged changed]", __func__);
return false;
}
}
@@ -3755,28 +3844,28 @@
StretchEffect temp = s.stretchEffect;
temp.sanitize();
if (mDrawingState.stretchEffect != temp) {
- ALOGV("%s: false [eStretchChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eStretchChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eBufferCropChanged) {
if (mDrawingState.bufferCrop != s.bufferCrop) {
- ALOGV("%s: false [eBufferCropChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eBufferCropChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eDestinationFrameChanged) {
if (mDrawingState.destinationFrame != s.destinationFrame) {
- ALOGV("%s: false [eDestinationFrameChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eDestinationFrameChanged changed]", __func__);
return false;
}
}
if (s.what & layer_state_t::eDimmingEnabledChanged) {
if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
- ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eDimmingEnabledChanged changed]", __func__);
return false;
}
}
@@ -3784,12 +3873,11 @@
if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) {
if (mDrawingState.currentHdrSdrRatio != s.currentHdrSdrRatio ||
mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) {
- ALOGV("%s: false [eExtendedRangeBrightnessChanged changed]", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false [eExtendedRangeBrightnessChanged changed]", __func__);
return false;
}
}
- ALOGV("%s: true", __func__);
return true;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f7596e2..5d77657 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -343,6 +343,7 @@
virtual sp<LayerFE> getCompositionEngineLayerFE() const;
virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
+ sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
const frontend::LayerSnapshot* getLayerSnapshot() const;
frontend::LayerSnapshot* editLayerSnapshot();
@@ -692,7 +693,8 @@
gui::LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
- void miniDump(std::string& result, const DisplayDevice&) const;
+ void miniDumpLegacy(std::string& result, const DisplayDevice&) const;
+ void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
void dumpOffscreenDebugInfo(std::string& result) const;
void clearFrameStats();
@@ -785,8 +787,8 @@
void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
nsecs_t postTime);
- void addSurfaceFrameDroppedForBuffer(
- std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
+ void addSurfaceFrameDroppedForBuffer(std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame,
+ nsecs_t dropTime);
void addSurfaceFramePresentedForBuffer(
std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime,
nsecs_t currentLatchTime);
@@ -795,6 +797,8 @@
const FrameTimelineInfo& info, nsecs_t postTime);
std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
+ void setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
+ std::string debugName);
bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener);
@@ -866,7 +870,7 @@
std::string getPendingBufferCounterName() { return mBlastTransactionName; }
bool updateGeometry();
- bool simpleBufferUpdate(const layer_state_t&) const;
+ bool isSimpleBufferUpdate(const layer_state_t& s) const;
static bool isOpaqueFormat(PixelFormat format);
@@ -958,6 +962,8 @@
void addZOrderRelative(const wp<Layer>& relative);
void removeZOrderRelative(const wp<Layer>& relative);
compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+ compositionengine::OutputLayer* findOutputLayerForDisplay(
+ const DisplayDevice*, const frontend::LayerHierarchy::TraversalPath& path) const;
bool usingRelativeZ(LayerVector::StateSet) const;
virtual ui::Transform getInputTransform() const;
@@ -1062,7 +1068,8 @@
aidl::android::hardware::graphics::composer3::Composition getCompositionType(
const DisplayDevice&) const;
-
+ aidl::android::hardware::graphics::composer3::Composition getCompositionType(
+ const compositionengine::OutputLayer*) const;
/**
* Returns an unsorted vector of all layers that are part of this tree.
* That includes the current layer and all its descendants.
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index e61916c..1c7581b 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -178,6 +178,7 @@
InputWindowInfoProto* proto = getInputWindowInfoProto();
proto->set_layout_params_flags(inputInfo.layoutParamsFlags.get());
+ proto->set_input_config(inputInfo.inputConfig.get());
using U = std::underlying_type_t<WindowInfo::Type>;
// TODO(b/129481165): This static assert can be safely removed once conversion warnings
// are re-enabled.
@@ -427,7 +428,7 @@
layerInfo->set_is_relative_of(requestedState.isRelativeOf);
- layerInfo->set_owner_uid(requestedState.ownerUid);
+ layerInfo->set_owner_uid(requestedState.ownerUid.val());
if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 4e7da82..4734097 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -1,6 +1,7 @@
adyabr@google.com
alecmouri@google.com
chaviw@google.com
+domlaskowski@google.com
lpy@google.com
pdwilliams@google.com
racarr@google.com
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 607bec2..577211f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,104 +16,20 @@
#include <algorithm>
-#include "BackgroundExecutor.h"
#include "Client.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <SkCanvas.h>
-#include <SkPaint.h>
-#pragma clang diagnostic pop
-#include <SkBlendMode.h>
-#include <SkRect.h>
#include <SkSurface.h>
-#include <gui/SurfaceControl.h>
#undef LOG_TAG
#define LOG_TAG "RefreshRateOverlay"
namespace android {
-namespace {
-constexpr int kDigitWidth = 64;
-constexpr int kDigitHeight = 100;
-constexpr int kDigitSpace = 16;
-
-constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
-constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
-constexpr int kBufferHeight = kDigitHeight;
-
-} // namespace
-
-SurfaceControlHolder::~SurfaceControlHolder() {
- // Hand the sp<SurfaceControl> to the helper thread to release the last
- // reference. This makes sure that the SurfaceControl is destructed without
- // SurfaceFlinger::mStateLock held.
- BackgroundExecutor::getInstance().sendCallbacks(
- {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
- SkCanvas& canvas) {
- const SkRect rect = [&]() {
- switch (segment) {
- case Segment::Upper:
- return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace);
- case Segment::UpperLeft:
- return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2);
- case Segment::UpperRight:
- return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth,
- kDigitHeight / 2);
- case Segment::Middle:
- return SkRect::MakeLTRB(left, kDigitHeight / 2 - kDigitSpace / 2,
- left + kDigitWidth, kDigitHeight / 2 + kDigitSpace / 2);
- case Segment::LowerLeft:
- return SkRect::MakeLTRB(left, kDigitHeight / 2, left + kDigitSpace, kDigitHeight);
- case Segment::LowerRight:
- return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2,
- left + kDigitWidth, kDigitHeight);
- case Segment::Bottom:
- return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth,
- kDigitHeight);
- }
- }();
-
- SkPaint paint;
- paint.setColor(color);
- paint.setBlendMode(SkBlendMode::kSrc);
- canvas.drawRect(rect, paint);
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor color,
- SkCanvas& canvas) {
- if (digit < 0 || digit > 9) return;
-
- if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
- digit == 8 || digit == 9)
- drawSegment(Segment::Upper, left, color, canvas);
- if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
- drawSegment(Segment::UpperLeft, left, color, canvas);
- if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
- digit == 8 || digit == 9)
- drawSegment(Segment::UpperRight, left, color, canvas);
- if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
- digit == 9)
- drawSegment(Segment::Middle, left, color, canvas);
- if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
- drawSegment(Segment::LowerLeft, left, color, canvas);
- if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
- digit == 7 || digit == 8 || digit == 9)
- drawSegment(Segment::LowerRight, left, color, canvas);
- if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
- digit == 9)
- drawSegment(Segment::Bottom, left, color, canvas);
-}
-
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
- ui::Transform::RotationFlags rotation,
- ftl::Flags<Features> features) -> Buffers {
+auto RefreshRateOverlay::draw(int displayFps, int renderFps, SkColor color,
+ ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
+ -> Buffers {
const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
Buffers buffers;
@@ -159,22 +75,27 @@
if (features.test(Features::Spinner)) {
switch (i) {
case 0:
- drawSegment(Segment::Upper, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Upper, left, color, *canvas);
break;
case 1:
- drawSegment(Segment::UpperRight, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperRight, left, color,
+ *canvas);
break;
case 2:
- drawSegment(Segment::LowerRight, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerRight, left, color,
+ *canvas);
break;
case 3:
- drawSegment(Segment::Bottom, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Bottom, left, color,
+ *canvas);
break;
case 4:
- drawSegment(Segment::LowerLeft, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerLeft, left, color,
+ *canvas);
break;
case 5:
- drawSegment(Segment::UpperLeft, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperLeft, left, color,
+ *canvas);
break;
}
}
@@ -200,34 +121,27 @@
return buffers;
}
-void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
- SkCanvas& canvas) {
+void RefreshRateOverlay::drawNumber(int number, int left, SkColor color, SkCanvas& canvas) {
if (number < 0 || number >= 1000) return;
if (number >= 100) {
- drawDigit(number / 100, left, color, canvas);
+ SegmentDrawer::drawDigit(number / 100, left, color, canvas);
}
left += kDigitWidth + kDigitSpace;
if (number >= 10) {
- drawDigit((number / 10) % 10, left, color, canvas);
+ SegmentDrawer::drawDigit((number / 10) % 10, left, color, canvas);
}
left += kDigitWidth + kDigitSpace;
- drawDigit(number % 10, left, color, canvas);
-}
-
-std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
- sp<SurfaceControl> surfaceControl =
- SurfaceComposerClient::getDefault()
- ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState);
- return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+ SegmentDrawer::drawDigit(number % 10, left, color, canvas);
}
RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
- : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
+ : mFpsRange(fpsRange),
+ mFeatures(features),
+ mSurfaceControl(
+ SurfaceControlHolder::createSurfaceControlHolder(String8("RefreshRateOverlay"))) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
@@ -296,8 +210,7 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
- mFeatures);
+ auto buffers = draw(displayIntFps, renderIntFps, color, transformHint, mFeatures);
it = mBufferCache
.try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
.first;
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 0b89b8e..65c61cb 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,12 +16,12 @@
#pragma once
-#include <SkColor.h>
+#include "Utils/OverlayUtils.h"
+
#include <vector>
#include <ftl/flags.h>
#include <ftl/small_map.h>
-#include <gui/SurfaceComposerClient.h>
#include <ui/LayerStack.h>
#include <ui/Size.h>
#include <ui/Transform.h>
@@ -34,22 +34,8 @@
namespace android {
class GraphicBuffer;
-class SurfaceControl;
class SurfaceFlinger;
-// Helper class to delete the SurfaceControl on a helper thread as
-// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
-class SurfaceControlHolder {
-public:
- explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
- ~SurfaceControlHolder();
-
- const sp<SurfaceControl>& get() const { return mSurfaceControl; }
-
-private:
- sp<SurfaceControl> mSurfaceControl;
-};
-
class RefreshRateOverlay {
public:
enum class Features {
@@ -70,18 +56,9 @@
private:
using Buffers = std::vector<sp<GraphicBuffer>>;
- class SevenSegmentDrawer {
- public:
- static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
- ftl::Flags<Features>);
-
- private:
- enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
-
- static void drawSegment(Segment, int left, SkColor, SkCanvas&);
- static void drawDigit(int digit, int left, SkColor, SkCanvas&);
- static void drawNumber(int number, int left, SkColor, SkCanvas&);
- };
+ static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+ ftl::Flags<Features>);
+ static void drawNumber(int number, int left, SkColor, SkCanvas&);
const Buffers& getOrCreateBuffers(Fps, Fps);
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index d5d8688..6d2586a 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -40,6 +40,7 @@
name: "libscheduler",
defaults: ["libscheduler_defaults"],
srcs: [
+ "src/FrameTargeter.cpp",
"src/PresentLatencyTracker.cpp",
"src/Timer.cpp",
],
@@ -52,6 +53,7 @@
test_suites: ["device-tests"],
defaults: ["libscheduler_defaults"],
srcs: [
+ "tests/FrameTargeterTest.cpp",
"tests/PresentLatencyTrackerTest.cpp",
"tests/TimerTest.cpp",
],
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 281b0ae..c70ed2c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -427,11 +427,6 @@
mCondition.notify_all();
}
-size_t EventThread::getEventThreadConnectionCount() {
- std::lock_guard<std::mutex> lock(mMutex);
- return mDisplayEventConnections.size();
-}
-
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 684745b..7023445 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -131,9 +131,6 @@
virtual VsyncEventData getLatestVsyncEventData(
const sp<EventThreadConnection>& connection) const = 0;
- // Retrieves the number of event connections tracked by this EventThread.
- virtual size_t getEventThreadConnectionCount() = 0;
-
virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
};
@@ -172,8 +169,6 @@
void setDuration(std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) override;
- size_t getEventThreadConnectionCount() override;
-
void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex);
private:
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index badbf53..3b61de7 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -29,6 +29,7 @@
virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
virtual void triggerOnFrameRateOverridesChanged() = 0;
+ virtual void onChoreographerAttached() = 0;
protected:
~ISchedulerCallback() = default;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index beaf972..5d00a26 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -131,22 +131,6 @@
const auto& info = layerPair->second;
info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
- // Set frame rate to attached choreographer.
- // TODO(b/260898223): Change to use layer hierarchy and handle frame rate vote.
- if (updateType == LayerUpdateType::SetFrameRate) {
- auto range = mAttachedChoreographers.equal_range(id);
- auto it = range.first;
- while (it != range.second) {
- sp<EventThreadConnection> choreographerConnection = it->second.promote();
- if (choreographerConnection) {
- choreographerConnection->frameRate = layerProps.setFrameRateVote.rate;
- it++;
- } else {
- it = mAttachedChoreographers.erase(it);
- }
- }
- }
-
// Activate layer if inactive.
if (found == LayerStatus::LayerInInactiveMap) {
mActiveLayerInfos.insert(
@@ -301,12 +285,6 @@
return 0.f;
}
-void LayerHistory::attachChoreographer(int32_t layerId,
- const sp<EventThreadConnection>& choreographerConnection) {
- std::lock_guard lock(mLock);
- mAttachedChoreographers.insert({layerId, wp<EventThreadConnection>(choreographerConnection)});
-}
-
auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> {
// the layer could be in either the active or inactive map, try both
auto it = mActiveLayerInfos.find(id);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 69caf9f..d083fa2 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -84,9 +84,6 @@
// return the frames per second of the layer with the given sequence id.
float getLayerFramerate(nsecs_t now, int32_t id) const;
- void attachChoreographer(int32_t layerId,
- const sp<EventThreadConnection>& choreographerConnection);
-
private:
friend class LayerHistoryTest;
friend class TestableScheduler;
@@ -124,10 +121,6 @@
LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
- // Map keyed by layer ID (sequence) to choreographer connections.
- std::unordered_multimap<int32_t, wp<EventThreadConnection>> mAttachedChoreographers
- GUARDED_BY(mLock);
-
uint32_t mDisplayArea = 0;
// Whether to emit systrace output and debug logs.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index f136e9f..fb985f7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -302,6 +302,19 @@
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
+ using fps_approx_ops::operator<;
+ if (refreshRate < 60_Hz) {
+ const bool favorsAtLeast60 =
+ std::find_if(mFrameRatesThatFavorsAtLeast60.begin(),
+ mFrameRatesThatFavorsAtLeast60.end(), [&](Fps fps) {
+ using fps_approx_ops::operator==;
+ return fps == layer.desiredRefreshRate;
+ }) != mFrameRatesThatFavorsAtLeast60.end();
+ if (favorsAtLeast60) {
+ return 0;
+ }
+ }
+
const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue();
// We only want to score this layer as a fractional pair if the content is not
@@ -853,6 +866,8 @@
return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
});
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);
frameRateOverrides.emplace(uid, overrideFps);
}
@@ -1221,10 +1236,19 @@
(supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
};
- const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ if (frameRateModes.empty()) {
+ ALOGW("No matching frame rate modes for %s range. policy: %s", rangeName,
+ policy->toString().c_str());
+ // TODO(b/292105422): Ideally DisplayManager should not send render ranges smaller than
+ // the min supported. See b/292047939.
+ // For not we just ignore the render ranges.
+ frameRateModes = createFrameRateModes(filterModes, {});
+ }
LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
- "No matching frame rate modes for %s range. policy: %s", rangeName,
- policy->toString().c_str());
+ "No matching frame rate modes for %s range even after ignoring the "
+ "render range. policy: %s",
+ rangeName, policy->toString().c_str());
const auto stringifyModes = [&] {
std::string str;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 5052e6e..7af8d03 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -18,6 +18,7 @@
#include <algorithm>
#include <numeric>
+#include <set>
#include <type_traits>
#include <utility>
#include <variant>
@@ -500,6 +501,12 @@
const std::vector<Fps> mKnownFrameRates;
const Config mConfig;
+
+ // A list of known frame rates that favors at least 60Hz if there is no exact match display
+ // refresh rate
+ const std::vector<Fps> mFrameRatesThatFavorsAtLeast60 = {23.976_Hz, 25_Hz, 29.97_Hz, 50_Hz,
+ 59.94_Hz};
+
Config::FrameRateOverride mFrameRateOverrideConfig;
struct GetRankedFrameRatesCache {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 918d401..d6d7725 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,6 +25,7 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <ftl/concat.h>
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <ftl/small_map.h>
@@ -130,8 +131,8 @@
auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
std::scoped_lock lock(mDisplayLock);
const bool isNew = mDisplays
- .emplace_or_replace(displayId, std::move(selectorPtr),
- std::move(schedulePtr))
+ .emplace_or_replace(displayId, displayId, std::move(selectorPtr),
+ std::move(schedulePtr), mFeatures)
.second;
return std::make_pair(promotePacesetterDisplayLocked(), isNew);
@@ -171,14 +172,43 @@
void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
TimePoint expectedVsyncTime) {
- const TimePoint frameTime = SchedulerClock::now();
+ const FrameTargeter::BeginFrameArgs beginFrameArgs =
+ {.frameBeginTime = SchedulerClock::now(),
+ .vsyncId = vsyncId,
+ // TODO(b/255601557): Calculate per display.
+ .expectedVsyncTime = expectedVsyncTime,
+ .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration};
- if (!compositor.commit(frameTime, vsyncId, expectedVsyncTime)) {
- return;
+ LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId);
+ const auto pacesetterId = *mPacesetterDisplayId;
+ const auto pacesetterOpt = mDisplays.get(pacesetterId);
+
+ FrameTargeter& pacesetterTargeter = *pacesetterOpt->get().targeterPtr;
+ pacesetterTargeter.beginFrame(beginFrameArgs, *pacesetterOpt->get().schedulePtr);
+
+ if (!compositor.commit(pacesetterTargeter.target())) return;
+
+ // TODO(b/256196556): Choose the frontrunner display.
+ FrameTargeters targeters;
+ targeters.try_emplace(pacesetterId, &pacesetterTargeter);
+
+ for (auto& [id, display] : mDisplays) {
+ if (id == pacesetterId) continue;
+
+ FrameTargeter& targeter = *display.targeterPtr;
+ targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
+
+ targeters.try_emplace(id, &targeter);
}
- compositor.composite(frameTime, vsyncId);
+ const auto resultsPerDisplay = compositor.composite(pacesetterId, targeters);
compositor.sample();
+
+ for (const auto& [id, targeter] : targeters) {
+ const auto resultOpt = resultsPerDisplay.get(id);
+ LOG_ALWAYS_FATAL_IF(!resultOpt);
+ targeter->endFrame(*resultOpt);
+ }
}
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
@@ -188,23 +218,23 @@
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
-bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const {
+bool Scheduler::isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const {
const auto frameRate = getFrameRateOverride(uid);
if (!frameRate.has_value()) {
return true;
}
ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str());
- return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate);
+ return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), *frameRate);
}
-bool Scheduler::isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const {
- return getVsyncSchedule()->getTracker().isVSyncInPhase(timePoint.ns(), frameRate);
+bool Scheduler::isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const {
+ return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), frameRate);
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
- return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
- return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
+ return [this](nsecs_t expectedVsyncTime, uid_t uid) {
+ return !isVsyncValid(TimePoint::fromNs(expectedVsyncTime), uid);
};
}
@@ -253,29 +283,40 @@
const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
- auto connection = createConnectionInternal(eventThread.get());
+ auto connection = eventThread->createEventConnection([&] { resync(); });
std::lock_guard<std::mutex> lock(mConnectionsLock);
mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
return handle;
}
-sp<EventThreadConnection> Scheduler::createConnectionInternal(
- EventThread* eventThread, EventRegistrationFlags eventRegistration,
- const sp<IBinder>& layerHandle) {
- int32_t layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
- auto connection = eventThread->createEventConnection([&] { resync(); }, eventRegistration);
- mLayerHistory.attachChoreographer(layerId, connection);
- return connection;
-}
-
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
ConnectionHandle handle, EventRegistrationFlags eventRegistration,
const sp<IBinder>& layerHandle) {
- std::lock_guard<std::mutex> lock(mConnectionsLock);
- RETURN_IF_INVALID_HANDLE(handle, nullptr);
- return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration,
- layerHandle);
+ const auto connection = [&]() -> sp<EventThreadConnection> {
+ std::scoped_lock lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle, nullptr);
+
+ return mConnections[handle].thread->createEventConnection([&] { resync(); },
+ eventRegistration);
+ }();
+ const auto layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
+
+ if (layerId != static_cast<int32_t>(UNASSIGNED_LAYER_ID)) {
+ // TODO(b/290409668): Moving the choreographer attachment to be a transaction that will be
+ // processed on the main thread.
+ mSchedulerCallback.onChoreographerAttached();
+
+ std::scoped_lock lock(mChoreographerLock);
+ const auto [iter, emplaced] =
+ mAttachedChoreographers.emplace(layerId,
+ AttachedChoreographers{Fps(), {connection}});
+ if (!emplaced) {
+ iter->second.connections.emplace(connection);
+ connection->frameRate = iter->second.frameRate;
+ }
+ }
+ return connection;
}
sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
@@ -375,12 +416,6 @@
thread->onModeChanged(mode);
}
-size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
- std::lock_guard<std::mutex> lock(mConnectionsLock);
- RETURN_IF_INVALID_HANDLE(handle, 0);
- return mConnections[handle].thread->getEventThreadConnectionCount();
-}
-
void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
android::EventThread* thread;
{
@@ -527,8 +562,16 @@
}
void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
- auto schedule = getVsyncSchedule(id);
- LOG_ALWAYS_FATAL_IF(!schedule);
+ const auto scheduleOpt =
+ (ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) {
+ return display.powerMode == hal::PowerMode::OFF
+ ? std::nullopt
+ : std::make_optional(display.schedulePtr);
+ });
+
+ if (!scheduleOpt) return;
+ const auto& schedule = scheduleOpt->get();
+
if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) {
schedule->enableHardwareVsync();
} else {
@@ -548,6 +591,11 @@
mLayerHistory.deregisterLayer(layer);
}
+void Scheduler::onLayerDestroyed(Layer* layer) {
+ std::scoped_lock lock(mChoreographerLock);
+ mAttachedChoreographers.erase(layer->getSequence());
+}
+
void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
if (pacesetterSelectorPtr()->canSwitch()) {
@@ -564,7 +612,9 @@
mFeatures.test(Feature::kContentDetection));
}
-void Scheduler::chooseRefreshRateForContent() {
+void Scheduler::chooseRefreshRateForContent(
+ const surfaceflinger::frontend::LayerHierarchy* hierarchy,
+ bool updateAttachedChoreographer) {
const auto selectorPtr = pacesetterSelectorPtr();
if (!selectorPtr->canSwitch()) return;
@@ -572,6 +622,20 @@
LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime());
applyPolicy(&Policy::contentRequirements, std::move(summary));
+
+ if (updateAttachedChoreographer) {
+ LOG_ALWAYS_FATAL_IF(!hierarchy);
+
+ // update the attached choreographers after we selected the render rate.
+ const ftl::Optional<FrameRateMode> modeOpt = [&] {
+ std::scoped_lock lock(mPolicyLock);
+ return mPolicy.modeOpt;
+ }();
+
+ if (modeOpt) {
+ updateAttachedChoreographers(*hierarchy, modeOpt->fps);
+ }
+ }
}
void Scheduler::resetIdleTimer() {
@@ -716,6 +780,24 @@
mFrameRateOverrideMappings.dump(dumper);
dumper.eol();
+
+ {
+ utils::Dumper::Section section(dumper, "Frame Targeting"sv);
+
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ for (const auto& [id, display] : mDisplays) {
+ utils::Dumper::Section
+ section(dumper,
+ id == mPacesetterDisplayId
+ ? ftl::Concat("Pacesetter Display ", id.value).c_str()
+ : ftl::Concat("Follower Display ", id.value).c_str());
+
+ display.targeterPtr->dump(dumper);
+ dumper.eol();
+ }
+ }
}
void Scheduler::dumpVsync(std::string& out) const {
@@ -736,6 +818,12 @@
}
bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
+ std::scoped_lock lock(mPolicyLock);
+ return updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate);
+}
+
+bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
+ Fps displayRefreshRate) {
if (consideredSignals.idle) return false;
const auto frameRateOverrides =
@@ -813,6 +901,105 @@
mPolicy = {};
}
+void Scheduler::updateAttachedChoreographersFrameRate(
+ const surfaceflinger::frontend::RequestedLayerState& layer, Fps fps) {
+ std::scoped_lock lock(mChoreographerLock);
+
+ const auto layerId = static_cast<int32_t>(layer.id);
+ const auto choreographers = mAttachedChoreographers.find(layerId);
+ if (choreographers == mAttachedChoreographers.end()) {
+ return;
+ }
+
+ auto& layerChoreographers = choreographers->second;
+
+ layerChoreographers.frameRate = fps;
+ ATRACE_FORMAT_INSTANT("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
+ ALOGV("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
+
+ auto it = layerChoreographers.connections.begin();
+ while (it != layerChoreographers.connections.end()) {
+ sp<EventThreadConnection> choreographerConnection = it->promote();
+ if (choreographerConnection) {
+ choreographerConnection->frameRate = fps;
+ it++;
+ } else {
+ it = choreographers->second.connections.erase(it);
+ }
+ }
+
+ if (layerChoreographers.connections.empty()) {
+ mAttachedChoreographers.erase(choreographers);
+ }
+}
+
+int Scheduler::updateAttachedChoreographersInternal(
+ const surfaceflinger::frontend::LayerHierarchy& layerHierarchy, Fps displayRefreshRate,
+ int parentDivisor) {
+ const char* name = layerHierarchy.getLayer() ? layerHierarchy.getLayer()->name.c_str() : "Root";
+
+ int divisor = 0;
+ if (layerHierarchy.getLayer()) {
+ const auto frameRateCompatibility = layerHierarchy.getLayer()->frameRateCompatibility;
+ const auto frameRate = Fps::fromValue(layerHierarchy.getLayer()->frameRate);
+ ALOGV("%s: %s frameRate %s parentDivisor=%d", __func__, name, to_string(frameRate).c_str(),
+ parentDivisor);
+
+ if (frameRate.isValid()) {
+ if (frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ||
+ frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_EXACT) {
+ // Since this layer wants an exact match, we would only set a frame rate if the
+ // desired rate is a divisor of the display refresh rate.
+ divisor = RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate);
+ } else if (frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) {
+ // find the closest frame rate divisor for the desired frame rate.
+ divisor = static_cast<int>(
+ std::round(displayRefreshRate.getValue() / frameRate.getValue()));
+ }
+ }
+ }
+
+ // We start by traversing the children, updating their choreographers, and getting back the
+ // aggregated frame rate.
+ int childrenDivisor = 0;
+ for (const auto& [child, _] : layerHierarchy.mChildren) {
+ LOG_ALWAYS_FATAL_IF(child == nullptr || child->getLayer() == nullptr);
+
+ ALOGV("%s: %s traversing child %s", __func__, name, child->getLayer()->name.c_str());
+
+ const int childDivisor =
+ updateAttachedChoreographersInternal(*child, displayRefreshRate, divisor);
+ childrenDivisor = childrenDivisor > 0 ? childrenDivisor : childDivisor;
+ if (childDivisor > 0) {
+ childrenDivisor = std::gcd(childrenDivisor, childDivisor);
+ }
+ ALOGV("%s: %s childrenDivisor=%d", __func__, name, childrenDivisor);
+ }
+
+ ALOGV("%s: %s divisor=%d", __func__, name, divisor);
+
+ // If there is no explicit vote for this layer. Use the children's vote if exists
+ divisor = (divisor == 0) ? childrenDivisor : divisor;
+ ALOGV("%s: %s divisor=%d with children", __func__, name, divisor);
+
+ // If there is no explicit vote for this layer or its children, Use the parent vote if exists
+ divisor = (divisor == 0) ? parentDivisor : divisor;
+ ALOGV("%s: %s divisor=%d with parent", __func__, name, divisor);
+
+ if (layerHierarchy.getLayer()) {
+ Fps fps = divisor > 1 ? displayRefreshRate / (unsigned int)divisor : Fps();
+ updateAttachedChoreographersFrameRate(*layerHierarchy.getLayer(), fps);
+ }
+
+ return divisor;
+}
+
+void Scheduler::updateAttachedChoreographers(
+ const surfaceflinger::frontend::LayerHierarchy& layerHierarchy, Fps displayRefreshRate) {
+ ATRACE_CALL();
+ updateAttachedChoreographersInternal(layerHierarchy, displayRefreshRate, 0);
+}
+
template <typename S, typename T>
auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
ATRACE_CALL();
@@ -855,7 +1042,7 @@
.emitEvent = !choice.consideredSignals.idle});
}
- frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modeOpt->fps);
+ frameRateOverridesChanged = updateFrameRateOverridesLocked(consideredSignals, modeOpt->fps);
if (mPolicy.modeOpt != modeOpt) {
mPolicy.modeOpt = modeOpt;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a1354fa..85d0f9a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -35,6 +35,7 @@
#include <ftl/fake_guard.h>
#include <ftl/optional.h>
#include <scheduler/Features.h>
+#include <scheduler/FrameTargeter.h>
#include <scheduler/Time.h>
#include <scheduler/VsyncConfig.h>
#include <ui/DisplayId.h>
@@ -51,6 +52,8 @@
#include "Utils/Dumper.h"
#include "VsyncModulator.h"
+#include <FrontEnd/LayerHierarchy.h>
+
namespace android::scheduler {
// Opaque handle to scheduler connection.
@@ -152,7 +155,7 @@
sp<IDisplayEventConnection> createDisplayEventConnection(
ConnectionHandle, EventRegistrationFlags eventRegistration = {},
- const sp<IBinder>& layerHandle = nullptr);
+ const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock);
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
@@ -219,7 +222,7 @@
// otherwise.
bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
std::optional<nsecs_t> hwcVsyncPeriod);
- void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock)
+ void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>)
REQUIRES(kMainThreadContext);
// Layers are registered on creation, and unregistered when the weak reference expires.
@@ -229,9 +232,11 @@
void setModeChangePending(bool pending);
void setDefaultFrameRateCompatibility(Layer*);
void deregisterLayer(Layer*);
+ void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
// Detects content using layer history, and selects a matching refresh rate.
- void chooseRefreshRateForContent() EXCLUDES(mDisplayLock);
+ void chooseRefreshRateForContent(const surfaceflinger::frontend::LayerHierarchy*,
+ bool updateAttachedChoreographer) EXCLUDES(mDisplayLock);
void resetIdleTimer();
@@ -249,9 +254,18 @@
return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
}
+ TimePoint expectedPresentTimeForPacesetter() const EXCLUDES(mDisplayLock) {
+ std::scoped_lock lock(mDisplayLock);
+ return pacesetterDisplayLocked()
+ .transform([](const Display& display) {
+ return display.targeterPtr->target().expectedPresentTime();
+ })
+ .value_or(TimePoint());
+ }
+
// Returns true if a given vsync timestamp is considered valid vsync
// for a given uid
- bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const;
+ bool isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const;
bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const;
@@ -271,8 +285,6 @@
// Notifies the scheduler when the display size has changed. Called from SF's main thread
void onActiveDisplayAreaChanged(uint32_t displayArea);
- size_t getEventThreadConnectionCount(ConnectionHandle handle);
-
// Stores the preferred refresh rate that an app should run at.
// FrameRateOverride.refreshRateHz == 0 means no preference.
void setPreferredRefreshRateForUid(FrameRateOverride);
@@ -295,6 +307,8 @@
return mLayerHistory.getLayerFramerate(now, id);
}
+ bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
+
private:
friend class TestableScheduler;
@@ -303,13 +317,11 @@
enum class TouchState { Inactive, Active };
// impl::MessageQueue overrides:
- void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override;
+ void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override
+ REQUIRES(kMainThreadContext, mDisplayLock);
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
- sp<EventThreadConnection> createConnectionInternal(
- EventThread*, EventRegistrationFlags eventRegistration = {},
- const sp<IBinder>& layerHandle = nullptr);
// Update feature state machine to given state when corresponding timer resets or expires.
void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
@@ -382,7 +394,14 @@
GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
- bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
+ bool updateFrameRateOverridesLocked(GlobalSignals, Fps displayRefreshRate)
+ REQUIRES(mPolicyLock);
+ void updateAttachedChoreographers(const surfaceflinger::frontend::LayerHierarchy&,
+ Fps displayRefreshRate);
+ int updateAttachedChoreographersInternal(const surfaceflinger::frontend::LayerHierarchy&,
+ Fps displayRefreshRate, int parentDivisor);
+ void updateAttachedChoreographersFrameRate(const surfaceflinger::frontend::RequestedLayerState&,
+ Fps fps) EXCLUDES(mChoreographerLock);
void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
@@ -426,13 +445,24 @@
// must lock for writes but not reads. See also mPolicyLock for locking order.
mutable std::mutex mDisplayLock;
+ using FrameTargeterPtr = std::unique_ptr<FrameTargeter>;
+
struct Display {
- Display(RefreshRateSelectorPtr selectorPtr, VsyncSchedulePtr schedulePtr)
- : selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)) {}
+ Display(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ VsyncSchedulePtr schedulePtr, FeatureFlags features)
+ : displayId(displayId),
+ selectorPtr(std::move(selectorPtr)),
+ schedulePtr(std::move(schedulePtr)),
+ targeterPtr(std::make_unique<
+ FrameTargeter>(displayId,
+ features.test(Feature::kBackpressureGpuComposition))) {}
+
+ const PhysicalDisplayId displayId;
// Effectively const except in move constructor.
RefreshRateSelectorPtr selectorPtr;
VsyncSchedulePtr schedulePtr;
+ FrameTargeterPtr targeterPtr;
hal::PowerMode powerMode = hal::PowerMode::OFF;
};
@@ -501,6 +531,16 @@
std::optional<ModeChangedParams> cachedModeChangedParams;
} mPolicy GUARDED_BY(mPolicyLock);
+ std::mutex mChoreographerLock;
+
+ struct AttachedChoreographers {
+ Fps frameRate;
+ std::unordered_set<wp<EventThreadConnection>, WpHash> connections;
+ };
+ // Map keyed by layer ID (sequence) to choreographer connections.
+ std::unordered_map<int32_t, AttachedChoreographers> mAttachedChoreographers
+ GUARDED_BY(mChoreographerLock);
+
std::mutex mVsyncTimelineLock;
std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 6499d69..e0fb8f9 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -146,13 +146,14 @@
void cancelTimer() REQUIRES(mMutex);
ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
+ std::mutex mutable mMutex;
+
static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
std::unique_ptr<TimeKeeper> const mTimeKeeper;
VsyncSchedule::TrackerPtr mTracker;
nsecs_t const mTimerSlack;
nsecs_t const mMinVsyncDistance;
- std::mutex mutable mMutex;
size_t mCallbackToken GUARDED_BY(mMutex) = 0;
CallbackMap mCallbacks GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 58e0432..47e92e1 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -20,15 +20,18 @@
#include <memory>
#include <string>
-#include <ThreadContext.h>
#include <android-base/thread_annotations.h>
#include <ThreadContext.h>
#include <ftl/enum.h>
#include <ftl/optional.h>
-#include <scheduler/Features.h>
-#include <scheduler/Time.h>
#include <ui/DisplayId.h>
+#include <scheduler/Features.h>
+#include <scheduler/IVsyncSource.h>
+#include <scheduler/Time.h>
+
+#include "ThreadContext.h"
+
namespace android {
class EventThreadTest;
class VsyncScheduleTest;
@@ -49,15 +52,16 @@
using VsyncTracker = VSyncTracker;
// Schedule that synchronizes to hardware VSYNC of a physical display.
-class VsyncSchedule {
+class VsyncSchedule final : public IVsyncSource {
public:
using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>;
VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync);
~VsyncSchedule();
- Period period() const;
- TimePoint vsyncDeadlineAfter(TimePoint) const;
+ // IVsyncSource overrides:
+ Period period() const override;
+ TimePoint vsyncDeadlineAfter(TimePoint) const override;
// Inform the schedule that the period is changing and the schedule needs to recalibrate
// itself. The schedule will end the period transition internally. This will
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
index b3a6a60..200407d 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -23,10 +23,11 @@
namespace android::scheduler {
enum class Feature : std::uint8_t {
- kPresentFences = 0b1,
- kKernelIdleTimer = 0b10,
- kContentDetection = 0b100,
- kTracePredictedVsync = 0b1000,
+ kPresentFences = 1 << 0,
+ kKernelIdleTimer = 1 << 1,
+ kContentDetection = 1 << 2,
+ kTracePredictedVsync = 1 << 3,
+ kBackpressureGpuComposition = 1 << 4,
};
using FeatureFlags = ftl::Flags<Feature>;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
new file mode 100644
index 0000000..ae74205
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <atomic>
+#include <memory>
+
+#include <ui/DisplayId.h>
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+
+#include <scheduler/Time.h>
+#include <scheduler/VsyncId.h>
+#include <scheduler/interface/CompositeResult.h>
+
+// TODO(b/185536303): Pull to FTL.
+#include "../../../TracedOrdinal.h"
+#include "../../../Utils/Dumper.h"
+
+namespace android::scheduler {
+
+struct IVsyncSource;
+
+// Read-only interface to the metrics computed by FrameTargeter for the latest frame.
+class FrameTarget {
+public:
+ VsyncId vsyncId() const { return mVsyncId; }
+
+ // The time when the frame actually began, as opposed to when it had been scheduled to begin.
+ TimePoint frameBeginTime() const { return mFrameBeginTime; }
+
+ // Relative to when the frame actually began, as opposed to when it had been scheduled to begin.
+ Duration expectedFrameDuration() const { return mExpectedPresentTime - mFrameBeginTime; }
+
+ TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
+
+ // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
+ TimePoint pastVsyncTime(Period vsyncPeriod) const;
+
+ // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
+ TimePoint previousFrameVsyncTime(Period vsyncPeriod) const {
+ return mExpectedPresentTime - vsyncPeriod;
+ }
+
+ // The present fence for the frame that had targeted the most recent VSYNC before this frame.
+ // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
+ // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
+ // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
+ // signaled by now (unless that frame missed).
+ const FenceTimePtr& presentFenceForPastVsync(Period vsyncPeriod) const;
+
+ // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
+ const FenceTimePtr& presentFenceForPreviousFrame() const {
+ return mPresentFences.front().fenceTime;
+ }
+
+ bool wouldPresentEarly(Period vsyncPeriod) const;
+
+ bool isFramePending() const { return mFramePending; }
+ bool didMissFrame() const { return mFrameMissed; }
+ bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
+
+protected:
+ explicit FrameTarget(const std::string& displayLabel);
+ ~FrameTarget() = default;
+
+ VsyncId mVsyncId;
+ TimePoint mFrameBeginTime;
+ TimePoint mExpectedPresentTime;
+
+ TracedOrdinal<bool> mFramePending;
+ TracedOrdinal<bool> mFrameMissed;
+ TracedOrdinal<bool> mHwcFrameMissed;
+ TracedOrdinal<bool> mGpuFrameMissed;
+
+ struct FenceWithFenceTime {
+ sp<Fence> fence = Fence::NO_FENCE;
+ FenceTimePtr fenceTime = FenceTime::NO_FENCE;
+ };
+ std::array<FenceWithFenceTime, 2> mPresentFences;
+
+private:
+ template <int N>
+ inline bool targetsVsyncsAhead(Period vsyncPeriod) const {
+ static_assert(N > 1);
+ return expectedFrameDuration() > (N - 1) * vsyncPeriod;
+ }
+};
+
+// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
+class FrameTargeter final : private FrameTarget {
+public:
+ FrameTargeter(PhysicalDisplayId displayId, bool backpressureGpuComposition)
+ : FrameTarget(to_string(displayId)),
+ mBackpressureGpuComposition(backpressureGpuComposition) {}
+
+ const FrameTarget& target() const { return *this; }
+
+ struct BeginFrameArgs {
+ TimePoint frameBeginTime;
+ VsyncId vsyncId;
+ TimePoint expectedVsyncTime;
+ Duration sfWorkDuration;
+ };
+
+ void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
+
+ // TODO(b/241285191): Merge with FrameTargeter::endFrame.
+ FenceTimePtr setPresentFence(sp<Fence>);
+
+ void endFrame(const CompositeResult&);
+
+ void dump(utils::Dumper&) const;
+
+private:
+ friend class FrameTargeterTest;
+
+ // For tests.
+ using IsFencePendingFuncPtr = bool (*)(const FenceTimePtr&, int graceTimeMs);
+ void beginFrame(const BeginFrameArgs&, const IVsyncSource&, IsFencePendingFuncPtr);
+ FenceTimePtr setPresentFence(sp<Fence>, FenceTimePtr);
+
+ static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
+
+ const bool mBackpressureGpuComposition;
+
+ TimePoint mScheduledPresentTime;
+ CompositionCoverageFlags mCompositionCoverage;
+
+ std::atomic_uint mFrameMissedCount = 0;
+ std::atomic_uint mHwcFrameMissedCount = 0;
+ std::atomic_uint mGpuFrameMissedCount = 0;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
new file mode 100644
index 0000000..bb2de75
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <scheduler/Time.h>
+
+namespace android::scheduler {
+
+struct IVsyncSource {
+ virtual Period period() const = 0;
+ virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0;
+
+protected:
+ ~IVsyncSource() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
new file mode 100644
index 0000000..87c704e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
+#include <scheduler/interface/CompositionCoverage.h>
+
+namespace android {
+
+struct CompositeResult {
+ CompositionCoverageFlags compositionCoverage;
+};
+
+using CompositeResultsPerDisplay = ui::PhysicalDisplayMap<PhysicalDisplayId, CompositeResult>;
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
index 3d0f1a9..767462d 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
@@ -19,6 +19,8 @@
#include <cstdint>
#include <ftl/flags.h>
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
namespace android {
@@ -34,4 +36,14 @@
using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>;
+using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayId, CompositionCoverageFlags>;
+
+inline CompositionCoverageFlags multiDisplayUnion(const CompositionCoveragePerDisplay& displays) {
+ CompositionCoverageFlags coverage;
+ for (const auto& [id, flags] : displays) {
+ coverage |= flags;
+ }
+ return coverage;
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
index cc41925..6fe813a 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -16,10 +16,22 @@
#pragma once
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
#include <scheduler/Time.h>
#include <scheduler/VsyncId.h>
+#include <scheduler/interface/CompositeResult.h>
namespace android {
+namespace scheduler {
+
+class FrameTarget;
+class FrameTargeter;
+
+using FrameTargeters = ui::PhysicalDisplayMap<PhysicalDisplayId, scheduler::FrameTargeter*>;
+
+} // namespace scheduler
struct ICompositor {
// Configures physical displays, processing hotplug and/or mode setting via the Composer HAL.
@@ -27,11 +39,12 @@
// Commits transactions for layers and displays. Returns whether any state has been invalidated,
// i.e. whether a frame should be composited for each display.
- virtual bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) = 0;
+ virtual bool commit(const scheduler::FrameTarget&) = 0;
// Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
// via RenderEngine and the Composer HAL, respectively.
- virtual void composite(TimePoint frameTime, VsyncId) = 0;
+ virtual CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters&) = 0;
// Samples the composited frame via RegionSamplingThread.
virtual void sample() = 0;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
new file mode 100644
index 0000000..7a18654
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/TraceUtils.h>
+
+#include <scheduler/FrameTargeter.h>
+#include <scheduler/IVsyncSource.h>
+
+namespace android::scheduler {
+
+FrameTarget::FrameTarget(const std::string& displayLabel)
+ : mFramePending("PrevFramePending " + displayLabel, false),
+ mFrameMissed("PrevFrameMissed " + displayLabel, false),
+ mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
+ mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
+
+TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const {
+ // TODO(b/267315508): Generalize to N VSYNCs.
+ const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod));
+ return mExpectedPresentTime - Period::fromNs(vsyncPeriod.ns() << shift);
+}
+
+const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period vsyncPeriod) const {
+ // TODO(b/267315508): Generalize to N VSYNCs.
+ const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(vsyncPeriod));
+ return mPresentFences[i].fenceTime;
+}
+
+bool FrameTarget::wouldPresentEarly(Period vsyncPeriod) const {
+ // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead`
+ // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
+
+ // TODO(b/267315508): Generalize to N VSYNCs.
+ if (targetsVsyncsAhead<3>(vsyncPeriod)) {
+ return true;
+ }
+
+ const auto fence = presentFenceForPastVsync(vsyncPeriod);
+ return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+}
+
+void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) {
+ return beginFrame(args, vsyncSource, &FrameTargeter::isFencePending);
+}
+
+void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource,
+ IsFencePendingFuncPtr isFencePendingFuncPtr) {
+ mVsyncId = args.vsyncId;
+ mFrameBeginTime = args.frameBeginTime;
+
+ // The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in
+ // the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust
+ // `mExpectedPresentTime` accordingly, but not `mScheduledPresentTime`.
+ const TimePoint lastScheduledPresentTime = mScheduledPresentTime;
+ mScheduledPresentTime = args.expectedVsyncTime;
+
+ const Period vsyncPeriod = vsyncSource.period();
+
+ // Calculate the expected present time once and use the cached value throughout this frame to
+ // make sure all layers are seeing this same value.
+ if (args.expectedVsyncTime >= args.frameBeginTime) {
+ mExpectedPresentTime = args.expectedVsyncTime;
+ } else {
+ mExpectedPresentTime = vsyncSource.vsyncDeadlineAfter(args.frameBeginTime);
+ if (args.sfWorkDuration > vsyncPeriod) {
+ // Inflate the expected present time if we're targeting the next VSYNC.
+ mExpectedPresentTime += vsyncPeriod;
+ }
+ }
+
+ ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
+ ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
+ mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
+
+ const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(vsyncPeriod);
+
+ // In cases where the present fence is about to fire, give it a small grace period instead of
+ // giving up on the frame.
+ //
+ // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being
+ // approximately equal, not whether backpressure propagation is enabled.
+ const int graceTimeForPresentFenceMs = static_cast<int>(
+ mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
+
+ // Pending frames may trigger backpressure propagation.
+ const auto& isFencePending = *isFencePendingFuncPtr;
+ mFramePending = pastPresentFence != FenceTime::NO_FENCE &&
+ isFencePending(pastPresentFence, graceTimeForPresentFenceMs);
+
+ // A frame is missed if the prior frame is still pending. If no longer pending, then we still
+ // count the frame as missed if the predicted present time was further in the past than when the
+ // fence actually fired. Add some slop to correct for drift. This should generally be smaller
+ // than a typical frame duration, but should not be so small that it reports reasonable drift as
+ // a missed frame.
+ mFrameMissed = mFramePending || [&] {
+ const nsecs_t pastPresentTime = pastPresentFence->getSignalTime();
+ if (pastPresentTime < 0) return false;
+ const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
+ return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
+ }();
+
+ mHwcFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Hwc);
+ mGpuFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Gpu);
+
+ if (mFrameMissed) mFrameMissedCount++;
+ if (mHwcFrameMissed) mHwcFrameMissedCount++;
+ if (mGpuFrameMissed) mGpuFrameMissedCount++;
+}
+
+void FrameTargeter::endFrame(const CompositeResult& result) {
+ mCompositionCoverage = result.compositionCoverage;
+}
+
+FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence) {
+ auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
+ return setPresentFence(std::move(presentFence), std::move(presentFenceTime));
+}
+
+FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime) {
+ mPresentFences[1] = mPresentFences[0];
+ mPresentFences[0] = {std::move(presentFence), presentFenceTime};
+ return presentFenceTime;
+}
+
+void FrameTargeter::dump(utils::Dumper& dumper) const {
+ // There are scripts and tests that expect this (rather than "name=value") format.
+ dumper.dump({}, "Total missed frame count: " + std::to_string(mFrameMissedCount));
+ dumper.dump({}, "HWC missed frame count: " + std::to_string(mHwcFrameMissedCount));
+ dumper.dump({}, "GPU missed frame count: " + std::to_string(mGpuFrameMissedCount));
+}
+
+bool FrameTargeter::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
+ ATRACE_CALL();
+ const status_t status = fence->wait(graceTimeMs);
+
+ // This is the same as Fence::Status::Unsignaled, but it saves a call to getStatus,
+ // which calls wait(0) again internally.
+ return status == -ETIME;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
new file mode 100644
index 0000000..1e038d1
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/optional.h>
+#include <gtest/gtest.h>
+
+#include <scheduler/Fps.h>
+#include <scheduler/FrameTargeter.h>
+#include <scheduler/IVsyncSource.h>
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+namespace {
+
+struct VsyncSource final : IVsyncSource {
+ VsyncSource(Period period, TimePoint deadline) : vsyncPeriod(period), vsyncDeadline(deadline) {}
+
+ const Period vsyncPeriod;
+ const TimePoint vsyncDeadline;
+
+ Period period() const override { return vsyncPeriod; }
+ TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; }
+};
+
+} // namespace
+
+class FrameTargeterTest : public testing::Test {
+public:
+ const auto& target() const { return mTargeter.target(); }
+
+ struct Frame {
+ Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
+ Duration frameDuration, Fps refreshRate,
+ FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled,
+ const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt)
+ : testPtr(testPtr), frameBeginTime(frameBeginTime), period(refreshRate.getPeriod()) {
+ const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime,
+ .vsyncId = vsyncId,
+ .expectedVsyncTime =
+ frameBeginTime + frameDuration,
+ .sfWorkDuration = 10ms};
+
+ testPtr->mTargeter.beginFrame(args,
+ vsyncSourceOpt
+ .or_else([&] {
+ return std::make_optional(
+ VsyncSource(period,
+ args.expectedVsyncTime));
+ })
+ .value(),
+ isFencePendingFuncPtr);
+ }
+
+ FenceTimePtr end(CompositionCoverage coverage = CompositionCoverage::Hwc) {
+ if (ended) return nullptr;
+ ended = true;
+
+ auto [fence, fenceTime] = testPtr->mFenceMap.makePendingFenceForTest();
+ testPtr->mTargeter.setPresentFence(std::move(fence), fenceTime);
+
+ testPtr->mTargeter.endFrame({.compositionCoverage = coverage});
+ return fenceTime;
+ }
+
+ ~Frame() {
+ end();
+ frameBeginTime += period;
+ }
+
+ static bool fencePending(const FenceTimePtr&, int) { return true; }
+ static bool fenceSignaled(const FenceTimePtr&, int) { return false; }
+
+ FrameTargeterTest* const testPtr;
+
+ TimePoint& frameBeginTime;
+ const Period period;
+
+ bool ended = false;
+ };
+
+private:
+ FenceToFenceTimeMap mFenceMap;
+
+ static constexpr bool kBackpressureGpuComposition = true;
+ FrameTargeter mTargeter{PhysicalDisplayId::fromPort(13), kBackpressureGpuComposition};
+};
+
+TEST_F(FrameTargeterTest, targetsFrames) {
+ VsyncId vsyncId{42};
+ {
+ TimePoint frameBeginTime(989ms);
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz);
+
+ EXPECT_EQ(target().vsyncId(), VsyncId{42});
+ EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms));
+ EXPECT_EQ(target().expectedPresentTime(), TimePoint(999ms));
+ EXPECT_EQ(target().expectedFrameDuration(), 10ms);
+ }
+ {
+ TimePoint frameBeginTime(1100ms);
+ const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz);
+
+ EXPECT_EQ(target().vsyncId(), VsyncId{43});
+ EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms));
+ EXPECT_EQ(target().expectedPresentTime(), TimePoint(1111ms));
+ EXPECT_EQ(target().expectedFrameDuration(), 11ms);
+ }
+}
+
+TEST_F(FrameTargeterTest, inflatesExpectedPresentTime) {
+ // Negative such that `expectedVsyncTime` is in the past.
+ constexpr Duration kFrameDuration = -3ms;
+ TimePoint frameBeginTime(777ms);
+
+ constexpr Fps kRefreshRate = 120_Hz;
+ const VsyncSource vsyncSource(kRefreshRate.getPeriod(), frameBeginTime + 5ms);
+ const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate,
+ Frame::fenceSignaled, vsyncSource);
+
+ EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod);
+}
+
+TEST_F(FrameTargeterTest, recallsPastVsync) {
+ VsyncId vsyncId{111};
+ TimePoint frameBeginTime(1000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 13ms;
+
+ for (int n = 5; n-- > 0;) {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
+ const auto fence = frame.end();
+
+ EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
+ EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence);
+ }
+}
+
+TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
+ VsyncId vsyncId{222};
+ TimePoint frameBeginTime(2000ms);
+ constexpr Fps kRefreshRate = 120_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 10ms;
+
+ FenceTimePtr previousFence = FenceTime::NO_FENCE;
+
+ for (int n = 5; n-- > 0;) {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
+ const auto fence = frame.end();
+
+ EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
+ EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
+
+ previousFence = fence;
+ }
+}
+
+TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
+ constexpr Period kPeriod = (60_Hz).getPeriod();
+ EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
+ EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresent) {
+ VsyncId vsyncId{333};
+ TimePoint frameBeginTime(3000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ }
+
+ // The target is early if the past present fence was signaled.
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+
+ EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
+ VsyncId vsyncId{444};
+ TimePoint frameBeginTime(4000ms);
+ constexpr Fps kRefreshRate = 120_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ }
+
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+
+ // The target is two VSYNCs ahead, so the past present fence is still pending.
+ EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+
+ { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); }
+
+ // The target is early if the past present fence was signaled.
+ EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
+ TimePoint frameBeginTime(5000ms);
+ constexpr Fps kRefreshRate = 144_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate);
+
+ // The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
+ EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+}
+
+TEST_F(FrameTargeterTest, detectsMissedFrames) {
+ VsyncId vsyncId{555};
+ TimePoint frameBeginTime(5000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ EXPECT_FALSE(target().isFramePending());
+ EXPECT_FALSE(target().didMissFrame());
+ EXPECT_FALSE(target().didMissHwcFrame());
+
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().isFramePending());
+
+ // The frame did not miss if the past present fence is invalid.
+ EXPECT_FALSE(target().didMissFrame());
+ EXPECT_FALSE(target().didMissHwcFrame());
+ }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
+ EXPECT_TRUE(target().isFramePending());
+
+ // The frame missed if the past present fence is pending.
+ EXPECT_TRUE(target().didMissFrame());
+ EXPECT_TRUE(target().didMissHwcFrame());
+
+ frame.end(CompositionCoverage::Gpu);
+ }
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
+ EXPECT_TRUE(target().isFramePending());
+
+ // The GPU frame missed if the past present fence is pending.
+ EXPECT_TRUE(target().didMissFrame());
+ EXPECT_FALSE(target().didMissHwcFrame());
+ }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().isFramePending());
+
+ const auto fence = frame.end();
+ const auto expectedPresentTime = target().expectedPresentTime();
+ fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1);
+ }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().isFramePending());
+
+ const auto fence = frame.end();
+ const auto expectedPresentTime = target().expectedPresentTime();
+ fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2);
+
+ // The frame missed if the past present fence was signaled but not within slop.
+ EXPECT_TRUE(target().didMissFrame());
+ EXPECT_TRUE(target().didMissHwcFrame());
+ }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().isFramePending());
+
+ // The frame did not miss if the past present fence was signaled within slop.
+ EXPECT_FALSE(target().didMissFrame());
+ EXPECT_FALSE(target().didMissHwcFrame());
+ }
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
index 8952ca9..df2ea83 100644
--- a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
@@ -23,16 +23,6 @@
#include <ui/FenceTime.h>
namespace android::scheduler {
-namespace {
-
-using FencePair = std::pair<sp<Fence>, std::shared_ptr<FenceTime>>;
-
-FencePair makePendingFence(FenceToFenceTimeMap& fenceMap) {
- const auto fence = sp<Fence>::make();
- return {fence, fenceMap.createFenceTimeForTest(fence)};
-}
-
-} // namespace
TEST(PresentLatencyTrackerTest, skipsInvalidFences) {
PresentLatencyTracker tracker;
@@ -43,7 +33,7 @@
EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());
FenceToFenceTimeMap fenceMap;
- const auto [fence, fenceTime] = makePendingFence(fenceMap);
+ const auto [fence, fenceTime] = fenceMap.makePendingFenceForTest();
EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero());
fenceTime->signalForTest(9999);
@@ -56,8 +46,9 @@
PresentLatencyTracker tracker;
FenceToFenceTimeMap fenceMap;
- std::array<FencePair, PresentLatencyTracker::kMaxPendingFrames> fences;
- std::generate(fences.begin(), fences.end(), [&fenceMap] { return makePendingFence(fenceMap); });
+ std::array<FenceToFenceTimeMap::FencePair, PresentLatencyTracker::kMaxPendingFrames> fences;
+ std::generate(fences.begin(), fences.end(),
+ [&fenceMap] { return fenceMap.makePendingFenceForTest(); });
// The present latency is 0 if all fences are pending.
const TimePoint kCompositeTime = TimePoint::fromNs(1234);
@@ -71,7 +62,7 @@
fences[i].second->signalForTest(kCompositeTime.ns() + static_cast<nsecs_t>(i));
}
- const auto fence = makePendingFence(fenceMap);
+ const auto fence = fenceMap.makePendingFenceForTest();
// ...then the present latency is measured using the latest frame.
constexpr Duration kPresentLatency = Duration::fromNs(static_cast<nsecs_t>(kPresentCount) - 1);
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 09dac23..0103843 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -24,6 +24,24 @@
namespace android {
+namespace {
+
+ui::Size getDisplaySize(ui::Rotation orientation, const Rect& sourceCrop) {
+ if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) {
+ return {sourceCrop.getHeight(), sourceCrop.getWidth()};
+ }
+ return {sourceCrop.getWidth(), sourceCrop.getHeight()};
+}
+
+Rect getOrientedDisplaySpaceRect(ui::Rotation orientation, int reqWidth, int reqHeight) {
+ if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) {
+ return {reqHeight, reqWidth};
+ }
+ return {reqWidth, reqHeight};
+}
+
+} // namespace
+
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
@@ -37,6 +55,7 @@
output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer)));
output->setDisplayBrightness(args.sdrWhitePointNits, args.displayBrightnessNits);
output->editState().clientTargetBrightness = args.targetBrightness;
+ output->editState().treat170mAsSrgb = args.treat170mAsSrgb;
output->setDisplayColorProfile(std::make_unique<compositionengine::impl::DisplayColorProfile>(
compositionengine::DisplayColorProfileCreationArgsBuilder()
@@ -45,10 +64,10 @@
const Rect& sourceCrop = args.renderArea.getSourceCrop();
const ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags());
- const Rect orientedDisplaySpaceRect{args.renderArea.getReqWidth(),
- args.renderArea.getReqHeight()};
- output->setProjection(orientation, sourceCrop, orientedDisplaySpaceRect);
- output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()});
+ output->setDisplaySize(getDisplaySize(orientation, sourceCrop));
+ output->setProjection(orientation, sourceCrop,
+ getOrientedDisplaySpaceRect(orientation, args.renderArea.getReqWidth(),
+ args.renderArea.getReqHeight()));
{
std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput";
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index 3c307b0..159c2bf 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -36,6 +36,7 @@
// Counterintuitively, when targetBrightness > 1.0 then dim the scene.
float targetBrightness;
bool regionSampling;
+ bool treat170mAsSrgb;
};
// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a32da6c..9f24dd6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -24,6 +24,7 @@
#include "SurfaceFlinger.h"
+#include <aidl/android/hardware/power/Boost.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -34,7 +35,6 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/Boost.h>
#include <android/native_window.h>
#include <android/os/IInputFlinger.h>
#include <binder/IPCThreadState.h>
@@ -68,6 +68,7 @@
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
#include <gui/TraceUtils.h>
#include <hidl/ServiceManagement.h>
#include <layerproto/LayerProtoParser.h>
@@ -77,6 +78,7 @@
#include <processgroup/processgroup.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/impl/ExternalTexture.h>
+#include <scheduler/FrameTargeter.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
#include <ui/DataspaceUtils.h>
@@ -90,12 +92,12 @@
#include <ui/LayerStack.h>
#include <ui/PixelFormat.h>
#include <ui/StaticDisplayInfo.h>
+#include <unistd.h>
#include <utils/StopWatch.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/misc.h>
-
#include <algorithm>
#include <cerrno>
#include <cinttypes>
@@ -324,6 +326,7 @@
bool SurfaceFlinger::useHwcForRgbToYuv;
bool SurfaceFlinger::hasSyncFramework;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+int64_t SurfaceFlinger::minAcquiredBuffers = 1;
uint32_t SurfaceFlinger::maxGraphicsWidth;
uint32_t SurfaceFlinger::maxGraphicsHeight;
bool SurfaceFlinger::useContextPriority;
@@ -385,6 +388,8 @@
useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
+ minAcquiredBuffers =
+ SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers);
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
maxGraphicsHeight = std::max(max_graphics_height(0), 0);
@@ -401,9 +406,6 @@
wideColorGamutCompositionPixelFormat =
static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
- mColorSpaceAgnosticDataspace =
- static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
-
mLayerCachingEnabled = [] {
const bool enable =
android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
@@ -492,10 +494,6 @@
return LatchUnsignaledConfig::AutoSingleLayer;
}
- if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
- return LatchUnsignaledConfig::Always;
- }
-
return LatchUnsignaledConfig::Disabled;
}
@@ -911,6 +909,27 @@
ALOGE("Run StartPropertySetThread failed!");
}
+ if (mTransactionTracing) {
+ TransactionTraceWriter::getInstance().setWriterFunction([&](const std::string& prefix,
+ bool overwrite) {
+ auto writeFn = [&]() {
+ const std::string filename =
+ TransactionTracing::DIR_NAME + prefix + TransactionTracing::FILE_NAME;
+ if (overwrite && access(filename.c_str(), F_OK) == 0) {
+ ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str());
+ return;
+ }
+ mTransactionTracing->flush();
+ mTransactionTracing->writeToFile(filename);
+ };
+ if (std::this_thread::get_id() == mMainThreadId) {
+ writeFn();
+ } else {
+ mScheduler->schedule(writeFn).get();
+ }
+ });
+ }
+
ALOGV("Done initializing");
}
@@ -1504,7 +1523,7 @@
}
display->getCompositionDisplay()->setColorProfile(
- {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN});
+ {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC});
return NO_ERROR;
});
@@ -1924,10 +1943,28 @@
float currentDimmingRatio =
compositionDisplay->editState().sdrWhitePointNits /
compositionDisplay->editState().displayBrightnessNits;
- compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits,
- brightness.displayBrightnessNits);
+ static constexpr float kDimmingThreshold = 0.02f;
+ if (brightness.sdrWhitePointNits == 0.f ||
+ abs(brightness.sdrWhitePointNits - brightness.displayBrightnessNits) /
+ brightness.sdrWhitePointNits >=
+ kDimmingThreshold) {
+ // to optimize, skip brightness setter if the brightness difference ratio
+ // is lower than threshold
+ compositionDisplay
+ ->setDisplayBrightness(brightness.sdrWhitePointNits,
+ brightness.displayBrightnessNits);
+ } else {
+ compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits,
+ brightness.sdrWhitePointNits);
+ }
+
FTL_FAKE_GUARD(kMainThreadContext,
display->stageBrightness(brightness.displayBrightness));
+ float currentHdrSdrRatio =
+ compositionDisplay->editState().displayBrightnessNits /
+ compositionDisplay->editState().sdrWhitePointNits;
+ FTL_FAKE_GUARD(kMainThreadContext,
+ display->updateHdrSdrRatioOverlayRatio(currentHdrSdrRatio));
if (brightness.sdrWhitePointNits / brightness.displayBrightnessNits !=
currentDimmingRatio) {
@@ -1999,7 +2036,7 @@
}
status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
- using hardware::power::Boost;
+ using aidl::android::hardware::power::Boost;
Boost powerBoost = static_cast<Boost>(boostId);
if (powerBoost == Boost::INTERACTION) {
@@ -2141,56 +2178,29 @@
}
}
-bool SurfaceFlinger::wouldPresentEarly(TimePoint frameTime, Period vsyncPeriod) const {
- const bool isThreeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
- return isThreeVsyncsAhead ||
- getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime() !=
- Fence::SIGNAL_TIME_PENDING;
-}
-
-auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod) const
- -> const FenceTimePtr& {
- const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod;
- const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
- return mPreviousPresentFences[i].fenceTime;
-}
-
-bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
- ATRACE_CALL();
- if (fence == FenceTime::NO_FENCE) {
- return false;
- }
-
- const status_t status = fence->wait(graceTimeMs);
- // This is the same as Fence::Status::Unsignaled, but it saves a getStatus() call,
- // which calls wait(0) again internally
- return status == -ETIME;
-}
-
-TimePoint SurfaceFlinger::calculateExpectedPresentTime(TimePoint frameTime) const {
- const auto& schedule = mScheduler->getVsyncSchedule();
-
- const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(frameTime);
- if (mScheduler->vsyncModulator().getVsyncConfig().sfOffset > 0) {
- return vsyncDeadline;
- }
-
- // Inflate the expected present time if we're targeting the next vsync.
- return vsyncDeadline + schedule->period();
-}
-
-void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
+void SurfaceFlinger::configure() {
Mutex::Autolock lock(mStateLock);
if (configureLocked()) {
setTransactionFlags(eDisplayTransactionNeeded);
}
}
-bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
- bool transactionsFlushed,
+bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs,
+ bool flushTransactions,
bool& outTransactionsAreEmpty) {
+ ATRACE_CALL();
+ frontend::Update update;
+ if (flushTransactions) {
+ update = flushLifecycleUpdates();
+ if (mTransactionTracing) {
+ mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
+ update, mFrontEndDisplayInfos,
+ mFrontEndDisplayInfosChanged);
+ }
+ }
+
bool needsTraversal = false;
- if (transactionsFlushed) {
+ if (flushTransactions) {
needsTraversal |= commitMirrorDisplays(vsyncId);
needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
needsTraversal |= applyTransactions(update.transactions, vsyncId);
@@ -2238,12 +2248,43 @@
}
}
-bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update,
- bool transactionsFlushed, bool& outTransactionsAreEmpty) {
+bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
+ bool flushTransactions, bool& outTransactionsAreEmpty) {
using Changes = frontend::RequestedLayerState::Changes;
- ATRACE_NAME("updateLayerSnapshots");
- {
+ ATRACE_CALL();
+ frontend::Update update;
+ if (flushTransactions) {
+ ATRACE_NAME("TransactionHandler:flushTransactions");
+ // Locking:
+ // 1. to prevent onHandleDestroyed from being called while the state lock is held,
+ // we must keep a copy of the transactions (specifically the composer
+ // states) around outside the scope of the lock.
+ // 2. Transactions and created layers do not share a lock. To prevent applying
+ // transactions with layers still in the createdLayer queue, collect the transactions
+ // before committing the created layers.
+ // 3. Transactions can only be flushed after adding layers, since the layer can be a newly
+ // created one
+ mTransactionHandler.collectTransactions();
+ {
+ // TODO(b/238781169) lockless queue this and keep order.
+ std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+ update.layerCreatedStates = std::move(mCreatedLayers);
+ mCreatedLayers.clear();
+ update.newLayers = std::move(mNewLayers);
+ mNewLayers.clear();
+ update.layerCreationArgs = std::move(mNewLayerArgs);
+ mNewLayerArgs.clear();
+ update.destroyedHandles = std::move(mDestroyedHandles);
+ mDestroyedHandles.clear();
+ }
+
mLayerLifecycleManager.addLayers(std::move(update.newLayers));
+ update.transactions = mTransactionHandler.flushTransactions();
+ if (mTransactionTracing) {
+ mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
+ update, mFrontEndDisplayInfos,
+ mFrontEndDisplayInfosChanged);
+ }
mLayerLifecycleManager.applyTransactions(update.transactions);
mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles);
for (auto& legacyLayer : update.layerCreatedStates) {
@@ -2252,11 +2293,11 @@
mLegacyLayers[layer->sequence] = layer;
}
}
- }
- if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
- ATRACE_NAME("LayerHierarchyBuilder:update");
- mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
- mLayerLifecycleManager.getDestroyedLayers());
+ if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
+ ATRACE_NAME("LayerHierarchyBuilder:update");
+ mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
+ mLayerLifecycleManager.getDestroyedLayers());
+ }
}
bool mustComposite = false;
@@ -2286,6 +2327,11 @@
Changes::Visibility)) {
mVisibleRegionsDirty = true;
}
+ if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) {
+ // The frame rate of attached choreographers can only change as a result of a
+ // FrameRate change (including when Hierarchy changes).
+ mUpdateAttachedChoreographer = true;
+ }
outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
@@ -2352,75 +2398,14 @@
return mustComposite;
}
-bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
- FTL_FAKE_GUARD(kMainThreadContext) {
- // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
- // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime
- // accordingly, but not mScheduledPresentTime.
- const TimePoint lastScheduledPresentTime = mScheduledPresentTime;
- mScheduledPresentTime = expectedVsyncTime;
+bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget) {
+ const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
+ ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
- // Calculate the expected present time once and use the cached value throughout this frame to
- // make sure all layers are seeing this same value.
- mExpectedPresentTime = expectedVsyncTime >= frameTime ? expectedVsyncTime
- : calculateExpectedPresentTime(frameTime);
-
- ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(vsyncId),
- ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
- mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
-
- const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
- const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod);
-
- // When backpressure propagation is enabled, we want to give a small grace period of 1ms
- // for the present fence to fire instead of just giving up on this frame to handle cases
- // where present fence is just about to get signaled.
- const int graceTimeForPresentFenceMs = static_cast<int>(
- mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
-
- // Pending frames may trigger backpressure propagation.
- const TracedOrdinal<bool> framePending = {"PrevFramePending",
- isFencePending(previousPresentFence,
- graceTimeForPresentFenceMs)};
-
- // Frame missed counts for metrics tracking.
- // A frame is missed if the prior frame is still pending. If no longer pending,
- // then we still count the frame as missed if the predicted present time
- // was further in the past than when the fence actually fired.
-
- // Add some slop to correct for drift. This should generally be
- // smaller than a typical frame duration, but should not be so small
- // that it reports reasonable drift as a missed frame.
- const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
- const nsecs_t previousPresentTime = previousPresentFence->getSignalTime();
- const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
- framePending ||
- (previousPresentTime >= 0 &&
- (lastScheduledPresentTime.ns() <
- previousPresentTime - frameMissedSlop))};
- const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
- frameMissed &&
- mCompositionCoverage.test(
- CompositionCoverage::Hwc)};
-
- const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
- frameMissed &&
- mCompositionCoverage.test(
- CompositionCoverage::Gpu)};
-
- if (frameMissed) {
- mFrameMissedCount++;
+ if (pacesetterFrameTarget.didMissFrame()) {
mTimeStats->incrementMissedFrames();
}
- if (hwcFrameMissed) {
- mHwcFrameMissedCount++;
- }
-
- if (gpuFrameMissed) {
- mGpuFrameMissedCount++;
- }
-
if (mTracingEnabledChanged) {
mLayerTracingEnabled = mLayerTracing.isEnabled();
mTracingEnabledChanged = false;
@@ -2429,7 +2414,7 @@
// If we are in the middle of a mode change and the fence hasn't
// fired yet just wait for the next commit.
if (mSetActiveModePending) {
- if (framePending) {
+ if (pacesetterFrameTarget.isFramePending()) {
mScheduler->scheduleFrame();
return false;
}
@@ -2443,64 +2428,63 @@
}
}
- if (framePending) {
- if (mBackpressureGpuComposition || (hwcFrameMissed && !gpuFrameMissed)) {
+ if (pacesetterFrameTarget.isFramePending()) {
+ if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
scheduleCommit(FrameHint::kNone);
return false;
}
}
+ const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
+
// Save this once per commit + composite to ensure consistency
// TODO (b/240619471): consider removing active display check once AOD is fixed
const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId));
mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
activeDisplay->getPowerMode() == hal::PowerMode::ON;
if (mPowerHintSessionEnabled) {
- mPowerAdvisor->setCommitStart(frameTime);
- mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
+ mPowerAdvisor->setCommitStart(pacesetterFrameTarget.frameBeginTime());
+ mPowerAdvisor->setExpectedPresentTime(pacesetterFrameTarget.expectedPresentTime());
// Frame delay is how long we should have minus how long we actually have.
const Duration idealSfWorkDuration =
mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration;
- const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime);
+ const Duration frameDelay =
+ idealSfWorkDuration - pacesetterFrameTarget.expectedFrameDuration();
mPowerAdvisor->setFrameDelay(frameDelay);
mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
- mPowerAdvisor->updateTargetWorkDuration(vsyncPeriod);
+
+ const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+ const Period idealVsyncPeriod = display->getActiveMode().fps.getPeriod();
+ mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod);
}
- if (mRefreshRateOverlaySpinner) {
+ if (mRefreshRateOverlaySpinner || mHdrSdrRatioOverlay) {
Mutex::Autolock lock(mStateLock);
if (const auto display = getDefaultDisplayDeviceLocked()) {
- display->animateRefreshRateOverlay();
+ display->animateOverlay();
}
}
// Composite if transactions were committed, or if requested by HWC.
bool mustComposite = mMustComposite.exchange(false);
{
- mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId), frameTime.ns(),
+ mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId),
+ pacesetterFrameTarget.frameBeginTime().ns(),
Fps::fromPeriodNsecs(vsyncPeriod.ns()));
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
- frontend::Update updates;
- if (flushTransactions) {
- updates = flushLifecycleUpdates();
- if (mTransactionTracing) {
- mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId),
- frameTime.ns(), updates,
- mFrontEndDisplayInfos,
- mFrontEndDisplayInfosChanged);
- }
- }
bool transactionsAreEmpty;
if (mLegacyFrontEndEnabled) {
- mustComposite |= updateLayerSnapshotsLegacy(vsyncId, updates, flushTransactions,
- transactionsAreEmpty);
+ mustComposite |=
+ updateLayerSnapshotsLegacy(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
+ flushTransactions, transactionsAreEmpty);
}
if (mLayerLifecycleManagerEnabled) {
mustComposite |=
- updateLayerSnapshots(vsyncId, updates, flushTransactions, transactionsAreEmpty);
+ updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
+ flushTransactions, transactionsAreEmpty);
}
if (transactionFlushNeeded()) {
@@ -2523,17 +2507,25 @@
// Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
// and may eventually call to ~Layer() if it holds the last reference
{
+ bool updateAttachedChoreographer = mUpdateAttachedChoreographer;
+ mUpdateAttachedChoreographer = false;
+
Mutex::Autolock lock(mStateLock);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(mLayerLifecycleManagerEnabled
+ ? &mLayerHierarchyBuilder.getHierarchy()
+ : nullptr,
+ updateAttachedChoreographer);
setActiveModeInHwcIfNeeded();
}
updateCursorAsync();
- updateInputFlinger(vsyncId, frameTime);
+ if (!mustComposite) {
+ updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
+ }
if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and tracing should only be enabled for debugging.
- addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId);
+ addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId);
}
mLastCommittedVsyncId = vsyncId;
@@ -2542,26 +2534,42 @@
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
-void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId)
- FTL_FAKE_GUARD(kMainThreadContext) {
+CompositeResultsPerDisplay SurfaceFlinger::composite(
+ PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& frameTargeters) {
+ const scheduler::FrameTarget& pacesetterTarget =
+ frameTargeters.get(pacesetterId)->get()->target();
+
+ const VsyncId vsyncId = pacesetterTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
compositionengine::CompositionRefreshArgs refreshArgs;
+ refreshArgs.powerCallback = this;
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
+
+ // Add outputs for physical displays.
+ for (const auto& [id, targeter] : frameTargeters) {
+ ftl::FakeGuard guard(mStateLock);
+
+ if (const auto display = getCompositionDisplayLocked(id)) {
+ refreshArgs.outputs.push_back(display);
+ }
+ }
+
std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
- bool dropFrame = false;
- if (display->isVirtual()) {
- Fps refreshRate = display->getAdjustedRefreshRate();
- using fps_approx_ops::operator>;
- dropFrame = (refreshRate > 0_Hz) && !mScheduler->isVsyncInPhase(frameTime, refreshRate);
- }
- if (!dropFrame) {
- refreshArgs.outputs.push_back(display->getCompositionDisplay());
- }
- display->tracePowerMode();
displayIds.push_back(display->getId());
+ display->tracePowerMode();
+
+ // Add outputs for virtual displays.
+ if (display->isVirtual()) {
+ const Fps refreshRate = display->getAdjustedRefreshRate();
+
+ if (!refreshRate.isValid() ||
+ mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) {
+ refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ }
+ }
}
mPowerAdvisor->setDisplays(displayIds);
@@ -2598,7 +2606,6 @@
refreshArgs.outputColorSetting = useColorManagement
? mDisplayColorSetting
: compositionengine::OutputColorSetting::kUnmanaged;
- refreshArgs.colorSpaceAgnosticDataspace = mColorSpaceAgnosticDataspace;
refreshArgs.forceOutputColorMode = mForceColorMode;
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
@@ -2621,15 +2628,16 @@
if (!getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
- wouldPresentEarly(frameTime, vsyncPeriod)) {
- const auto prevVsyncTime = mExpectedPresentTime - vsyncPeriod;
+ pacesetterTarget.wouldPresentEarly(vsyncPeriod)) {
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
- refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
+ // TODO(b/255601557): Calculate and pass per-display values for each FrameTarget.
+ refreshArgs.earliestPresentTime =
+ pacesetterTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration;
}
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
- refreshArgs.expectedPresentTime = mExpectedPresentTime.ns();
+ refreshArgs.expectedPresentTime = pacesetterTarget.expectedPresentTime().ns();
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
// Store the present time just before calling to the composition engine so we could notify
@@ -2655,14 +2663,14 @@
}
}
- mTimeStats->recordFrameDuration(frameTime.ns(), systemTime());
+ mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime());
// Send a power hint after presentation is finished.
if (mPowerHintSessionEnabled) {
// Now that the current frame has been presented above, PowerAdvisor needs the present time
// of the previous frame (whose fence is signaled by now) to determine how long the HWC had
// waited on that fence to retire before presenting.
- const auto& previousPresentFence = mPreviousPresentFences[0].fenceTime;
+ const auto& previousPresentFence = pacesetterTarget.presentFenceForPreviousFrame();
mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()),
TimePoint::now());
@@ -2673,23 +2681,27 @@
scheduleComposite(FrameHint::kNone);
}
- postComposition(presentTime);
+ postComposition(pacesetterId, frameTargeters, presentTime);
- const bool hadGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
+ const bool hadGpuComposited =
+ multiDisplayUnion(mCompositionCoverage).test(CompositionCoverage::Gpu);
mCompositionCoverage.clear();
TimeStats::ClientCompositionRecord clientCompositionRecord;
+
for (const auto& [_, display] : displays) {
const auto& state = display->getCompositionDisplay()->getState();
+ CompositionCoverageFlags& flags =
+ mCompositionCoverage.try_emplace(display->getId()).first->second;
if (state.usesDeviceComposition) {
- mCompositionCoverage |= CompositionCoverage::Hwc;
+ flags |= CompositionCoverage::Hwc;
}
if (state.reusedClientComposition) {
- mCompositionCoverage |= CompositionCoverage::GpuReuse;
+ flags |= CompositionCoverage::GpuReuse;
} else if (state.usesClientComposition) {
- mCompositionCoverage |= CompositionCoverage::Gpu;
+ flags |= CompositionCoverage::Gpu;
}
clientCompositionRecord.predicted |=
@@ -2698,10 +2710,11 @@
(state.strategyPrediction == CompositionStrategyPredictionState::SUCCESS);
}
- const bool hasGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
+ const auto coverage = multiDisplayUnion(mCompositionCoverage);
+ const bool hasGpuComposited = coverage.test(CompositionCoverage::Gpu);
clientCompositionRecord.hadClientComposition = hasGpuComposited;
- clientCompositionRecord.reused = mCompositionCoverage.test(CompositionCoverage::GpuReuse);
+ clientCompositionRecord.reused = coverage.test(CompositionCoverage::GpuReuse);
clientCompositionRecord.changed = hadGpuComposited != hasGpuComposited;
mTimeStats->pushCompositionStrategyState(clientCompositionRecord);
@@ -2710,15 +2723,17 @@
// TODO(b/160583065): Enable skip validation when SF caches all client composition layers.
const bool hasGpuUseOrReuse =
- mCompositionCoverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse);
+ coverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse);
mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
mLayersWithQueuedFrames.clear();
if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and should only be used for debugging.
- addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId);
+ addToLayerTracing(mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(), vsyncId);
}
+ updateInputFlinger(vsyncId, pacesetterTarget.frameBeginTime());
+
if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
mVisibleRegionsDirty = false;
@@ -2729,6 +2744,17 @@
if (mPowerHintSessionEnabled) {
mPowerAdvisor->setCompositeEnd(TimePoint::now());
}
+
+ CompositeResultsPerDisplay resultsPerDisplay;
+
+ // Filter out virtual displays.
+ for (const auto& [id, coverage] : mCompositionCoverage) {
+ if (const auto idOpt = PhysicalDisplayId::tryCast(id)) {
+ resultsPerDisplay.try_emplace(*idOpt, CompositeResult{coverage});
+ }
+ }
+
+ return resultsPerDisplay;
}
void SurfaceFlinger::updateLayerGeometry() {
@@ -2812,38 +2838,56 @@
return ui::ROTATION_0;
}
-void SurfaceFlinger::postComposition(nsecs_t callTime) {
+void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters& frameTargeters,
+ nsecs_t presentStartTime) {
ATRACE_CALL();
ALOGV(__func__);
- const auto* defaultDisplay = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+ ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences;
+ ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences;
- std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
- if (defaultDisplay &&
- defaultDisplay->getCompositionDisplay()->getState().usesClientComposition) {
- glCompositionDoneFenceTime =
- std::make_shared<FenceTime>(defaultDisplay->getCompositionDisplay()
- ->getRenderSurface()
- ->getClientTargetAcquireFence());
- } else {
- glCompositionDoneFenceTime = FenceTime::NO_FENCE;
+ for (const auto& [id, targeter] : frameTargeters) {
+ auto presentFence = getHwComposer().getPresentFence(id);
+
+ if (id == pacesetterId) {
+ mTransactionCallbackInvoker.addPresentFence(presentFence);
+ }
+
+ if (auto fenceTime = targeter->setPresentFence(std::move(presentFence));
+ fenceTime->isValid()) {
+ presentFences.try_emplace(id, std::move(fenceTime));
+ }
+
+ ftl::FakeGuard guard(mStateLock);
+ if (const auto display = getCompositionDisplayLocked(id);
+ display && display->getState().usesClientComposition) {
+ gpuCompositionDoneFences
+ .try_emplace(id, display->getRenderSurface()->getClientTargetAcquireFence());
+ }
}
- mPreviousPresentFences[1] = mPreviousPresentFences[0];
+ const auto pacesetterDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(pacesetterId));
- auto presentFence = defaultDisplay
- ? getHwComposer().getPresentFence(defaultDisplay->getPhysicalId())
- : Fence::NO_FENCE;
+ std::shared_ptr<FenceTime> pacesetterPresentFenceTime =
+ presentFences.get(pacesetterId)
+ .transform([](const FenceTimePtr& ptr) { return ptr; })
+ .value_or(FenceTime::NO_FENCE);
- auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
- mPreviousPresentFences[0] = {presentFence, presentFenceTime};
+ std::shared_ptr<FenceTime> pacesetterGpuCompositionDoneFenceTime =
+ gpuCompositionDoneFences.get(pacesetterId)
+ .transform([](sp<Fence> fence) {
+ return std::make_shared<FenceTime>(std::move(fence));
+ })
+ .value_or(FenceTime::NO_FENCE);
const TimePoint presentTime = TimePoint::now();
// Set presentation information before calling Layer::releasePendingBuffer, such that jank
// information from previous' frame classification is already available when sending jank info
// to clients, so they get jank classification as early as possible.
- mFrameTimeline->setSfPresent(presentTime.ns(), presentFenceTime, glCompositionDoneFenceTime);
+ mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime,
+ pacesetterGpuCompositionDoneFenceTime);
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
@@ -2851,9 +2895,9 @@
const TimePoint compositeTime =
TimePoint::fromNs(mCompositionEngine->getLastFrameRefreshTimestamp());
const Duration presentLatency =
- !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
- ? mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime)
- : Duration::zero();
+ getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
+ ? Duration::zero()
+ : mPresentLatencyTracker.trackPendingFrame(compositeTime, pacesetterPresentFenceTime);
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
@@ -2890,8 +2934,8 @@
mLayersWithBuffersRemoved.clear();
for (const auto& layer: mLayersWithQueuedFrames) {
- layer->onPostComposition(defaultDisplay, glCompositionDoneFenceTime, presentFenceTime,
- compositorTiming);
+ layer->onPostComposition(pacesetterDisplay.get(), pacesetterGpuCompositionDoneFenceTime,
+ pacesetterPresentFenceTime, compositorTiming);
layer->releasePendingBuffer(presentTime.ns());
}
@@ -2923,73 +2967,87 @@
for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
HdrLayerInfoReporter::HdrLayerInfo info;
int32_t maxArea = 0;
- mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
- const auto layerFe = layer->getCompositionEngineLayerFE();
- const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
- if (snapshot.isVisible &&
- compositionDisplay->includesLayer(snapshot.outputFilter)) {
- if (isHdrLayer(snapshot)) {
- const auto* outputLayer =
- compositionDisplay->getOutputLayerForLayer(layerFe);
- if (outputLayer) {
- const float desiredHdrSdrRatio = snapshot.desiredHdrSdrRatio <= 1.f
- ? std::numeric_limits<float>::infinity()
- : snapshot.desiredHdrSdrRatio;
- info.mergeDesiredRatio(desiredHdrSdrRatio);
- info.numberOfHdrLayers++;
- const auto displayFrame = outputLayer->getState().displayFrame;
- const int32_t area = displayFrame.width() * displayFrame.height();
- if (area > maxArea) {
- maxArea = area;
- info.maxW = displayFrame.width();
- info.maxH = displayFrame.height();
+ auto updateInfoFn =
+ [&](const std::shared_ptr<compositionengine::Display>& compositionDisplay,
+ const frontend::LayerSnapshot& snapshot, const sp<LayerFE>& layerFe) {
+ if (snapshot.isVisible &&
+ compositionDisplay->includesLayer(snapshot.outputFilter)) {
+ if (isHdrLayer(snapshot)) {
+ const auto* outputLayer =
+ compositionDisplay->getOutputLayerForLayer(layerFe);
+ if (outputLayer) {
+ const float desiredHdrSdrRatio =
+ snapshot.desiredHdrSdrRatio <= 1.f
+ ? std::numeric_limits<float>::infinity()
+ : snapshot.desiredHdrSdrRatio;
+ info.mergeDesiredRatio(desiredHdrSdrRatio);
+ info.numberOfHdrLayers++;
+ const auto displayFrame = outputLayer->getState().displayFrame;
+ const int32_t area =
+ displayFrame.width() * displayFrame.height();
+ if (area > maxArea) {
+ maxArea = area;
+ info.maxW = displayFrame.width();
+ info.maxH = displayFrame.height();
+ }
+ }
}
}
- }
- }
- });
+ };
+
+ if (mLayerLifecycleManagerEnabled) {
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&, compositionDisplay = compositionDisplay](
+ std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ auto& legacyLayer = it->second;
+ sp<LayerFE> layerFe =
+ legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+
+ updateInfoFn(compositionDisplay, *snapshot, layerFe);
+ });
+ } else {
+ mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
+ const auto layerFe = layer->getCompositionEngineLayerFE();
+ const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
+ updateInfoFn(compositionDisplay, snapshot, layerFe);
+ });
+ }
listener->dispatchHdrLayerInfo(info);
}
}
mHdrLayerInfoChanged = false;
- mTransactionCallbackInvoker.addPresentFence(std::move(presentFence));
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
mTransactionCallbackInvoker.clearCompletedTransactions();
mTimeStats->incrementTotalFrames();
- mTimeStats->setPresentFenceGlobal(presentFenceTime);
+ mTimeStats->setPresentFenceGlobal(pacesetterPresentFenceTime);
- {
+ for (auto&& [id, presentFence] : presentFences) {
ftl::FakeGuard guard(mStateLock);
- for (const auto& [id, physicalDisplay] : mPhysicalDisplays) {
- if (auto displayDevice = getDisplayDeviceLocked(id);
- displayDevice && displayDevice->isPoweredOn() && physicalDisplay.isInternal()) {
- auto presentFenceTimeI = defaultDisplay && defaultDisplay->getPhysicalId() == id
- ? std::move(presentFenceTime)
- : std::make_shared<FenceTime>(getHwComposer().getPresentFence(id));
- if (presentFenceTimeI->isValid()) {
- mScheduler->addPresentFence(id, std::move(presentFenceTimeI));
- }
- }
+ const bool isInternalDisplay =
+ mPhysicalDisplays.get(id).transform(&PhysicalDisplay::isInternal).value_or(false);
+
+ if (isInternalDisplay) {
+ mScheduler->addPresentFence(id, std::move(presentFence));
}
}
- const bool isDisplayConnected =
- defaultDisplay && getHwComposer().isConnected(defaultDisplay->getPhysicalId());
+ const bool hasPacesetterDisplay =
+ pacesetterDisplay && getHwComposer().isConnected(pacesetterId);
if (!hasSyncFramework) {
- if (isDisplayConnected && defaultDisplay->isPoweredOn()) {
- mScheduler->enableHardwareVsync(defaultDisplay->getPhysicalId());
+ if (hasPacesetterDisplay && pacesetterDisplay->isPoweredOn()) {
+ mScheduler->enableHardwareVsync(pacesetterId);
}
}
- const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
- const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
- mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
-
- if (isDisplayConnected && !defaultDisplay->isPoweredOn()) {
+ if (hasPacesetterDisplay && !pacesetterDisplay->isPoweredOn()) {
getRenderEngine().cleanupPostRender();
return;
}
@@ -3020,7 +3078,7 @@
if (!layer->hasTrustedPresentationListener()) {
return;
}
- const frontend::LayerSnapshot* snapshot = (mLayerLifecycleManagerEnabled)
+ const frontend::LayerSnapshot* snapshot = mLayerLifecycleManagerEnabled
? mLayerSnapshotBuilder.getSnapshot(layer->sequence)
: layer->getLayerSnapshot();
std::optional<const DisplayDevice*> displayOpt = std::nullopt;
@@ -3029,7 +3087,8 @@
}
const DisplayDevice* display = displayOpt.value_or(nullptr);
layer->updateTrustedPresentationState(display, snapshot,
- nanoseconds_to_milliseconds(callTime), false);
+ nanoseconds_to_milliseconds(presentStartTime),
+ false);
});
}
@@ -3103,7 +3162,9 @@
int attempt = 0;
constexpr int kMaxAttempts = 3;
do {
- hwcModes = getHwComposer().getModes(displayId);
+ hwcModes = getHwComposer().getModes(displayId,
+ scheduler::RefreshRateSelector::kMinSupportedFrameRate
+ .getPeriodNsecs());
activeModeHwcId = getHwComposer().getActiveMode(displayId);
const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
@@ -3356,8 +3417,7 @@
}
display->getCompositionDisplay()->setColorProfile(
compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace,
- RenderIntent::COLORIMETRIC,
- Dataspace::UNKNOWN});
+ RenderIntent::COLORIMETRIC});
if (const auto& physical = state.physical) {
mPhysicalDisplays.get(physical->id)
@@ -3913,6 +3973,18 @@
mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
}
+void SurfaceFlinger::notifyCpuLoadUp() {
+ mPowerAdvisor->notifyCpuLoadUp();
+}
+
+void SurfaceFlinger::onChoreographerAttached() {
+ ATRACE_CALL();
+ if (mLayerLifecycleManagerEnabled) {
+ mUpdateAttachedChoreographer = true;
+ scheduleCommit(FrameHint::kNone);
+ }
+}
+
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
using namespace scheduler;
@@ -3940,6 +4012,9 @@
if (display->refreshRateSelector().kernelIdleTimerController()) {
features |= Feature::kKernelIdleTimer;
}
+ if (mBackpressureGpuComposition) {
+ features |= Feature::kBackpressureGpuComposition;
+ }
auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
@@ -4257,48 +4332,51 @@
TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
const TransactionHandler::TransactionFlushState& flushState) {
- using TransactionReadiness = TransactionHandler::TransactionReadiness;
const auto& transaction = *flushState.transaction;
- TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+
+ const TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+ const TimePoint expectedPresentTime = mScheduler->expectedPresentTimeForPacesetter();
+
+ using TransactionReadiness = TransactionHandler::TransactionReadiness;
+
// Do not present if the desiredPresentTime has not passed unless it is more than
// one second in the future. We ignore timestamps more than 1 second in the future
// for stability reasons.
- if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
- desiredPresentTime < mExpectedPresentTime + 1s) {
+ if (!transaction.isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
+ desiredPresentTime < expectedPresentTime + 1s) {
ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64,
- desiredPresentTime, mExpectedPresentTime);
+ desiredPresentTime, expectedPresentTime);
return TransactionReadiness::NotReady;
}
- if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
- ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d",
- mExpectedPresentTime, transaction.originUid);
+ if (!mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) {
+ ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime,
+ transaction.originUid);
return TransactionReadiness::NotReady;
}
// If the client didn't specify desiredPresentTime, use the vsyncId to determine the
// expected present time of this transaction.
if (transaction.isAutoTimestamp &&
- frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
+ frameIsEarly(expectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
- transaction.frameTimelineInfo.vsyncId, mExpectedPresentTime);
+ transaction.frameTimelineInfo.vsyncId, expectedPresentTime);
return TransactionReadiness::NotReady;
}
+
return TransactionReadiness::Ready;
}
-TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheckLegacy(
const TransactionHandler::TransactionFlushState& flushState) {
using TransactionReadiness = TransactionHandler::TransactionReadiness;
auto ready = TransactionReadiness::Ready;
- flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s,
- const std::shared_ptr<
- renderengine::
- ExternalTexture>&
- externalTexture)
- -> bool {
- sp<Layer> layer = LayerHandle::getLayer(s.surface);
+ flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
+ resolvedState) -> bool {
+ sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
+
const auto& transaction = *flushState.transaction;
+ const auto& s = resolvedState.state;
// check for barrier frames
if (s.bufferData->hasBarrier) {
// The current producerId is already a newer producer than the buffer that has a
@@ -4306,7 +4384,7 @@
// don't wait on the barrier since we know that's stale information.
if (layer->getDrawingState().barrierProducerId > s.bufferData->producerId) {
layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener,
- externalTexture->getBuffer(),
+ resolvedState.externalTexture->getBuffer(),
s.bufferData->frameNumber,
s.bufferData->acquireFence);
// Delete the entire state at this point and not just release the buffer because
@@ -4343,18 +4421,17 @@
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
- // ignore the acquire fence if LatchUnsignaledConfig::Always is set.
- const bool checkAcquireFence = enableLatchUnsignaledConfig != LatchUnsignaledConfig::Always;
const bool acquireFenceAvailable = s.bufferData &&
s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
s.bufferData->acquireFence;
- const bool fenceSignaled = !checkAcquireFence || !acquireFenceAvailable ||
+ const bool fenceSignaled = !acquireFenceAvailable ||
s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
if (!fenceSignaled) {
// check fence status
- const bool allowLatchUnsignaled =
- shouldLatchUnsignaled(layer, s, transaction.states.size(),
- flushState.firstTransaction);
+ const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
+ flushState.firstTransaction) &&
+ layer->isSimpleBufferUpdate(s);
+
if (allowLatchUnsignaled) {
ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
layer->getDebugName());
@@ -4379,15 +4456,118 @@
return ready;
}
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+ const TransactionHandler::TransactionFlushState& flushState) {
+ using TransactionReadiness = TransactionHandler::TransactionReadiness;
+ auto ready = TransactionReadiness::Ready;
+ flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
+ resolvedState) -> bool {
+ const frontend::RequestedLayerState* layer =
+ mLayerLifecycleManager.getLayerFromId(resolvedState.layerId);
+ const auto& transaction = *flushState.transaction;
+ const auto& s = resolvedState.state;
+ // check for barrier frames
+ if (s.bufferData->hasBarrier) {
+ // The current producerId is already a newer producer than the buffer that has a
+ // barrier. This means the incoming buffer is older and we can release it here. We
+ // don't wait on the barrier since we know that's stale information.
+ if (layer->barrierProducerId > s.bufferData->producerId) {
+ if (s.bufferData->releaseBufferListener) {
+ uint32_t currentMaxAcquiredBufferCount =
+ getMaxAcquiredBufferCountForCurrentRefreshRate(layer->ownerUid.val());
+ ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
+ layer->name.c_str(), s.bufferData->frameNumber);
+ s.bufferData->releaseBufferListener
+ ->onReleaseBuffer({resolvedState.externalTexture->getBuffer()->getId(),
+ s.bufferData->frameNumber},
+ s.bufferData->acquireFence
+ ? s.bufferData->acquireFence
+ : Fence::NO_FENCE,
+ currentMaxAcquiredBufferCount);
+ }
+
+ // Delete the entire state at this point and not just release the buffer because
+ // everything associated with the Layer in this Transaction is now out of date.
+ ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", layer->name.c_str(),
+ layer->barrierProducerId, s.bufferData->producerId);
+ return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
+ }
+
+ if (layer->barrierFrameNumber < s.bufferData->barrierFrameNumber) {
+ const bool willApplyBarrierFrame =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+ ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+ s.bufferData->barrierFrameNumber));
+ if (!willApplyBarrierFrame) {
+ ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
+ layer->name.c_str(), layer->barrierFrameNumber,
+ s.bufferData->barrierFrameNumber);
+ ready = TransactionReadiness::NotReadyBarrier;
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
+ }
+ }
+
+ // If backpressure is enabled and we already have a buffer to commit, keep
+ // the transaction in the queue.
+ const bool hasPendingBuffer =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+ if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
+ ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
+ ready = TransactionReadiness::NotReady;
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
+
+ const bool acquireFenceAvailable = s.bufferData &&
+ s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ s.bufferData->acquireFence;
+ const bool fenceSignaled = !acquireFenceAvailable ||
+ s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
+ if (!fenceSignaled) {
+ // check fence status
+ const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
+ flushState.firstTransaction) &&
+ layer->isSimpleBufferUpdate(s);
+ if (allowLatchUnsignaled) {
+ ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", layer->name.c_str());
+ ready = TransactionReadiness::NotReadyUnsignaled;
+ } else {
+ ready = TransactionReadiness::NotReady;
+ auto& listener = s.bufferData->releaseBufferListener;
+ if (listener &&
+ (flushState.queueProcessTime - transaction.postTime) >
+ std::chrono::nanoseconds(4s).count()) {
+ mTransactionHandler
+ .onTransactionQueueStalled(transaction.id, listener,
+ "Buffer processing hung up due to stuck "
+ "fence. Indicates GPU hang");
+ }
+ ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
+ }
+ return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
+ });
+ return ready;
+}
+
void SurfaceFlinger::addTransactionReadyFilters() {
mTransactionHandler.addTransactionReadyFilter(
std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
- mTransactionHandler.addTransactionReadyFilter(
- std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
+ if (mLayerLifecycleManagerEnabled) {
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this,
+ std::placeholders::_1));
+ } else {
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyBufferCheckLegacy, this,
+ std::placeholders::_1));
+ }
}
// For tests only
bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
+ mTransactionHandler.collectTransactions();
std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
return applyTransactions(transactions, vsyncId);
}
@@ -4440,29 +4620,25 @@
predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
}
-bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
- size_t numStates, bool firstTransaction) const {
+bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t numStates,
+ bool firstTransaction) const {
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
- ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
+ ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
return false;
}
- if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
- ALOGV("%s: true (LatchUnsignaledConfig::Always)", __func__);
- return true;
- }
-
// We only want to latch unsignaled when a single layer is updated in this
// transaction (i.e. not a blast sync transaction).
if (numStates != 1) {
- ALOGV("%s: false (numStates=%zu)", __func__, numStates);
+ ATRACE_FORMAT_INSTANT("%s: false (numStates=%zu)", __func__, numStates);
return false;
}
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
if (!firstTransaction) {
- ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first transaction)",
- __func__);
+ ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first "
+ "transaction)",
+ __func__);
return false;
}
@@ -4470,18 +4646,13 @@
// as it leads to jank due to RenderEngine waiting for unsignaled buffer
// or window animations being slow.
if (mScheduler->vsyncModulator().isVsyncConfigEarly()) {
- ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; isVsyncConfigEarly)",
- __func__);
+ ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; "
+ "isVsyncConfigEarly)",
+ __func__);
return false;
}
}
- if (!layer->simpleBufferUpdate(state)) {
- ALOGV("%s: false (!simpleBufferUpdate)", __func__);
- return false;
- }
-
- ALOGV("%s: true", __func__);
return true;
}
@@ -5612,7 +5783,7 @@
{"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
{"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
{"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
- {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
+ {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy)},
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
{"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
@@ -5630,17 +5801,56 @@
// traversals, which can result in use-after-frees.
std::string compositionLayers;
mScheduler
- ->schedule([&] {
- StringAppendF(&compositionLayers, "Composition layers\n");
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- auto* compositionState = layer->getCompositionState();
- if (!compositionState || !compositionState->isVisible) return;
+ ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+ if (!mLayerLifecycleManagerEnabled) {
+ StringAppendF(&compositionLayers, "Composition layers\n");
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto* compositionState = layer->getCompositionState();
+ if (!compositionState || !compositionState->isVisible) return;
+ android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n",
+ layer,
+ layer->getDebugName()
+ ? layer->getDebugName()
+ : "<unknown>");
+ compositionState->dump(compositionLayers);
+ });
+ } else {
+ std::ostringstream out;
+ out << "\nComposition list\n";
+ ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (snapshot->hasSomethingToDraw()) {
+ if (lastPrintedLayerStackHeader !=
+ snapshot->outputFilter.layerStack) {
+ lastPrintedLayerStackHeader =
+ snapshot->outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id
+ << "\n";
+ }
+ out << " " << *snapshot << "\n";
+ }
+ });
- android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer,
- layer->getDebugName() ? layer->getDebugName()
- : "<unknown>");
- compositionState->dump(compositionLayers);
- });
+ out << "\nInput list\n";
+ lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachInputSnapshot(
+ [&](const frontend::LayerSnapshot& snapshot) {
+ if (lastPrintedLayerStackHeader !=
+ snapshot.outputFilter.layerStack) {
+ lastPrintedLayerStackHeader =
+ snapshot.outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id
+ << "\n";
+ }
+ out << " " << snapshot << "\n";
+ });
+
+ out << "\nLayer Hierarchy\n"
+ << mLayerHierarchyBuilder.getHierarchy() << "\n\n";
+ compositionLayers = out.str();
+ dumpHwcLayersMinidump(compositionLayers);
+ }
})
.get();
@@ -5970,7 +6180,7 @@
result.append(future.get());
}
-void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const {
+void SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy(std::string& result) const {
for (const auto& [token, display] : mDisplays) {
const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
@@ -5982,7 +6192,33 @@
Layer::miniDumpHeader(result);
const DisplayDevice& ref = *display;
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
+ mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDumpLegacy(result, ref); });
+ result.append("\n");
+ }
+}
+
+void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
+ for (const auto& [token, display] : mDisplays) {
+ const auto displayId = HalDisplayId::tryCast(display->getId());
+ if (!displayId) {
+ continue;
+ }
+
+ StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
+ displayId == mActiveDisplayId ? "active" : "inactive");
+ Layer::miniDumpHeader(result);
+
+ const DisplayDevice& ref = *display;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (!snapshot.hasSomethingToDraw() ||
+ ref.getLayerStack() != snapshot.outputFilter.layerStack) {
+ return;
+ }
+ auto it = mLegacyLayers.find(snapshot.sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+ snapshot.getDebugString().c_str());
+ it->second->miniDump(result, snapshot, ref);
+ });
result.append("\n");
}
}
@@ -6027,15 +6263,14 @@
dumpVsync(result);
result.append("\n");
- StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load());
- StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load());
- StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load());
-
/*
* Dump the visible layer list
*/
colorizer.bold(result);
- StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
+ StringAppendF(&result, "SurfaceFlinger New Frontend Enabled:%s\n",
+ mLayerLifecycleManagerEnabled ? "true" : "false");
+ StringAppendF(&result, "Active Layers - layers with client handles (count = %zu)\n",
+ mNumLayers.load());
colorizer.reset(result);
result.append(compositionLayers);
@@ -6108,7 +6343,9 @@
}
result.push_back('\n');
- dumpHwcLayersMinidumpLocked(result);
+ if (mLegacyFrontEndEnabled) {
+ dumpHwcLayersMinidumpLockedLegacy(result);
+ }
{
DumpArgs plannerArgs;
@@ -6147,8 +6384,7 @@
ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId));
StringAppendF(&result, " max send delay (ns): %" PRId64 " ns\n",
windowInfosDebug.maxSendDelayDuration);
- StringAppendF(&result, " unsent messages: %" PRIu32 "\n",
- windowInfosDebug.pendingMessageCount);
+ StringAppendF(&result, " unsent messages: %zu\n", windowInfosDebug.pendingMessageCount);
result.append("\n");
}
@@ -6286,9 +6522,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1042 are currently used for backdoors. The code
+ // Numbers from 1000 to 1044 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1042) {
+ if (code >= 1000 && code <= 1044) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -6731,7 +6967,7 @@
mTransactionTracing->setBufferSize(
TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
} else {
- mTransactionTracing->writeToFile();
+ TransactionTraceWriter::getInstance().invoke("", /* overwrite= */ true);
mTransactionTracing->setBufferSize(
TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
}
@@ -6749,6 +6985,91 @@
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
+ // hdr sdr ratio overlay
+ case 1043: {
+ auto future = mScheduler->schedule(
+ [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+ n = data.readInt32();
+ mHdrSdrRatioOverlay = n != 0;
+ switch (n) {
+ case 0:
+ case 1:
+ enableHdrSdrRatioOverlay(mHdrSdrRatioOverlay);
+ break;
+ default:
+ reply->writeBool(isHdrSdrRatioOverlayEnabled());
+ }
+ });
+ future.wait();
+ return NO_ERROR;
+ }
+
+ case 1044: { // Enable/Disable mirroring from one display to another
+ /*
+ * Mirror one display onto another.
+ * Ensure the source and destination displays are on.
+ * Commands:
+ * 0: Mirror one display to another
+ * 1: Disable mirroring to a previously mirrored display
+ * 2: Disable mirroring on previously mirrored displays
+ *
+ * Ex:
+ * Get the display ids:
+ * adb shell dumpsys SurfaceFlinger --display-id
+ * Mirror first display to the second:
+ * adb shell service call SurfaceFlinger 1044 i64 0 i64 4619827677550801152 i64
+ * 4619827677550801153
+ * Stop mirroring:
+ * adb shell service call SurfaceFlinger 1044 i64 1
+ */
+
+ int64_t arg0 = data.readInt64();
+
+ switch (arg0) {
+ case 0: {
+ // Mirror arg1 to arg2
+ int64_t arg1 = data.readInt64();
+ int64_t arg2 = data.readInt64();
+ // Enable mirroring for one display
+ const auto display1id = DisplayId::fromValue(arg1);
+ auto mirrorRoot = SurfaceComposerClient::getDefault()->mirrorDisplay(
+ display1id.value());
+ auto id2 = DisplayId::fromValue<PhysicalDisplayId>(arg2);
+ const auto token2 = getPhysicalDisplayToken(*id2);
+ ui::LayerStack layerStack;
+ {
+ Mutex::Autolock lock(mStateLock);
+ sp<DisplayDevice> display = getDisplayDeviceLocked(token2);
+ layerStack = display->getLayerStack();
+ }
+ SurfaceComposerClient::Transaction t;
+ t.setDisplayLayerStack(token2, layerStack);
+ t.setLayer(mirrorRoot, INT_MAX); // Top-most layer
+ t.setLayerStack(mirrorRoot, layerStack);
+ t.apply();
+
+ mMirrorMapForDebug.emplace_or_replace(arg2, mirrorRoot);
+ break;
+ }
+
+ case 1: {
+ // Disable mirroring for arg1
+ int64_t arg1 = data.readInt64();
+ mMirrorMapForDebug.erase(arg1);
+ break;
+ }
+
+ case 2: {
+ // Disable mirroring for all displays
+ mMirrorMapForDebug.clear();
+ break;
+ }
+
+ default:
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+ }
}
}
return err;
@@ -7411,6 +7732,16 @@
renderArea->getHintForSeamlessTransition());
sdrWhitePointNits = state.sdrWhitePointNits;
displayBrightnessNits = state.displayBrightnessNits;
+ // 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 (requestedDataspace == ui::Dataspace::UNKNOWN) {
renderIntent = state.renderIntent;
@@ -7465,7 +7796,8 @@
.sdrWhitePointNits = sdrWhitePointNits,
.displayBrightnessNits = displayBrightnessNits,
.targetBrightness = targetBrightness,
- .regionSampling = regionSampling});
+ .regionSampling = regionSampling,
+ .treat170mAsSrgb = mTreat170mAsSrgb});
const float colorSaturation = grayscale ? 0 : 1;
compositionengine::CompositionRefreshArgs refreshArgs{
@@ -7654,7 +7986,13 @@
return INVALID_OPERATION;
}
- setDesiredActiveMode({std::move(preferredMode), .emitEvent = true}, force);
+ setDesiredActiveMode({preferredMode, .emitEvent = true}, force);
+
+ // Update the frameRateOverride list as the display render rate might have changed
+ if (mScheduler->updateFrameRateOverrides(/*consideredSignals*/ {}, preferredMode.fps)) {
+ triggerOnFrameRateOverridesChanged();
+ }
+
return NO_ERROR;
}
@@ -7755,6 +8093,7 @@
if (mTransactionTracing) {
mTransactionTracing->onLayerRemoved(layer->getSequence());
}
+ mScheduler->onLayerDestroyed(layer);
}
void SurfaceFlinger::onLayerUpdate() {
@@ -7840,6 +8179,16 @@
}
}
+void SurfaceFlinger::enableHdrSdrRatioOverlay(bool enable) {
+ for (const auto& [id, display] : mPhysicalDisplays) {
+ if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+ if (const auto device = getDisplayDeviceLocked(id)) {
+ device->enableHdrSdrRatioOverlay(enable);
+ }
+ }
+ }
+}
+
int SurfaceFlinger::getGpuContextPriority() {
return getRenderEngine().getContextPriority();
}
@@ -7850,7 +8199,7 @@
if (presentLatency.count() % refreshRate.getPeriodNsecs()) {
pipelineDepth++;
}
- return std::max(1ll, pipelineDepth - 1);
+ return std::max(minAcquiredBuffers, static_cast<int64_t>(pipelineDepth - 1));
}
status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
@@ -7977,9 +8326,9 @@
forceApplyPolicy);
}
-status_t SurfaceFlinger::addWindowInfosListener(
- const sp<IWindowInfosListener>& windowInfosListener) {
- mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
+status_t SurfaceFlinger::addWindowInfosListener(const sp<IWindowInfosListener>& windowInfosListener,
+ gui::WindowInfosListenerInfo* outInfo) {
+ mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener, outInfo);
setTransactionFlags(eInputInfoUpdateNeeded);
return NO_ERROR;
}
@@ -8217,7 +8566,7 @@
if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
return;
}
- if (uid != CaptureArgs::UNSET_UID && snapshot->uid != uid) {
+ if (uid != CaptureArgs::UNSET_UID && snapshot->uid != gui::Uid(uid)) {
return;
}
if (!snapshot->hasSomethingToDraw()) {
@@ -8324,6 +8673,7 @@
// 2. Transactions and created layers do not share a lock. To prevent applying
// transactions with layers still in the createdLayer queue, flush the transactions
// before committing the created layers.
+ mTransactionHandler.collectTransactions();
update.transactions = mTransactionHandler.flushTransactions();
{
// TODO(b/238781169) lockless queue this and keep order.
@@ -9062,7 +9412,8 @@
}
binder::Status SurfaceComposerAIDL::addWindowInfosListener(
- const sp<gui::IWindowInfosListener>& windowInfosListener) {
+ const sp<gui::IWindowInfosListener>& windowInfosListener,
+ gui::WindowInfosListenerInfo* outInfo) {
status_t status;
const int pid = IPCThreadState::self()->getCallingPid();
const int uid = IPCThreadState::self()->getCallingUid();
@@ -9070,7 +9421,7 @@
// WindowInfosListeners
if (uid == AID_SYSTEM || uid == AID_GRAPHICS ||
checkPermission(sAccessSurfaceFlinger, pid, uid)) {
- status = mFlinger->addWindowInfosListener(windowInfosListener);
+ status = mFlinger->addWindowInfosListener(windowInfosListener, outInfo);
} else {
status = PERMISSION_DENIED;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b7d2047..b07910d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -194,7 +194,8 @@
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
private ICompositor,
- private scheduler::ISchedulerCallback {
+ private scheduler::ISchedulerCallback,
+ private compositionengine::ICEPowerCallback {
public:
struct SkipInitializationTag {};
@@ -226,6 +227,10 @@
// FramebufferSurface
static int64_t maxFrameBufferAcquiredBuffers;
+ // Controls the minimum acquired buffers SurfaceFlinger will suggest via
+ // ISurfaceComposer.getMaxAcquiredBufferCount().
+ static int64_t minAcquiredBuffers;
+
// Controls the maximum width and height in pixels that the graphics pipeline can support for
// GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
static uint32_t maxGraphicsWidth;
@@ -612,7 +617,8 @@
status_t getMaxAcquiredBufferCount(int* buffers) const;
- status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
+ status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener,
+ gui::WindowInfosListenerInfo* outResult);
status_t removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) const;
@@ -631,9 +637,12 @@
void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override;
// ICompositor overrides:
- void configure() override;
- bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) override;
- void composite(TimePoint frameTime, VsyncId) override;
+ void configure() override REQUIRES(kMainThreadContext);
+ bool commit(const scheduler::FrameTarget&) override REQUIRES(kMainThreadContext);
+ CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters&) override
+ REQUIRES(kMainThreadContext);
+
void sample() override;
// ISchedulerCallback overrides:
@@ -641,6 +650,10 @@
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
void triggerOnFrameRateOverridesChanged() override;
+ void onChoreographerAttached() override;
+
+ // ICEPowerCallback overrides:
+ void notifyCpuLoadUp() override;
// Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
void toggleKernelIdleTimer() REQUIRES(mStateLock);
@@ -664,6 +677,8 @@
bool mRefreshRateOverlayRenderRate = false;
// Show render rate overlay offseted to the middle of the screen (e.g. for circular displays)
bool mRefreshRateOverlayShowInMiddle = false;
+ // Show hdr sdr ratio overlay
+ bool mHdrSdrRatioOverlay = false;
void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
REQUIRES(mStateLock);
@@ -710,10 +725,9 @@
compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly);
void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
const std::vector<std::pair<Layer*, LayerFE*>>& layers);
- bool updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
- bool transactionsFlushed, bool& out)
- REQUIRES(kMainThreadContext);
- bool updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update, bool transactionsFlushed,
+ bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
+ bool& out) REQUIRES(kMainThreadContext);
+ bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
@@ -757,6 +771,9 @@
TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
+ TransactionHandler::TransactionReadiness transactionReadyBufferCheckLegacy(
+ const TransactionHandler::TransactionFlushState& flushState)
+ REQUIRES(kMainThreadContext);
TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
@@ -781,8 +798,7 @@
void commitOffscreenLayers();
static LatchUnsignaledConfig getLatchUnsignaledConfig();
- bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
- bool firstTransaction) const;
+ bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -879,6 +895,14 @@
return findDisplay([id](const auto& display) { return display.getId() == id; });
}
+ std::shared_ptr<compositionengine::Display> getCompositionDisplayLocked(DisplayId id) const
+ REQUIRES(mStateLock) {
+ if (const auto display = getDisplayDeviceLocked(id)) {
+ return display->getCompositionDisplay();
+ }
+ return nullptr;
+ }
+
// Returns the primary display or (for foldables) the active display, assuming that the inner
// and outer displays have mutually exclusive power states.
sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
@@ -952,7 +976,8 @@
/*
* Compositing
*/
- void postComposition(nsecs_t callTime) REQUIRES(kMainThreadContext);
+ void postComposition(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&,
+ nsecs_t presentStartTime) REQUIRES(kMainThreadContext);
/*
* Display management
@@ -993,20 +1018,6 @@
*/
nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
- using FenceTimePtr = std::shared_ptr<FenceTime>;
-
- bool wouldPresentEarly(TimePoint frameTime, Period) const REQUIRES(kMainThreadContext);
-
- const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period) const
- REQUIRES(kMainThreadContext);
-
- // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal.
- static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
-
- // Calculates the expected present time for this frame. For negative offsets, performs a
- // correction using the predicted vsync for the next frame instead.
- TimePoint calculateExpectedPresentTime(TimePoint frameTime) const;
-
/*
* Display identification
*/
@@ -1053,7 +1064,8 @@
*/
void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
std::string& result) const REQUIRES(mStateLock);
- void dumpHwcLayersMinidumpLocked(std::string& result) const REQUIRES(mStateLock);
+ void dumpHwcLayersMinidump(std::string& result) const REQUIRES(mStateLock, kMainThreadContext);
+ void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
void listLayersLocked(std::string& result) const;
@@ -1184,6 +1196,7 @@
bool mUpdateInputInfo = false;
bool mSomeChildrenChanged;
bool mForceTransactionDisplayChange = false;
+ bool mUpdateAttachedChoreographer = false;
// Set if LayerMetadata has changed since the last LayerMetadata snapshot.
bool mLayerMetadataSnapshotNeeded = false;
@@ -1252,9 +1265,6 @@
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
- std::atomic<uint32_t> mFrameMissedCount = 0;
- std::atomic<uint32_t> mHwcFrameMissedCount = 0;
- std::atomic<uint32_t> mGpuFrameMissedCount = 0;
TransactionCallbackInvoker mTransactionCallbackInvoker;
@@ -1293,15 +1303,13 @@
ui::Dataspace mDefaultCompositionDataspace;
ui::Dataspace mWideColorGamutCompositionDataspace;
- ui::Dataspace mColorSpaceAgnosticDataspace;
- float mDimmingRatio = -1.f;
std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
std::atomic<int> mNumTrustedPresentationListeners = 0;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
- CompositionCoverageFlags mCompositionCoverage;
+ CompositionCoveragePerDisplay mCompositionCoverage;
// mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
// any mutex.
@@ -1322,15 +1330,6 @@
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
- struct FenceWithFenceTime {
- sp<Fence> fence = Fence::NO_FENCE;
- FenceTimePtr fenceTime = FenceTime::NO_FENCE;
- };
- std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
-
- TimePoint mScheduledPresentTime GUARDED_BY(kMainThreadContext);
- TimePoint mExpectedPresentTime GUARDED_BY(kMainThreadContext);
-
// below flags are set by main thread only
bool mSetActiveModePending = false;
@@ -1351,6 +1350,8 @@
void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
+ void enableHdrSdrRatioOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
+
// Flag used to set override desired display mode from backdoor
bool mDebugDisplayModeSetByBackdoor = false;
@@ -1396,6 +1397,10 @@
return hasDisplay(
[](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
}
+ bool isHdrSdrRatioOverlayEnabled() const REQUIRES(mStateLock) {
+ return hasDisplay(
+ [](const auto& display) { return display.isHdrSdrRatioOverlayEnabled(); });
+ }
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
std::optional<ui::LayerStack> layerStack, uint32_t uid,
std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
@@ -1438,6 +1443,10 @@
// WindowInfo ids visible during the last commit.
std::unordered_set<int32_t> mVisibleWindowIds;
+
+ // Mirroring
+ // Map of displayid to mirrorRoot
+ ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1547,8 +1556,8 @@
binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
binder::Status getGpuContextPriority(int32_t* outPriority) override;
binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
- binder::Status addWindowInfosListener(
- const sp<gui::IWindowInfosListener>& windowInfosListener) override;
+ binder::Status addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener,
+ gui::WindowInfosListenerInfo* outInfo) override;
binder::Status removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) override;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 20fa091..66c8f33 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -227,14 +227,6 @@
return static_cast<int32_t>(defaultValue);
}
-int64_t color_space_agnostic_dataspace(Dataspace defaultValue) {
- auto temp = SurfaceFlingerProperties::color_space_agnostic_dataspace();
- if (temp.has_value()) {
- return *temp;
- }
- return static_cast<int64_t>(defaultValue);
-}
-
bool refresh_rate_switching(bool defaultValue) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -375,5 +367,9 @@
return SurfaceFlingerProperties::ignore_hdr_camera_layers().value_or(defaultValue);
}
+bool clear_slots_with_set_layer_buffer(bool defaultValue) {
+ return SurfaceFlingerProperties::clear_slots_with_set_layer_buffer().value_or(defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 080feee..a080420 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -71,9 +71,6 @@
int32_t wcg_composition_pixel_format(
android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
-int64_t color_space_agnostic_dataspace(
- android::hardware::graphics::common::V1_2::Dataspace defaultValue);
-
bool refresh_rate_switching(bool defaultValue);
int32_t set_idle_timer_ms(int32_t defaultValue);
@@ -102,6 +99,8 @@
bool ignore_hdr_camera_layers(bool defaultValue);
+bool clear_slots_with_set_layer_buffer(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 155a275..5512734 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -1,4 +1,9 @@
{
+ "imports": [
+ {
+ "path": "frameworks/native/libs/gui"
+ }
+ ],
"presubmit": [
{
"name": "libsurfaceflinger_unittest"
@@ -7,15 +12,22 @@
"name": "libcompositionengine_test"
},
{
- "name": "libgui_test",
+ "name": "libscheduler_test"
+ },
+ {
+ "name": "CtsGraphicsTestCases",
+ // flaky on mixed gsi builds
"options": [
{
- "native-test-flag": "--gtest_filter=\"InputSurfacesTest*:MultiDisplayTests*\""
+ "exclude-filter": "android.graphics.cts.CameraGpuTest#testCameraImageCaptureAndRendering"
+ },
+ {
+ "exclude-filter": "android.graphics.cts.AnimatorLeakTest#testPauseResume"
}
]
},
{
- "name": "libscheduler_test"
+ "name": "CtsSurfaceControlTests"
}
],
"hwasan-presubmit": [
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 630cef1..368cb41 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -106,7 +106,8 @@
atom->set_client_composition_frames(mTimeStats.clientCompositionFramesLegacy);
atom->set_display_on_millis(mTimeStats.displayOnTimeLegacy);
atom->set_animation_millis(mTimeStats.presentToPresentLegacy.totalTime());
- atom->set_event_connection_count(mTimeStats.displayEventConnectionsCountLegacy);
+ // Deprecated
+ atom->set_event_connection_count(0);
*atom->mutable_frame_duration() =
histogramToProto(mTimeStats.frameDurationLegacy.hist, mMaxPulledHistogramBuckets);
*atom->mutable_render_engine_timing() =
@@ -356,16 +357,6 @@
mTimeStats.refreshRateSwitchesLegacy++;
}
-void TimeStats::recordDisplayEventConnectionCount(int32_t count) {
- if (!mEnabled.load()) return;
-
- ATRACE_CALL();
-
- std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.displayEventConnectionsCountLegacy =
- std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
-}
-
static int32_t toMs(nsecs_t nanos) {
int64_t millis =
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds(nanos))
@@ -1072,7 +1063,6 @@
mTimeStats.compositionStrategyPredictedLegacy = 0;
mTimeStats.compositionStrategyPredictionSucceededLegacy = 0;
mTimeStats.refreshRateSwitchesLegacy = 0;
- mTimeStats.displayEventConnectionsCountLegacy = 0;
mTimeStats.displayOnTimeLegacy = 0;
mTimeStats.presentToPresentLegacy.hist.clear();
mTimeStats.frameDurationLegacy.hist.clear();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5f58657..0c227d4 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -57,9 +57,6 @@
virtual void incrementMissedFrames() = 0;
// Increments the number of times the display refresh rate changed.
virtual void incrementRefreshRateSwitches() = 0;
- // Records the most up-to-date count of display event connections.
- // The stored count will be the maximum ever recoded.
- virtual void recordDisplayEventConnectionCount(int32_t count) = 0;
// Records the start and end times for a frame.
// The start time is the same as the beginning of a SurfaceFlinger
@@ -253,7 +250,6 @@
void incrementTotalFrames() override;
void incrementMissedFrames() override;
void incrementRefreshRateSwitches() override;
- void recordDisplayEventConnectionCount(int32_t count) override;
void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 60aa810..9e97f0d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -175,7 +175,6 @@
int32_t clientCompositionReusedFramesLegacy = 0;
int32_t refreshRateSwitchesLegacy = 0;
int32_t compositionStrategyChangesLegacy = 0;
- int32_t displayEventConnectionsCountLegacy = 0;
int64_t displayOnTimeLegacy = 0;
Histogram presentToPresentLegacy;
Histogram frameDurationLegacy;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index dafdc8a..b1e3d63 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -195,6 +195,7 @@
windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
windowInfoProto->set_layout_params_type(
static_cast<int32_t>(inputInfo->layoutParamsType));
+ windowInfoProto->set_input_config(inputInfo->inputConfig.get());
LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
windowInfoProto->mutable_touchable_region());
windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
@@ -467,11 +468,9 @@
static_cast<gui::WindowInfo::Type>(windowInfoProto.layout_params_type());
LayerProtoHelper::readFromProto(windowInfoProto.touchable_region(),
inputInfo.touchableRegion);
+ inputInfo.inputConfig =
+ ftl::Flags<gui::WindowInfo::InputConfig>(windowInfoProto.input_config());
inputInfo.surfaceInset = windowInfoProto.surface_inset();
- inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_FOCUSABLE,
- !windowInfoProto.focusable());
- inputInfo.setInputConfig(gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER,
- windowInfoProto.has_wallpaper());
inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
const proto::Transform& transformProto = windowInfoProto.transform();
inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 632de01..7e330b9 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -28,6 +28,7 @@
#include "TransactionTracing.h"
namespace android {
+ANDROID_SINGLETON_STATIC_INSTANCE(android::TransactionTraceWriter)
TransactionTracing::TransactionTracing()
: mProtoParser(std::make_unique<TransactionProtoParser::FlingerDataMapper>()) {
@@ -56,7 +57,7 @@
writeToFile();
}
-status_t TransactionTracing::writeToFile(std::string filename) {
+status_t TransactionTracing::writeToFile(const std::string& filename) {
std::scoped_lock lock(mTraceLock);
proto::TransactionTraceFile fileProto = createTraceFileProto();
addStartingStateToProtoLocked(fileProto);
@@ -115,6 +116,7 @@
}
mPendingUpdates.emplace_back(update);
tryPushToTracingThread();
+ mLastUpdatedVsyncId = vsyncId;
}
void TransactionTracing::loop() {
@@ -218,19 +220,29 @@
mTransactionsAddedToBufferCv.notify_one();
}
-void TransactionTracing::flush(int64_t vsyncId) {
- while (!mPendingUpdates.empty() || !mPendingDestroyedLayers.empty()) {
- tryPushToTracingThread();
+void TransactionTracing::flush() {
+ {
+ std::scoped_lock lock(mMainThreadLock);
+ // Collect any pending transactions and wait for transactions to be added to
+ mUpdates.insert(mUpdates.end(), std::make_move_iterator(mPendingUpdates.begin()),
+ std::make_move_iterator(mPendingUpdates.end()));
+ mPendingUpdates.clear();
+ mDestroyedLayers.insert(mDestroyedLayers.end(), mPendingDestroyedLayers.begin(),
+ mPendingDestroyedLayers.end());
+ mPendingDestroyedLayers.clear();
+ mTransactionsAvailableCv.notify_one();
}
std::unique_lock<std::mutex> lock(mTraceLock);
base::ScopedLockAssertion assumeLocked(mTraceLock);
- mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
- proto::TransactionTraceEntry entry;
- if (mBuffer.used() > 0) {
- entry.ParseFromString(mBuffer.back());
- }
- return mBuffer.used() > 0 && entry.vsync_id() >= vsyncId;
- });
+ mTransactionsAddedToBufferCv.wait_for(lock, std::chrono::milliseconds(100),
+ [&]() REQUIRES(mTraceLock) {
+ proto::TransactionTraceEntry entry;
+ if (mBuffer.used() > 0) {
+ entry.ParseFromString(mBuffer.back());
+ }
+ return mBuffer.used() > 0 &&
+ entry.vsync_id() >= mLastUpdatedVsyncId;
+ });
}
void TransactionTracing::onLayerRemoved(int32_t layerId) {
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 0e56627..a59dc6e 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -19,6 +19,7 @@
#include <android-base/thread_annotations.h>
#include <layerproto/TransactionProto.h>
#include <utils/Errors.h>
+#include <utils/Singleton.h>
#include <utils/Timers.h>
#include <memory>
@@ -60,10 +61,12 @@
void addQueuedTransaction(const TransactionState&);
void addCommittedTransactions(int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
const frontend::DisplayInfos&, bool displayInfoChanged);
- status_t writeToFile(std::string filename = FILE_NAME);
+ status_t writeToFile(const std::string& filename = FILE_PATH);
void setBufferSize(size_t bufferSizeInBytes);
void onLayerRemoved(int layerId);
void dump(std::string&) const;
+ // Wait until all the committed transactions for the specified vsync id are added to the buffer.
+ void flush() EXCLUDES(mMainThreadLock);
static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
// version 1 - switching to support new frontend
@@ -73,7 +76,9 @@
friend class TransactionTracingTest;
friend class SurfaceFlinger;
- static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
+ static constexpr auto DIR_NAME = "/data/misc/wmtrace/";
+ static constexpr auto FILE_NAME = "transactions_trace.winscope";
+ static constexpr auto FILE_PATH = "/data/misc/wmtrace/transactions_trace.winscope";
mutable std::mutex mTraceLock;
RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer
@@ -111,6 +116,7 @@
std::vector<uint32_t /* layerId */> mDestroyedLayers GUARDED_BY(mMainThreadLock);
std::vector<uint32_t /* layerId */> mPendingDestroyedLayers; // only accessed by main thread
+ int64_t mLastUpdatedVsyncId = -1;
proto::TransactionTraceFile createTraceFileProto() const;
void loop();
@@ -121,10 +127,21 @@
void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
// TEST
- // Wait until all the committed transactions for the specified vsync id are added to the buffer.
- void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
// Return buffer contents as trace file proto
proto::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
};
+class TransactionTraceWriter : public Singleton<TransactionTraceWriter> {
+ friend class Singleton<TransactionTracing>;
+ std::function<void(const std::string& prefix, bool overwrite)> mWriterFunction =
+ [](const std::string&, bool) {};
+
+public:
+ void setWriterFunction(
+ std::function<void(const std::string& prefix, bool overwrite)> function) {
+ mWriterFunction = std::move(function);
+ }
+ void invoke(const std::string& prefix, bool overwrite) { mWriterFunction(prefix, overwrite); }
+};
+
} // namespace android
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 72a11c6..519ef44 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -41,7 +41,7 @@
using namespace ftl::flag_operators;
bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile,
- const char* outputLayersTracePath) {
+ const char* outputLayersTracePath, bool onlyLastEntry) {
if (traceFile.entry_size() == 0) {
ALOGD("Trace file is empty");
return false;
@@ -158,9 +158,11 @@
layerTracing.getFlags())
.generate(hierarchyBuilder.getHierarchy());
auto displayProtos = LayerProtoHelper::writeDisplayInfoToProto(displayInfos);
- layerTracing.notify(visibleRegionsDirty, entry.elapsed_realtime_nanos(), entry.vsync_id(),
- &layersProto, {}, &displayProtos);
- layerTracing.appendToStream(out);
+ if (!onlyLastEntry || (i == traceFile.entry_size() - 1)) {
+ layerTracing.notify(visibleRegionsDirty, entry.elapsed_realtime_nanos(),
+ entry.vsync_id(), &layersProto, {}, &displayProtos);
+ layerTracing.appendToStream(out);
+ }
}
layerTracing.disable("", /*writeToFile=*/false);
out.close();
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
index ee1ea6c..e41d1e6 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
@@ -21,6 +21,7 @@
namespace android {
class LayerTraceGenerator {
public:
- bool generate(const proto::TransactionTraceFile&, const char* outputLayersTracePath);
+ bool generate(const proto::TransactionTraceFile&, const char* outputLayersTracePath,
+ bool onlyLastEntry);
};
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index c440c19..5ca87e4 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -26,9 +26,9 @@
using namespace android;
int main(int argc, char** argv) {
- if (argc > 3) {
+ if (argc > 4) {
std::cout << "Usage: " << argv[0]
- << " [transaction-trace-path] [output-layers-trace-path]\n";
+ << " [transaction-trace-path] [output-layers-trace-path] [--last-entry-only]\n";
return -1;
}
@@ -48,12 +48,16 @@
}
const char* outputLayersTracePath =
- (argc == 3) ? argv[2] : "/data/misc/wmtrace/layers_trace.winscope";
- ;
+ (argc >= 3) ? argv[2] : "/data/misc/wmtrace/layers_trace.winscope";
+
+ const bool generateLastEntryOnly =
+ argc >= 4 && std::string_view(argv[3]) == "--last-entry-only";
+
ALOGD("Generating %s...", outputLayersTracePath);
std::cout << "Generating " << outputLayersTracePath << "\n";
- if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) {
+ if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath,
+ generateLastEntryOnly)) {
std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
return -1;
}
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 7132a59..31cd2d7 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -89,7 +89,7 @@
void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) {
for (auto state = states.begin(); state != states.end();) {
if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) {
- int result = visitor(state->state, state->externalTexture);
+ int result = visitor(*state);
if (result == STOP_TRAVERSAL) return;
if (result == DELETE_AND_CONTINUE_TRAVERSAL) {
state = states.erase(state);
diff --git a/services/surfaceflinger/Utils/OverlayUtils.h b/services/surfaceflinger/Utils/OverlayUtils.h
new file mode 100644
index 0000000..0ef0be1
--- /dev/null
+++ b/services/surfaceflinger/Utils/OverlayUtils.h
@@ -0,0 +1,146 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#pragma once
+
+#include "BackgroundExecutor.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#pragma clang diagnostic pop
+
+#include <gui/SurfaceComposerClient.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+inline constexpr int kDigitWidth = 64;
+inline constexpr int kDigitHeight = 100;
+inline constexpr int kDigitSpace = 16;
+
+// HdrSdrRatioOverlay re-uses this value though it doesn't really need such amount buffer.
+// for output good-looking and code conciseness.
+inline constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+inline constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
+inline constexpr int kBufferHeight = kDigitHeight;
+
+class SurfaceControl;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+ explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+
+ ~SurfaceControlHolder() {
+ // Hand the sp<SurfaceControl> to the helper thread to release the last
+ // reference. This makes sure that the SurfaceControl is destructed without
+ // SurfaceFlinger::mStateLock held.
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+ }
+
+ static std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder(const String8& name) {
+ sp<SurfaceControl> surfaceControl =
+ SurfaceComposerClient::getDefault()
+ ->createSurface(name, kBufferWidth, kBufferHeight, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+ }
+
+ const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+ sp<SurfaceControl> mSurfaceControl;
+};
+
+// Helper class to draw digit and decimal point.
+class SegmentDrawer {
+public:
+ enum class Segment {
+ Upper,
+ UpperLeft,
+ UpperRight,
+ Middle,
+ LowerLeft,
+ LowerRight,
+ Bottom,
+ DecimalPoint
+ };
+ static void drawSegment(Segment segment, int left, SkColor color, SkCanvas& canvas) {
+ const SkRect rect = [&]() {
+ switch (segment) {
+ case Segment::Upper:
+ return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace);
+ case Segment::UpperLeft:
+ return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2.);
+ case Segment::UpperRight:
+ return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth,
+ kDigitHeight / 2.);
+ case Segment::Middle:
+ return SkRect::MakeLTRB(left, kDigitHeight / 2. - kDigitSpace / 2.,
+ left + kDigitWidth,
+ kDigitHeight / 2. + kDigitSpace / 2.);
+ case Segment::LowerLeft:
+ return SkRect::MakeLTRB(left, kDigitHeight / 2., left + kDigitSpace,
+ kDigitHeight);
+ case Segment::LowerRight:
+ return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2.,
+ left + kDigitWidth, kDigitHeight);
+ case Segment::Bottom:
+ return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth,
+ kDigitHeight);
+ case Segment::DecimalPoint:
+ return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitSpace,
+ kDigitHeight);
+ }
+ }();
+
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas.drawRect(rect, paint);
+ }
+
+ static void drawDigit(int digit, int left, SkColor color, SkCanvas& canvas) {
+ if (digit < 0 || digit > 9) return;
+
+ if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
+ digit == 8 || digit == 9)
+ drawSegment(Segment::Upper, left, color, canvas);
+ if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
+ drawSegment(Segment::UpperLeft, left, color, canvas);
+ if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
+ digit == 8 || digit == 9)
+ drawSegment(Segment::UpperRight, left, color, canvas);
+ if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
+ digit == 9)
+ drawSegment(Segment::Middle, left, color, canvas);
+ if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
+ drawSegment(Segment::LowerLeft, left, color, canvas);
+ if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
+ digit == 7 || digit == 8 || digit == 9)
+ drawSegment(Segment::LowerRight, left, color, canvas);
+ if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
+ digit == 9)
+ drawSegment(Segment::Bottom, left, color, canvas);
+ }
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 20699ef..7062a4e 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-#include <ftl/small_vector.h>
+#include <android/gui/BnWindowInfosPublisher.h>
+#include <android/gui/IWindowInfosPublisher.h>
+#include <android/gui/WindowInfosListenerInfo.h>
#include <gui/ISurfaceComposer.h>
#include <gui/TraceUtils.h>
#include <gui/WindowInfosUpdate.h>
@@ -23,162 +25,130 @@
#include "BackgroundExecutor.h"
#include "WindowInfosListenerInvoker.h"
+#undef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
namespace android {
using gui::DisplayInfo;
using gui::IWindowInfosListener;
using gui::WindowInfo;
-using WindowInfosListenerVector = ftl::SmallVector<const sp<gui::IWindowInfosListener>, 3>;
+void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener,
+ gui::WindowInfosListenerInfo* outInfo) {
+ int64_t listenerId = mNextListenerId++;
+ outInfo->listenerId = listenerId;
+ outInfo->windowInfosPublisher = sp<gui::IWindowInfosPublisher>::fromExisting(this);
-struct WindowInfosReportedListenerInvoker : gui::BnWindowInfosReportedListener,
- IBinder::DeathRecipient {
- WindowInfosReportedListenerInvoker(WindowInfosListenerVector windowInfosListeners,
- WindowInfosReportedListenerSet windowInfosReportedListeners)
- : mCallbacksPending(windowInfosListeners.size()),
- mWindowInfosListeners(std::move(windowInfosListeners)),
- mWindowInfosReportedListeners(std::move(windowInfosReportedListeners)) {}
-
- binder::Status onWindowInfosReported() override {
- if (--mCallbacksPending == 0) {
- for (const auto& listener : mWindowInfosReportedListeners) {
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[this, listener = std::move(listener), listenerId]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::addWindowInfosListener");
sp<IBinder> asBinder = IInterface::asBinder(listener);
- if (asBinder->isBinderAlive()) {
- listener->onWindowInfosReported();
- }
- }
-
- auto wpThis = wp<WindowInfosReportedListenerInvoker>::fromExisting(this);
- for (const auto& listener : mWindowInfosListeners) {
- sp<IBinder> binder = IInterface::asBinder(listener);
- binder->unlinkToDeath(wpThis);
- }
- }
- return binder::Status::ok();
- }
-
- void binderDied(const wp<IBinder>&) { onWindowInfosReported(); }
-
-private:
- std::atomic<size_t> mCallbacksPending;
- static constexpr size_t kStaticCapacity = 3;
- const WindowInfosListenerVector mWindowInfosListeners;
- WindowInfosReportedListenerSet mWindowInfosReportedListeners;
-};
-
-void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
- asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
-
- std::scoped_lock lock(mListenersMutex);
- mWindowInfosListeners.try_emplace(asBinder, std::move(listener));
+ asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
+ mWindowInfosListeners.try_emplace(asBinder,
+ std::make_pair(listenerId, std::move(listener)));
+ }});
}
void WindowInfosListenerInvoker::removeWindowInfosListener(
const sp<IWindowInfosListener>& listener) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
-
- std::scoped_lock lock(mListenersMutex);
- asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
- mWindowInfosListeners.erase(asBinder);
+ BackgroundExecutor::getInstance().sendCallbacks({[this, listener]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::removeWindowInfosListener");
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
+ mWindowInfosListeners.erase(asBinder);
+ }});
}
void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) {
- std::scoped_lock lock(mListenersMutex);
- mWindowInfosListeners.erase(who);
+ BackgroundExecutor::getInstance().sendCallbacks({[this, who]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::binderDied");
+ auto it = mWindowInfosListeners.find(who);
+ int64_t listenerId = it->second.first;
+ mWindowInfosListeners.erase(who);
+
+ std::vector<int64_t> vsyncIds;
+ for (auto& [vsyncId, state] : mUnackedState) {
+ if (std::find(state.unackedListenerIds.begin(), state.unackedListenerIds.end(),
+ listenerId) != state.unackedListenerIds.end()) {
+ vsyncIds.push_back(vsyncId);
+ }
+ }
+
+ for (int64_t vsyncId : vsyncIds) {
+ ackWindowInfosReceived(vsyncId, listenerId);
+ }
+ }});
}
void WindowInfosListenerInvoker::windowInfosChanged(
gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
bool forceImmediateCall) {
- WindowInfosListenerVector listeners;
- {
- std::scoped_lock lock{mMessagesMutex};
+ if (!mDelayInfo) {
+ mDelayInfo = DelayInfo{
+ .vsyncId = update.vsyncId,
+ .frameTime = update.timestamp,
+ };
+ }
- if (!mDelayInfo) {
- mDelayInfo = DelayInfo{
- .vsyncId = update.vsyncId,
- .frameTime = update.timestamp,
- };
- }
+ // If there are unacked messages and this isn't a forced call, then return immediately.
+ // If a forced window infos change doesn't happen first, the update will be sent after
+ // the WindowInfosReportedListeners are called. If a forced window infos change happens or
+ // if there are subsequent delayed messages before this update is sent, then this message
+ // will be dropped and the listeners will only be called with the latest info. This is done
+ // to reduce the amount of binder memory used.
+ if (!mUnackedState.empty() && !forceImmediateCall) {
+ mDelayedUpdate = std::move(update);
+ mReportedListeners.merge(reportedListeners);
+ return;
+ }
- // If there are unacked messages and this isn't a forced call, then return immediately.
- // If a forced window infos change doesn't happen first, the update will be sent after
- // the WindowInfosReportedListeners are called. If a forced window infos change happens or
- // if there are subsequent delayed messages before this update is sent, then this message
- // will be dropped and the listeners will only be called with the latest info. This is done
- // to reduce the amount of binder memory used.
- if (mActiveMessageCount > 0 && !forceImmediateCall) {
- mDelayedUpdate = std::move(update);
- mReportedListeners.merge(reportedListeners);
- return;
- }
+ if (mDelayedUpdate) {
+ mDelayedUpdate.reset();
+ }
- if (mDelayedUpdate) {
- mDelayedUpdate.reset();
- }
-
- {
- std::scoped_lock lock{mListenersMutex};
- for (const auto& [_, listener] : mWindowInfosListeners) {
- listeners.push_back(listener);
- }
- }
- if (CC_UNLIKELY(listeners.empty())) {
- mReportedListeners.merge(reportedListeners);
- mDelayInfo.reset();
- return;
- }
-
- reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
- reportedListeners.merge(mReportedListeners);
- mReportedListeners.clear();
-
- mActiveMessageCount++;
- updateMaxSendDelay();
+ if (CC_UNLIKELY(mWindowInfosListeners.empty())) {
+ mReportedListeners.merge(reportedListeners);
mDelayInfo.reset();
+ return;
}
- auto reportedInvoker =
- sp<WindowInfosReportedListenerInvoker>::make(listeners, std::move(reportedListeners));
+ reportedListeners.merge(mReportedListeners);
+ mReportedListeners.clear();
- for (const auto& listener : listeners) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
+ // Update mUnackedState to include the message we're about to send
+ auto [it, _] = mUnackedState.try_emplace(update.vsyncId,
+ UnackedState{.reportedListeners =
+ std::move(reportedListeners)});
+ auto& unackedState = it->second;
+ for (auto& pair : mWindowInfosListeners) {
+ int64_t listenerId = pair.second.first;
+ unackedState.unackedListenerIds.push_back(listenerId);
+ }
- // linkToDeath is used here to ensure that the windowInfosReportedListeners
- // are called even if one of the windowInfosListeners dies before
- // calling onWindowInfosReported.
- asBinder->linkToDeath(reportedInvoker);
+ mDelayInfo.reset();
+ updateMaxSendDelay();
- auto status = listener->onWindowInfosChanged(update, reportedInvoker);
+ // Call the listeners
+ for (auto& pair : mWindowInfosListeners) {
+ auto& [listenerId, listener] = pair.second;
+ auto status = listener->onWindowInfosChanged(update);
if (!status.isOk()) {
- reportedInvoker->onWindowInfosReported();
+ ackWindowInfosReceived(update.vsyncId, listenerId);
}
}
}
-binder::Status WindowInfosListenerInvoker::onWindowInfosReported() {
- BackgroundExecutor::getInstance().sendCallbacks({[this]() {
- gui::WindowInfosUpdate update;
- {
- std::scoped_lock lock{mMessagesMutex};
- mActiveMessageCount--;
- if (!mDelayedUpdate || mActiveMessageCount > 0) {
- return;
- }
- update = std::move(*mDelayedUpdate);
- mDelayedUpdate.reset();
- }
- windowInfosChanged(std::move(update), {}, false);
- }});
- return binder::Status::ok();
-}
-
WindowInfosListenerInvoker::DebugInfo WindowInfosListenerInvoker::getDebugInfo() {
- std::scoped_lock lock{mMessagesMutex};
- updateMaxSendDelay();
- mDebugInfo.pendingMessageCount = mActiveMessageCount;
- return mDebugInfo;
+ DebugInfo result;
+ BackgroundExecutor::getInstance().sendCallbacks({[&, this]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::getDebugInfo");
+ updateMaxSendDelay();
+ result = mDebugInfo;
+ result.pendingMessageCount = mUnackedState.size();
+ }});
+ BackgroundExecutor::getInstance().flushQueue();
+ return result;
}
void WindowInfosListenerInvoker::updateMaxSendDelay() {
@@ -192,4 +162,41 @@
}
}
+binder::Status WindowInfosListenerInvoker::ackWindowInfosReceived(int64_t vsyncId,
+ int64_t listenerId) {
+ BackgroundExecutor::getInstance().sendCallbacks({[this, vsyncId, listenerId]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::ackWindowInfosReceived");
+ auto it = mUnackedState.find(vsyncId);
+ if (it == mUnackedState.end()) {
+ return;
+ }
+
+ auto& state = it->second;
+ state.unackedListenerIds.unstable_erase(std::find(state.unackedListenerIds.begin(),
+ state.unackedListenerIds.end(),
+ listenerId));
+ if (!state.unackedListenerIds.empty()) {
+ return;
+ }
+
+ WindowInfosReportedListenerSet reportedListeners{std::move(state.reportedListeners)};
+ mUnackedState.erase(vsyncId);
+
+ for (const auto& reportedListener : reportedListeners) {
+ sp<IBinder> asBinder = IInterface::asBinder(reportedListener);
+ if (asBinder->isBinderAlive()) {
+ reportedListener->onWindowInfosReported();
+ }
+ }
+
+ if (!mDelayedUpdate || !mUnackedState.empty()) {
+ return;
+ }
+ gui::WindowInfosUpdate update{std::move(*mDelayedUpdate)};
+ mDelayedUpdate.reset();
+ windowInfosChanged(std::move(update), {}, false);
+ }});
+ return binder::Status::ok();
+}
+
} // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index bc465a3..f36b0ed 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -19,11 +19,12 @@
#include <optional>
#include <unordered_set>
-#include <android/gui/BnWindowInfosReportedListener.h>
+#include <android/gui/BnWindowInfosPublisher.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/IWindowInfosReportedListener.h>
#include <binder/IBinder.h>
#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
#include <gui/SpHash.h>
#include <utils/Mutex.h>
@@ -35,22 +36,22 @@
std::unordered_set<sp<gui::IWindowInfosReportedListener>,
gui::SpHash<gui::IWindowInfosReportedListener>>;
-class WindowInfosListenerInvoker : public gui::BnWindowInfosReportedListener,
+class WindowInfosListenerInvoker : public gui::BnWindowInfosPublisher,
public IBinder::DeathRecipient {
public:
- void addWindowInfosListener(sp<gui::IWindowInfosListener>);
+ void addWindowInfosListener(sp<gui::IWindowInfosListener>, gui::WindowInfosListenerInfo*);
void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
void windowInfosChanged(gui::WindowInfosUpdate update,
WindowInfosReportedListenerSet windowInfosReportedListeners,
bool forceImmediateCall);
- binder::Status onWindowInfosReported() override;
+ binder::Status ackWindowInfosReceived(int64_t, int64_t) override;
struct DebugInfo {
VsyncId maxSendDelayVsyncId;
nsecs_t maxSendDelayDuration;
- uint32_t pendingMessageCount;
+ size_t pendingMessageCount;
};
DebugInfo getDebugInfo();
@@ -58,24 +59,28 @@
void binderDied(const wp<IBinder>& who) override;
private:
- std::mutex mListenersMutex;
-
static constexpr size_t kStaticCapacity = 3;
- ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
- mWindowInfosListeners GUARDED_BY(mListenersMutex);
+ std::atomic<int64_t> mNextListenerId{0};
+ ftl::SmallMap<wp<IBinder>, const std::pair<int64_t, sp<gui::IWindowInfosListener>>,
+ kStaticCapacity>
+ mWindowInfosListeners;
- std::mutex mMessagesMutex;
- uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
- std::optional<gui::WindowInfosUpdate> mDelayedUpdate GUARDED_BY(mMessagesMutex);
+ std::optional<gui::WindowInfosUpdate> mDelayedUpdate;
WindowInfosReportedListenerSet mReportedListeners;
- DebugInfo mDebugInfo GUARDED_BY(mMessagesMutex);
+ struct UnackedState {
+ ftl::SmallVector<int64_t, kStaticCapacity> unackedListenerIds;
+ WindowInfosReportedListenerSet reportedListeners;
+ };
+ ftl::SmallMap<int64_t /* vsyncId */, UnackedState, 5> mUnackedState;
+
+ DebugInfo mDebugInfo;
struct DelayInfo {
int64_t vsyncId;
nsecs_t frameTime;
};
- std::optional<DelayInfo> mDelayInfo GUARDED_BY(mMessagesMutex);
- void updateMaxSendDelay() REQUIRES(mMessagesMutex);
+ std::optional<DelayInfo> mDelayInfo;
+ void updateMaxSendDelay();
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index f76a8d7..0f9060d 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -138,3 +138,18 @@
"surfaceflinger_frametracer_fuzzer.cpp",
],
}
+
+cc_fuzz {
+ name: "surfaceflinger_service_fuzzer",
+ defaults: [
+ "surfaceflinger_fuzz_defaults",
+ "service_fuzzer_defaults",
+ "fuzzer_disable_leaks",
+ ],
+ srcs: [
+ "surfaceflinger_service_fuzzer.cpp",
+ ],
+ fuzz_config: {
+ triage_assignee: "waghpawan@google.com",
+ },
+}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index 9fac14e..f22315a 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -597,7 +597,7 @@
mFdp.ConsumeBool() ? hal::Vsync::ENABLE : hal::Vsync::DISABLE);
mHwc.isConnected(mPhysicalDisplayId);
- mHwc.getModes(mPhysicalDisplayId);
+ mHwc.getModes(mPhysicalDisplayId, mFdp.ConsumeIntegral<int32_t>());
mHwc.getActiveMode(mPhysicalDisplayId);
mHwc.getColorModes(mPhysicalDisplayId);
mHwc.hasCapability(mFdp.PickValueInArray(kCapability));
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index 80943b5..b2d4131 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -24,7 +24,6 @@
namespace android::fuzz {
static constexpr LatchUnsignaledConfig kLatchUnsignaledConfig[] = {
- LatchUnsignaledConfig::Always,
LatchUnsignaledConfig::AutoSingleLayer,
LatchUnsignaledConfig::Disabled,
};
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 8e208bc..28ac664 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -286,8 +286,11 @@
private:
// ICompositor overrides:
void configure() override {}
- bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
- void composite(TimePoint, VsyncId) override {}
+ bool commit(const scheduler::FrameTarget&) override { return false; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
// MessageQueue overrides:
@@ -474,25 +477,25 @@
&outWideColorGamutPixelFormat);
}
- void overrideHdrTypes(sp<IBinder> &display, FuzzedDataProvider *fdp) {
+ void overrideHdrTypes(const sp<IBinder>& display, FuzzedDataProvider* fdp) {
std::vector<ui::Hdr> hdrTypes;
hdrTypes.push_back(fdp->PickValueInArray(kHdrTypes));
mFlinger->overrideHdrTypes(display, hdrTypes);
}
- void getDisplayedContentSample(sp<IBinder> &display, FuzzedDataProvider *fdp) {
+ void getDisplayedContentSample(const sp<IBinder>& display, FuzzedDataProvider* fdp) {
DisplayedFrameStats outDisplayedFrameStats;
mFlinger->getDisplayedContentSample(display, fdp->ConsumeIntegral<uint64_t>(),
fdp->ConsumeIntegral<uint64_t>(),
&outDisplayedFrameStats);
}
- void getDisplayStats(sp<IBinder> &display) {
+ void getDisplayStats(const sp<IBinder>& display) {
android::DisplayStatInfo stats;
mFlinger->getDisplayStats(display, &stats);
}
- void getDisplayState(sp<IBinder> &display) {
+ void getDisplayState(const sp<IBinder>& display) {
ui::DisplayState displayState;
mFlinger->getDisplayState(display, &displayState);
}
@@ -506,12 +509,12 @@
android::ui::DynamicDisplayInfo dynamicDisplayInfo;
mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
}
- void getDisplayNativePrimaries(sp<IBinder> &display) {
+ void getDisplayNativePrimaries(const sp<IBinder>& display) {
android::ui::DisplayPrimaries displayPrimaries;
mFlinger->getDisplayNativePrimaries(display, displayPrimaries);
}
- void getDesiredDisplayModeSpecs(sp<IBinder> &display) {
+ void getDesiredDisplayModeSpecs(const sp<IBinder>& display) {
gui::DisplayModeSpecs _;
mFlinger->getDesiredDisplayModeSpecs(display, &_);
}
@@ -523,7 +526,7 @@
return ids.front();
}
- std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
+ std::pair<sp<IBinder>, PhysicalDisplayId> fuzzBoot(FuzzedDataProvider* fdp) {
mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
const sp<Client> client = sp<Client>::make(mFlinger);
@@ -550,13 +553,13 @@
mFlinger->bootFinished();
- return {display, physicalDisplayId.value};
+ return {display, physicalDisplayId};
}
void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
FuzzedDataProvider mFdp(data, size);
- auto [display, displayId] = fuzzBoot(&mFdp);
+ const auto [display, displayId] = fuzzBoot(&mFdp);
sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
@@ -564,8 +567,8 @@
getDisplayStats(display);
getDisplayState(display);
- getStaticDisplayInfo(displayId);
- getDynamicDisplayInfo(displayId);
+ getStaticDisplayInfo(displayId.value);
+ getDynamicDisplayInfo(displayId.value);
getDisplayNativePrimaries(display);
mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
@@ -604,7 +607,10 @@
mFlinger->commitTransactions();
mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
- mFlinger->postComposition(systemTime());
+
+ scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool());
+ mFlinger->postComposition(displayId, ftl::init::map(displayId, &frameTargeter),
+ mFdp.ConsumeIntegral<nsecs_t>());
}
mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -622,8 +628,6 @@
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>());
- mFlinger->calculateExpectedPresentTime({});
-
mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool());
fuzzDumpsysAndDebug(&mFdp);
@@ -792,6 +796,7 @@
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
+ void onChoreographerAttached() override {}
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger =
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 921cae4..9f0bdde 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -106,7 +106,7 @@
effectLayer->addSurfaceFramePresentedForBuffer(surfaceFrame,
mFdp.ConsumeIntegral<int64_t>() /*acquireTime*/,
mFdp.ConsumeIntegral<int64_t>() /*currentTime*/);
- effectLayer->addSurfaceFrameDroppedForBuffer(surfaceFrame1);
+ effectLayer->addSurfaceFrameDroppedForBuffer(surfaceFrame1, mFdp.ConsumeIntegral<nsecs_t>());
parent.clear();
client.clear();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index f17d2e1..4d1a5ff 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -19,6 +19,7 @@
#include <fuzzer/FuzzedDataProvider.h>
#include <processgroup/sched_policy.h>
+#include <scheduler/IVsyncSource.h>
#include <scheduler/PresentLatencyTracker.h>
#include "Scheduler/OneShotTimer.h"
@@ -42,13 +43,14 @@
(120_Hz).getPeriodNsecs()};
constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>();
+constexpr auto kCompositionCoverage = ftl::enum_range<CompositionCoverage>();
constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
constexpr uint16_t kRandomStringLength = 256;
constexpr std::chrono::duration kSyncPeriod(16ms);
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr PhysicalDisplayId kDisplayId = PhysicalDisplayId::fromPort(42u);
template <typename T>
void dump(T* component, FuzzedDataProvider* fdp) {
@@ -56,6 +58,10 @@
component->dump(res);
}
+inline sp<Fence> makeFakeFence() {
+ return sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
+}
+
class SchedulerFuzzer {
public:
SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
@@ -65,6 +71,7 @@
void fuzzRefreshRateSelection();
void fuzzRefreshRateSelector();
void fuzzPresentLatencyTracker();
+ void fuzzFrameTargeter();
void fuzzVSyncModulator();
void fuzzVSyncPredictor();
void fuzzVSyncReactor();
@@ -170,9 +177,8 @@
uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
- scheduler::VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
- mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize,
- minimumSamplesForPrediction,
+ scheduler::VSyncPredictor tracker{kDisplayId, mFdp.ConsumeIntegral<uint16_t>() /*period*/,
+ historySize, minimumSamplesForPrediction,
mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
tracker.setPeriod(period);
@@ -244,7 +250,7 @@
void SchedulerFuzzer::fuzzVSyncReactor() {
std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>();
- scheduler::VSyncReactor reactor(DEFAULT_DISPLAY_ID,
+ scheduler::VSyncReactor reactor(kDisplayId,
std::make_unique<ClockWrapper>(
std::make_shared<FuzzImplClock>()),
*vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
@@ -256,13 +262,13 @@
reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt,
&periodFlushed);
- sp<Fence> fence = sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
- std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+
+ const auto fence = std::make_shared<FenceTime>(makeFakeFence());
vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>());
FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>());
- ft->applyTrustedSnapshot(snap);
+ fence->applyTrustedSnapshot(snap);
reactor.setIgnorePresentFences(mFdp.ConsumeBool());
- reactor.addPresentFence(ft);
+ reactor.addPresentFence(fence);
dump<scheduler::VSyncReactor>(&reactor, &mFdp);
}
@@ -392,14 +398,45 @@
void SchedulerFuzzer::fuzzPresentLatencyTracker() {
scheduler::PresentLatencyTracker tracker;
- tracker.trackPendingFrame(TimePoint::fromNs(mFdp.ConsumeIntegral<nsecs_t>()),
- FenceTime::NO_FENCE);
+
+ int i = 5;
+ while (i-- > 0) {
+ tracker.trackPendingFrame(getFuzzedTimePoint(mFdp),
+ std::make_shared<FenceTime>(makeFakeFence()));
+ }
+}
+
+void SchedulerFuzzer::fuzzFrameTargeter() {
+ scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool());
+
+ const struct VsyncSource final : scheduler::IVsyncSource {
+ explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
+ FuzzedDataProvider& fuzzer;
+
+ Period period() const { return getFuzzedDuration(fuzzer); }
+ TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); }
+ } vsyncSource{mFdp};
+
+ int i = 10;
+ while (i-- > 0) {
+ frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp),
+ .vsyncId = getFuzzedVsyncId(mFdp),
+ .expectedVsyncTime = getFuzzedTimePoint(mFdp),
+ .sfWorkDuration = getFuzzedDuration(mFdp)},
+ vsyncSource);
+
+ frameTargeter.setPresentFence(makeFakeFence());
+
+ frameTargeter.endFrame(
+ {.compositionCoverage = mFdp.PickValueInArray(kCompositionCoverage.values)});
+ }
}
void SchedulerFuzzer::process() {
fuzzRefreshRateSelection();
fuzzRefreshRateSelector();
fuzzPresentLatencyTracker();
+ fuzzFrameTargeter();
fuzzVSyncModulator();
fuzzVSyncPredictor();
fuzzVSyncReactor();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp
new file mode 100644
index 0000000..849a896
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_driver.h>
+
+#include "SurfaceFlinger.h"
+#include "SurfaceFlingerDefaultFactory.h"
+
+using namespace android;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ DefaultFactory factory;
+ sp<SurfaceFlinger> flinger = sp<SurfaceFlinger>::make(factory);
+ flinger->init();
+
+ sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
+ fuzzService({flinger, composerAIDL}, FuzzedDataProvider(data, size));
+ return 0;
+}
diff --git a/services/surfaceflinger/layerproto/common.proto b/services/surfaceflinger/layerproto/common.proto
index a6d8d61..5e20d4d 100644
--- a/services/surfaceflinger/layerproto/common.proto
+++ b/services/surfaceflinger/layerproto/common.proto
@@ -70,6 +70,7 @@
bool replace_touchable_region_with_crop = 14;
RectProto touchable_region_crop = 15;
TransformProto transform = 16;
+ uint32 input_config = 17;
}
message BlurRegion {
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index b0cee9b..d03afa0 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -256,13 +256,14 @@
int32 layout_params_type = 2;
RegionProto touchable_region = 3;
int32 surface_inset = 4;
- bool focusable = 5;
- bool has_wallpaper = 6;
+ bool focusable = 5; // unused
+ bool has_wallpaper = 6; // unused
float global_scale_factor = 7;
uint32 crop_layer_id = 8;
bool replace_touchable_region_with_crop = 9;
RectProto touchable_region_crop = 10;
Transform transform = 11;
+ uint32 input_config = 12;
}
WindowInfo window_info_handle = 27;
float bg_color_alpha = 28;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index bcbe21a..be29be4 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -199,6 +199,7 @@
# useColorManagement indicates whether SurfaceFlinger should manage color
# by switching to appropriate color mode automatically depending on the
# Dataspace of the surfaces on screen.
+# DEPRECATED: SurfaceFlinger is always color managed.
prop {
api_name: "use_color_management"
type: Boolean
@@ -207,7 +208,7 @@
prop_name: "ro.surface_flinger.use_color_management"
}
-# The following four propertiess define:
+# The following four properties define:
# Returns the default data space and pixel format that SurfaceFlinger
# expects to receive and output as well as the wide color gamut data space
# and pixel format for wide color gamut surfaces.
@@ -276,7 +277,7 @@
# The variable works only when useColorManagement is specified. If
# unspecified, the data space follows what SurfaceFlinger expects for
# surfaces when useColorManagement is specified.
-
+# DEPRECATED: do not use
prop {
api_name: "color_space_agnostic_dataspace"
type: Long
@@ -470,4 +471,27 @@
scope: Public
access: Readonly
prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
-}
\ No newline at end of file
+}
+
+# Controls the minimum acquired buffers SurfaceFlinger will suggest via
+# ISurfaceComposer.getMaxAcquiredBufferCount().
+prop {
+ api_name: "min_acquired_buffers"
+ type: Long
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.min_acquired_buffers"
+}
+
+# When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for
+# buffers when they are evicted from the app cache by using additional setLayerBuffer commands.
+# Ideally, this behavior would always be enabled to reduce graphics memory consumption. However,
+# Some HAL implementations may not support the additional setLayerBuffer commands used to clear
+# the cache slots.
+prop {
+ api_name: "clear_slots_with_set_layer_buffer"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 348a462..ba88acc 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -1,6 +1,10 @@
props {
module: "android.sysprop.SurfaceFlingerProperties"
prop {
+ api_name: "clear_slots_with_set_layer_buffer"
+ prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer"
+ }
+ prop {
api_name: "color_space_agnostic_dataspace"
type: Long
prop_name: "ro.surface_flinger.color_space_agnostic_dataspace"
@@ -93,6 +97,11 @@
prop_name: "ro.surface_flinger.max_virtual_display_dimension"
}
prop {
+ api_name: "min_acquired_buffers"
+ type: Long
+ prop_name: "ro.surface_flinger.min_acquired_buffers"
+ }
+ prop {
api_name: "present_time_offset_from_vsync_ns"
type: Long
prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 62b539a..b5168b0 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -37,8 +37,9 @@
"DisplayConfigs_test.cpp",
"DisplayEventReceiver_test.cpp",
"EffectLayer_test.cpp",
- "LayerBorder_test.cpp",
+ "HdrSdrRatioOverlay_test.cpp",
"InvalidHandles_test.cpp",
+ "LayerBorder_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
"LayerState_test.cpp",
diff --git a/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp b/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp
new file mode 100644
index 0000000..77a8f9c
--- /dev/null
+++ b/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp
@@ -0,0 +1,89 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <chrono>
+
+using ::std::literals::chrono_literals::operator""s;
+
+static constexpr int kHdrSdrRatioOverlayCode = 1043;
+static constexpr int kHdrSdrRatioOverlayEnable = 1;
+static constexpr int kHdrSdrRatioOverlayDisable = 0;
+static constexpr int kHdrSdrRatioOverlayQuery = 2;
+
+// These values must match the ones we used for developer options in
+// com.android.settings.development.ShowHdrSdrRatioPreferenceController
+static_assert(kHdrSdrRatioOverlayCode == 1043);
+static_assert(kHdrSdrRatioOverlayEnable == 1);
+static_assert(kHdrSdrRatioOverlayDisable == 0);
+static_assert(kHdrSdrRatioOverlayQuery == 2);
+
+namespace android {
+
+namespace {
+void sendCommandToSf(int command, Parcel& reply) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ Parcel request;
+ request.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+ request.writeInt32(command);
+ ASSERT_EQ(NO_ERROR,
+ IInterface::asBinder(sf)->transact(kHdrSdrRatioOverlayCode, request, &reply));
+}
+
+bool isOverlayEnabled() {
+ Parcel reply;
+ sendCommandToSf(kHdrSdrRatioOverlayQuery, reply);
+ return reply.readBool();
+}
+
+void waitForOverlay(bool enabled) {
+ static constexpr auto kTimeout = std::chrono::nanoseconds(1s);
+ static constexpr auto kIterations = 10;
+ for (int i = 0; i < kIterations; i++) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+ std::this_thread::sleep_for(kTimeout / kIterations);
+ }
+}
+
+void toggleOverlay(bool enabled) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+
+ Parcel reply;
+ const auto command = enabled ? kHdrSdrRatioOverlayEnable : kHdrSdrRatioOverlayDisable;
+ sendCommandToSf(command, reply);
+ waitForOverlay(enabled);
+ ASSERT_EQ(enabled, isOverlayEnabled());
+}
+
+} // namespace
+
+TEST(HdrSdrRatioOverlayTest, enableAndDisableOverlay) {
+ toggleOverlay(true);
+ toggleOverlay(false);
+
+ toggleOverlay(true);
+ toggleOverlay(false);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index cbd54e7..03de8d0 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -184,6 +184,35 @@
}
}
+TEST_F(LayerTransactionTest, CommitCallbackCalledOnce) {
+ auto callCount = 0;
+ auto commitCallback =
+ [&callCount](void* /* context */, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) mutable {
+ callCount++;
+ };
+
+ // Create two transactions that both contain the same callback id.
+ Transaction t1;
+ t1.addTransactionCommittedCallback(commitCallback, nullptr);
+ Parcel parcel;
+ t1.writeToParcel(&parcel);
+ parcel.setDataPosition(0);
+ Transaction t2;
+ t2.readFromParcel(&parcel);
+
+ // Apply the two transactions. There is a race here as we can't guarantee that the two
+ // transactions will be applied within the same SurfaceFlinger commit. If the transactions are
+ // applied within the same commit then we verify that callback ids are deduplicated within a
+ // single commit. Otherwise, we verify that commit callbacks are deduplicated across separate
+ // commits.
+ t1.apply();
+ t2.apply(/*synchronous=*/true);
+
+ ASSERT_EQ(callCount, 1);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 013694f..96cc333 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -19,6 +19,7 @@
#pragma clang diagnostic ignored "-Wconversion"
#include <private/android_filesystem_config.h>
+#include <ui/DisplayState.h>
#include "LayerTransactionTest.h"
@@ -32,11 +33,11 @@
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
ASSERT_FALSE(ids.empty());
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- ASSERT_FALSE(display == nullptr);
+ mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+ ASSERT_FALSE(mDisplayToken == nullptr);
ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode));
const ui::Size& resolution = mode.resolution;
mDisplaySize = resolution;
@@ -57,7 +58,7 @@
TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
+ t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
@@ -71,11 +72,18 @@
LayerTransactionTest::TearDown();
mBGSurfaceControl = 0;
mFGSurfaceControl = 0;
+
+ // Restore display rotation
+ asTransaction([&](Transaction& t) {
+ Rect displayBounds{mDisplaySize};
+ t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, displayBounds, displayBounds);
+ });
}
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
std::unique_ptr<ScreenCapture> mCapture;
+ sp<IBinder> mDisplayToken;
ui::Size mDisplaySize;
};
@@ -870,6 +878,42 @@
mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
+TEST_F(ScreenCaptureTest, CaptureDisplayWith90DegRotation) {
+ asTransaction([&](Transaction& t) {
+ Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
+ t.setDisplayProjection(mDisplayToken, ui::ROTATION_90, newDisplayBounds, newDisplayBounds);
+ });
+
+ DisplayCaptureArgs displayCaptureArgs;
+ displayCaptureArgs.displayToken = mDisplayToken;
+ displayCaptureArgs.width = mDisplaySize.width;
+ displayCaptureArgs.height = mDisplaySize.height;
+ displayCaptureArgs.useIdentityTransform = true;
+ ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
+
+ mCapture->expectBGColor(0, 0);
+ mCapture->expectFGColor(mDisplaySize.width - 65, 65);
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayWith270DegRotation) {
+ asTransaction([&](Transaction& t) {
+ Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
+ t.setDisplayProjection(mDisplayToken, ui::ROTATION_270, newDisplayBounds, newDisplayBounds);
+ });
+
+ DisplayCaptureArgs displayCaptureArgs;
+ displayCaptureArgs.displayToken = mDisplayToken;
+ displayCaptureArgs.width = mDisplaySize.width;
+ displayCaptureArgs.height = mDisplaySize.height;
+ displayCaptureArgs.useIdentityTransform = true;
+ ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
+
+ std::this_thread::sleep_for(std::chrono::seconds{5});
+
+ mCapture->expectBGColor(mDisplayWidth - 1, mDisplaySize.height - 1);
+ mCapture->expectFGColor(65, mDisplaySize.height - 65);
+}
+
TEST_F(ScreenCaptureTest, CaptureNonHdrLayer) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 2b29530..b8a5e79 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -59,8 +59,8 @@
std::string actualLayersTracePath =
std::string(temp_dir.path) + "/" + expectedLayersFilename + "_actual";
- EXPECT_TRUE(
- LayerTraceGenerator().generate(mTransactionTrace, actualLayersTracePath.c_str()))
+ EXPECT_TRUE(LayerTraceGenerator().generate(mTransactionTrace, actualLayersTracePath.c_str(),
+ /*onlyLastEntry=*/true))
<< "Failed to generate layers trace from " << transactionTracePath;
EXPECT_TRUE(std::filesystem::exists(std::filesystem::path(actualLayersTracePath)));
parseLayersTraceFromFile(actualLayersTracePath.c_str(), mActualLayersTraceProto);
@@ -86,9 +86,9 @@
std::vector<std::filesystem::path> TransactionTraceTestSuite::sTransactionTraces{};
struct LayerInfo {
- int32_t id;
+ uint64_t id;
std::string name;
- int32_t parent;
+ uint64_t parent;
int z;
uint64_t curr_frame;
float x;
@@ -119,8 +119,8 @@
}
struct find_id : std::unary_function<LayerInfo, bool> {
- int id;
- find_id(int id) : id(id) {}
+ uint64_t id;
+ find_id(uint64_t id) : id(id) {}
bool operator()(LayerInfo const& m) const { return m.id == id; }
};
@@ -136,9 +136,9 @@
touchableRegionBounds = touchableRegion.bounds();
}
- return {proto.id(),
+ return {static_cast<uint64_t>(proto.id()),
proto.name(),
- proto.parent(),
+ static_cast<uint64_t>(proto.parent()),
proto.z(),
proto.curr_frame(),
proto.has_position() ? proto.position().x() : -1,
@@ -150,7 +150,7 @@
static std::vector<LayerInfo> getLayerInfosFromProto(
android::surfaceflinger::LayersTraceProto& entry) {
- std::unordered_map<int32_t /* snapshotId*/, int32_t /*layerId*/> snapshotIdToLayerId;
+ std::unordered_map<uint64_t /* snapshotId*/, uint64_t /*layerId*/> snapshotIdToLayerId;
std::vector<LayerInfo> layers;
layers.reserve(static_cast<size_t>(entry.layers().layers_size()));
bool mapSnapshotIdToLayerId = false;
@@ -158,7 +158,12 @@
auto layer = entry.layers().layers(i);
LayerInfo layerInfo = getLayerInfoFromProto(layer);
- snapshotIdToLayerId[layerInfo.id] = static_cast<int32_t>(layer.original_id());
+ uint64_t layerId = layerInfo.name.find("(Mirror)") == std::string::npos
+ ? static_cast<uint64_t>(layer.original_id())
+ : static_cast<uint64_t>(layer.original_id()) | 1ull << 63;
+
+ snapshotIdToLayerId[layerInfo.id] = layerId;
+
if (layer.original_id() != 0) {
mapSnapshotIdToLayerId = true;
}
@@ -172,7 +177,7 @@
for (auto& layer : layers) {
layer.id = snapshotIdToLayerId[layer.id];
auto it = snapshotIdToLayerId.find(layer.parent);
- layer.parent = it == snapshotIdToLayerId.end() ? -1 : it->second;
+ layer.parent = it == snapshotIdToLayerId.end() ? static_cast<uint64_t>(-1) : it->second;
}
return layers;
}
@@ -189,7 +194,6 @@
std::vector<LayerInfo> expectedLayers = getLayerInfosFromProto(expectedLastEntry);
std::vector<LayerInfo> actualLayers = getLayerInfosFromProto(actualLastEntry);
- ;
size_t i = 0;
for (; i < actualLayers.size() && i < expectedLayers.size(); i++) {
@@ -197,9 +201,9 @@
find_id(expectedLayers[i].id));
EXPECT_NE(it, actualLayers.end());
EXPECT_EQ(expectedLayers[i], *it);
- ALOGV("Validating %s[%d] parent=%d z=%d frame=%" PRIu64, expectedLayers[i].name.c_str(),
- expectedLayers[i].id, expectedLayers[i].parent, expectedLayers[i].z,
- expectedLayers[i].curr_frame);
+ ALOGV("Validating %s[%" PRIu64 "] parent=%" PRIu64 " z=%d frame=%" PRIu64,
+ expectedLayers[i].name.c_str(), expectedLayers[i].id, expectedLayers[i].parent,
+ expectedLayers[i].z, expectedLayers[i].curr_frame);
}
EXPECT_EQ(expectedLayers.size(), actualLayers.size());
@@ -208,9 +212,9 @@
for (size_t j = 0; j < actualLayers.size(); j++) {
if (std::find_if(expectedLayers.begin(), expectedLayers.end(),
find_id(actualLayers[j].id)) == expectedLayers.end()) {
- ALOGD("actualLayers [%d]:%s parent=%d z=%d frame=%" PRIu64, actualLayers[j].id,
- actualLayers[j].name.c_str(), actualLayers[j].parent, actualLayers[j].z,
- actualLayers[j].curr_frame);
+ ALOGD("actualLayers [%" PRIu64 "]:%s parent=%" PRIu64 " z=%d frame=%" PRIu64,
+ actualLayers[j].id, actualLayers[j].name.c_str(), actualLayers[j].parent,
+ actualLayers[j].z, actualLayers[j].curr_frame);
}
}
FAIL();
@@ -220,9 +224,9 @@
for (size_t j = 0; j < expectedLayers.size(); j++) {
if (std::find_if(actualLayers.begin(), actualLayers.end(),
find_id(expectedLayers[j].id)) == actualLayers.end()) {
- ALOGD("expectedLayers [%d]:%s parent=%d z=%d frame=%" PRIu64, expectedLayers[j].id,
- expectedLayers[j].name.c_str(), expectedLayers[j].parent, expectedLayers[j].z,
- expectedLayers[j].curr_frame);
+ ALOGD("expectedLayers [%" PRIu64 "]:%s parent=%" PRIu64 " z=%d frame=%" PRIu64,
+ expectedLayers[j].id, expectedLayers[j].name.c_str(),
+ expectedLayers[j].parent, expectedLayers[j].z, expectedLayers[j].curr_frame);
}
}
FAIL();
diff --git a/services/surfaceflinger/tests/tracing/readme.md b/services/surfaceflinger/tests/tracing/readme.md
index 3e80a74..f545a3c 100644
--- a/services/surfaceflinger/tests/tracing/readme.md
+++ b/services/surfaceflinger/tests/tracing/readme.md
@@ -14,7 +14,9 @@
#### Workflow ####
Add transaction traces that resulted in front end bugs along
with the layer trace after fixing the issue. The layer trace
-can be generated by using the layertracegenerator tool. The
+can be generated by using the layertracegenerator tool. Use the
+--last-entry-only flag to generate only the last entry in the
+trace. This will keep the test data to a manageable size. The
main goal of this test suite is to add regression tests with
minimal effort.
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_b282110579.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_b282110579.winscope
new file mode 100644
index 0000000..3246453
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_b282110579.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b282110579.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b282110579.winscope
new file mode 100644
index 0000000..ecb9431
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b282110579.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 86af303..fa5fa95 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -161,7 +161,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
"libaidlcommonsupport",
"libcompositionengine_mocks",
"libcompositionengine",
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 5fed9b4..91c6239 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -634,31 +634,6 @@
expectVSyncCallbackScheduleReceived(false);
}
-TEST_F(EventThreadTest, tracksEventConnections) {
- setupEventThread(VSYNC_PERIOD);
-
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
- ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
- sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
- mThread->setVsyncRate(1, errorConnection);
- EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
- ConnectionEventRecorder secondConnectionEventRecorder{0};
- sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder);
- mThread->setVsyncRate(1, secondConnection);
- EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
-
- // EventThread should enable vsync callbacks.
- expectVSyncCallbackScheduleReceived(true);
-
- // The first event will be seen by the connection, which then returns an error.
- onVSyncEvent(123, 456, 789);
- expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
- expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
- 1u);
- EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
-}
-
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
setupEventThread(VSYNC_PERIOD);
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index da00377..8a45f17 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -53,6 +53,7 @@
using Hwc2::Config;
using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
+using hal::IComposerClient;
using ::testing::_;
using ::testing::DoAll;
using ::testing::ElementsAreArray;
@@ -119,6 +120,157 @@
}
}
+TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) {
+ constexpr hal::HWDisplayId kHwcDisplayId = 2;
+ constexpr hal::HWConfigId kConfigId = 42;
+ constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
+
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+
+ {
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+ .WillOnce(Return(HalError::BAD_DISPLAY));
+ EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
+ }
+ {
+ constexpr int32_t kWidth = 480;
+ constexpr int32_t kHeight = 720;
+ constexpr int32_t kConfigGroup = 1;
+ constexpr int32_t kVsyncPeriod = 16666667;
+
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
+ _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kWidth), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kHeight), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kConfigGroup), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::VSYNC_PERIOD, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kVsyncPeriod), Return(HalError::NONE)));
+
+ // Optional Parameters UNSUPPORTED
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+ _))
+ .WillOnce(Return(HalError::UNSUPPORTED));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+ _))
+ .WillOnce(Return(HalError::UNSUPPORTED));
+
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{kConfigId}),
+ Return(HalError::NONE)));
+
+ auto modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+
+ // Optional parameters are supported
+ constexpr int32_t kDpi = 320;
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+ _))
+ .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+ _))
+ .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+
+ modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ // DPI values are scaled by 1000 in the legacy implementation.
+ EXPECT_EQ(modes.front().dpiX, kDpi / 1000.f);
+ EXPECT_EQ(modes.front().dpiY, kDpi / 1000.f);
+ }
+}
+
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations) {
+ constexpr hal::HWDisplayId kHwcDisplayId = 2;
+ constexpr hal::HWConfigId kConfigId = 42;
+ constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(true));
+
+ {
+ EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
+ .WillOnce(Return(HalError::BAD_DISPLAY));
+ EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
+ }
+ {
+ constexpr int32_t kWidth = 480;
+ constexpr int32_t kHeight = 720;
+ constexpr int32_t kConfigGroup = 1;
+ constexpr int32_t kVsyncPeriod = 16666667;
+ hal::DisplayConfiguration displayConfiguration;
+ displayConfiguration.configId = kConfigId;
+ displayConfiguration.configGroup = kConfigGroup;
+ displayConfiguration.height = kHeight;
+ displayConfiguration.width = kWidth;
+ displayConfiguration.vsyncPeriod = kVsyncPeriod;
+
+ EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
+ displayConfiguration}),
+ Return(HalError::NONE)));
+
+ // Optional dpi not supported
+ auto modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+
+ // Supports optional dpi parameter
+ constexpr int32_t kDpi = 320;
+ displayConfiguration.dpi = {kDpi, kDpi};
+
+ EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
+ displayConfiguration}),
+ Return(HalError::NONE)));
+
+ modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().dpiX, kDpi);
+ EXPECT_EQ(modes.front().dpiY, kDpi);
+ }
+}
+
TEST_F(HWComposerTest, onVsync) {
constexpr hal::HWDisplayId kHwcDisplayId = 1;
expectHotplugConnect(kHwcDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 4301186..e475b84 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -308,6 +308,31 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRateSelectionPriority = priority;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setFrameRate(uint32_t id, float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRate = frameRate;
+ transactions.back().states.front().state.frameRateCompatibility = compatibility;
+ transactions.back().states.front().state.changeFrameRateStrategy = changeFrameRateStrategy;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 12cf070..a581d5b 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -20,6 +20,7 @@
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
#include "FrontEnd/LayerSnapshotBuilder.h"
+#include "Layer.h"
#include "LayerHierarchyTest.h"
#define UPDATE_AND_VERIFY(BUILDER, ...) \
@@ -228,6 +229,7 @@
setAlpha(1, 0.5);
setAlpha(122, 0.5);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->alpha, 0.5f);
EXPECT_EQ(getSnapshot(12)->alpha, 0.5f);
EXPECT_EQ(getSnapshot(1221)->alpha, 0.25f);
}
@@ -236,28 +238,30 @@
TEST_F(LayerSnapshotTest, UpdateClearsPreviousChangeStates) {
setCrop(1, Rect(1, 2, 3, 4));
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_TRUE(getSnapshot(1)->changes.get() != 0);
- EXPECT_TRUE(getSnapshot(11)->changes.get() != 0);
+ EXPECT_TRUE(getSnapshot(1)->changes.test(RequestedLayerState::Changes::Geometry));
+ EXPECT_TRUE(getSnapshot(11)->changes.test(RequestedLayerState::Changes::Geometry));
setCrop(2, Rect(1, 2, 3, 4));
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_TRUE(getSnapshot(2)->changes.get() != 0);
- EXPECT_TRUE(getSnapshot(1)->changes.get() == 0);
- EXPECT_TRUE(getSnapshot(11)->changes.get() == 0);
+ EXPECT_TRUE(getSnapshot(2)->changes.test(RequestedLayerState::Changes::Geometry));
+ EXPECT_FALSE(getSnapshot(1)->changes.test(RequestedLayerState::Changes::Geometry));
+ EXPECT_FALSE(getSnapshot(11)->changes.test(RequestedLayerState::Changes::Geometry));
}
TEST_F(LayerSnapshotTest, FastPathClearsPreviousChangeStates) {
setColor(11, {1._hf, 0._hf, 0._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_TRUE(getSnapshot(11)->changes.get() != 0);
- EXPECT_TRUE(getSnapshot(1)->changes.get() == 0);
+ EXPECT_EQ(getSnapshot(11)->changes, RequestedLayerState::Changes::Content);
+ EXPECT_EQ(getSnapshot(11)->clientChanges, layer_state_t::eColorChanged);
+ EXPECT_EQ(getSnapshot(1)->changes.get(), 0u);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_TRUE(getSnapshot(11)->changes.get() == 0);
+ EXPECT_EQ(getSnapshot(11)->changes.get(), 0u);
}
TEST_F(LayerSnapshotTest, FastPathSetsChangeFlagToContent) {
setColor(1, {1._hf, 0._hf, 0._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(1)->changes, RequestedLayerState::Changes::Content);
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eColorChanged);
}
TEST_F(LayerSnapshotTest, GameMode) {
@@ -270,7 +274,9 @@
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);
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);
}
@@ -309,7 +315,7 @@
EXPECT_EQ(getSnapshot(1)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::NoVote);
}
-TEST_F(LayerSnapshotTest, canCropTouchableRegion) {
+TEST_F(LayerSnapshotTest, CanCropTouchableRegion) {
// ROOT
// ├── 1
// │ ├── 11
@@ -462,4 +468,132 @@
EXPECT_LE(startingNumSnapshots - 2, mSnapshotBuilder.getSnapshots().size());
}
+TEST_F(LayerSnapshotTest, snashotContainsMetadataFromLayerCreationArgs) {
+ LayerCreationArgs args(std::make_optional<uint32_t>(200));
+ args.name = "testlayer";
+ args.addToRoot = true;
+ args.metadata.setInt32(42, 24);
+
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(args));
+ EXPECT_TRUE(layers.back()->metadata.has(42));
+ EXPECT_EQ(layers.back()->metadata.getInt32(42, 0), 24);
+ mLifecycleManager.addLayers(std::move(layers));
+
+ std::vector<uint32_t> expected = STARTING_ZORDER;
+ expected.push_back(200);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+ EXPECT_TRUE(mSnapshotBuilder.getSnapshot(200)->layerMetadata.has(42));
+ EXPECT_EQ(mSnapshotBuilder.getSnapshot(200)->layerMetadata.getInt32(42, 0), 24);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSelectionPriorityPassedToChildLayers) {
+ setFrameRateSelectionPriority(11, 1);
+
+ setFrameRateSelectionPriority(12, 2);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionPriority, Layer::PRIORITY_UNSET);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionPriority, 1);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionPriority, 2);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionPriority, 2);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionPriority, 2);
+
+ // reparent and verify the child gets the new parent's framerate selection priority
+ reparentLayer(122, 11);
+
+ std::vector<uint32_t> expected = {1, 11, 111, 122, 1221, 12, 121, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionPriority, Layer::PRIORITY_UNSET);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionPriority, 1);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionPriority, 2);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionPriority, 1);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionPriority, 1);
+}
+
+TEST_F(LayerSnapshotTest, framerate) {
+ setFrameRate(11, 244.f, 0, 0);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent is gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer and children get the requested votes
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // reparent and verify the child gets the new parent's framerate
+ reparentLayer(122, 11);
+
+ std::vector<uint32_t> expected = {1, 11, 111, 122, 1221, 12, 121, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ // verify parent is gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+
+ // verify layer and children get the requested votes
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // reparent and verify the new parent gets no vote
+ reparentLayer(11, 2);
+ expected = {1, 12, 121, 13, 2, 11, 111, 122, 1221};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+ // verify old parent has invalid framerate (default)
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify new parent get no vote
+ EXPECT_FALSE(getSnapshot({.id = 2})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 2})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 2})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer and children get the requested votes (unchanged)
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 91875cc..1dcf222 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -20,9 +20,10 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <scheduler/interface/ICompositor.h>
+
#include "FrameTimeline.h"
#include "Scheduler/MessageQueue.h"
-#include "SurfaceFlinger.h"
#include "mock/MockVSyncDispatch.h"
namespace android {
@@ -34,8 +35,11 @@
struct NoOpCompositor final : ICompositor {
void configure() override {}
- bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
- void composite(TimePoint, VsyncId) override {}
+ bool commit(const scheduler::FrameTarget&) override { return false; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
} gNoOpCompositor;
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 0d66d59..85f66f4 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -18,10 +18,10 @@
#define LOG_TAG "PowerAdvisorTest"
#include <DisplayHardware/PowerAdvisor.h>
-#include <compositionengine/Display.h>
-#include <ftl/fake_guard.h>
+#include <binder/Status.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
#include <ui/DisplayId.h>
#include <chrono>
#include "TestableSurfaceFlinger.h"
@@ -50,7 +50,7 @@
TestableSurfaceFlinger mFlinger;
std::unique_ptr<PowerAdvisor> mPowerAdvisor;
MockPowerHalController* mMockPowerHalController;
- sp<MockIPowerHintSession> mMockPowerHintSession;
+ std::shared_ptr<MockIPowerHintSession> mMockPowerHintSession;
};
void PowerAdvisorTest::SetUp() {
@@ -64,13 +64,14 @@
void PowerAdvisorTest::startPowerHintSession() {
const std::vector<int32_t> threadIds = {1, 2, 3};
- mMockPowerHintSession = android::sp<NiceMock<MockIPowerHintSession>>::make();
+ mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(
- Return(HalResult<sp<IPowerHintSession>>::fromStatus(binder::Status::ok(),
- mMockPowerHintSession)));
+ .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
+ fromStatus(binder::Status::ok(), mMockPowerHintSession)));
mPowerAdvisor->enablePowerHintSession(true);
mPowerAdvisor->startPowerHintSession(threadIds);
+ ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration)
+ .WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration,
@@ -123,8 +124,8 @@
EXPECT_CALL(*mMockPowerHintSession,
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
- .Times(1);
-
+ .Times(1)
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
mPowerAdvisor->setDisplays(displayIds);
@@ -163,7 +164,8 @@
EXPECT_CALL(*mMockPowerHintSession,
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
- .Times(1);
+ .Times(1)
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
@@ -205,7 +207,8 @@
EXPECT_CALL(*mMockPowerHintSession,
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
- .Times(1);
+ .Times(1)
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index d63e187..aaf55fb 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -222,6 +222,7 @@
makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
static inline const DisplayModes kModes_1_5_10 = makeModes(kMode1, kMode5, kMode10);
+ static inline const DisplayModes kModes_60_90_120 = makeModes(kMode60, kMode90, kMode120);
// This is a typical TV configuration.
static inline const DisplayModes kModes_24_25_30_50_60_Frac =
@@ -1413,7 +1414,9 @@
ss << "ExplicitDefault " << desired;
lr.name = ss.str();
- EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps());
+ const auto bestFps = selector.getBestFrameRateMode(layers)->getFps();
+ EXPECT_EQ(expected, bestFps)
+ << "expected " << expected << " for " << desired << " but got " << bestFps;
}
}
@@ -1422,7 +1425,7 @@
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
- // Test that 23.976 will choose 24 if 23.976 is not supported
+ // Test that 23.976 will prefer 60 over 59.94 and 30
{
auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60,
kMode60Frac),
@@ -1431,7 +1434,7 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.desiredRefreshRate = 23.976_Hz;
lr.name = "ExplicitExactOrMultiple 23.976 Hz";
- EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 24 will choose 23.976 if 24 is not supported
@@ -1456,13 +1459,13 @@
EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
}
- // Test that 29.97 will choose 30 if 59.94 is not supported
+ // Test that 29.97 will choose 60 if 59.94 is not supported
{
auto selector = createSelector(makeModes(kMode30, kMode60), kModeId60);
lr.desiredRefreshRate = 29.97_Hz;
lr.name = "ExplicitExactOrMultiple 29.97 Hz";
- EXPECT_EQ(kModeId30, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 59.94 will choose 60 if 59.94 is not supported
@@ -2516,6 +2519,71 @@
EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
}
+TEST_P(RefreshRateSelectorTest, test23976Chooses120) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "23.976 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 23.976_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test23976Chooses60IfThresholdIs120) {
+ auto selector =
+ createSelector(kModes_60_90_120, kModeId120, {.frameRateMultipleThreshold = 120});
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "23.976 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 23.976_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test25Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "25 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 25.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test2997Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "29.97 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 29.97_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test50Chooses120) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "50 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 50.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test50Chooses60IfThresholdIs120) {
+ auto selector =
+ createSelector(kModes_60_90_120, kModeId120, {.frameRateMultipleThreshold = 120});
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "50 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 50.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test5994Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "59.94 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 59.94_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
@@ -3042,5 +3110,20 @@
EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
}
+TEST_P(RefreshRateSelectorTest, frameRateIsLowerThanMinSupported) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ constexpr Fps kMin = RefreshRateSelector::kMinSupportedFrameRate;
+ constexpr FpsRanges kLowerThanMin = {{60_Hz, 90_Hz}, {kMin / 2, kMin / 2}};
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {DisplayModeId(kModeId60), kLowerThanMin, kLowerThanMin}));
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 682c998..3200003 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -30,6 +30,10 @@
#include "mock/MockLayer.h"
#include "mock/MockSchedulerCallback.h"
+#include <FrontEnd/LayerHierarchy.h>
+
+#include "FpsOps.h"
+
namespace android::scheduler {
using android::mock::createDisplayMode;
@@ -42,6 +46,10 @@
using MockEventThread = android::mock::EventThread;
using MockLayer = android::mock::MockLayer;
+using LayerHierarchy = surfaceflinger::frontend::LayerHierarchy;
+using LayerHierarchyBuilder = surfaceflinger::frontend::LayerHierarchyBuilder;
+using RequestedLayerState = surfaceflinger::frontend::RequestedLayerState;
+
class SchedulerTest : public testing::Test {
protected:
class MockEventThreadConnection : public android::EventThreadConnection {
@@ -83,6 +91,7 @@
mock::SchedulerCallback mSchedulerCallback;
TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
+ surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
ConnectionHandle mConnectionHandle;
MockEventThread* mEventThread;
@@ -149,10 +158,6 @@
EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
mScheduler->setDuration(mConnectionHandle, 10ns, 20ns);
-
- static constexpr size_t kEventConnections = 5;
- EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
- EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
}
TEST_F(SchedulerTest, registerDisplay) FTL_FAKE_GUARD(kMainThreadContext) {
@@ -199,7 +204,8 @@
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+ /*updateAttachedChoreographer*/ false);
}
TEST_F(SchedulerTest, updateDisplayModes) {
@@ -249,6 +255,11 @@
EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms));
EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+
+ const auto savedMinAcquiredBuffers = mFlinger.mutableMinAcquiredBuffers();
+ mFlinger.mutableMinAcquiredBuffers() = 2;
+ EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+ mFlinger.mutableMinAcquiredBuffers() = savedMinAcquiredBuffers;
}
MATCHER(Is120Hz, "") {
@@ -273,11 +284,13 @@
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+ /*updateAttachedChoreographer*/ false);
// No-op if layer requirements have not changed.
EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+ /*updateAttachedChoreographer*/ false);
}
TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
@@ -432,4 +445,344 @@
}
}
+class AttachedChoreographerTest : public SchedulerTest {
+protected:
+ void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
+ Fps expectedChoreographerFps);
+};
+
+TEST_F(AttachedChoreographerTest, registerSingle) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ const sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size());
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+ EXPECT_EQ(1u,
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+ EXPECT_FALSE(
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, registerMultipleOnSameLayer) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ const auto handle = layer->getHandle();
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2);
+
+ EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_))
+ .WillOnce(Return(0))
+ .WillOnce(Return(0));
+
+ const auto mockConnection1 = sp<MockEventThreadConnection>::make(mEventThread);
+ const auto mockConnection2 = sp<MockEventThreadConnection>::make(mEventThread);
+ EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+ .WillOnce(Return(mockConnection1))
+ .WillOnce(Return(mockConnection2));
+
+ const sp<IDisplayEventConnection> connection1 =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle);
+ const sp<IDisplayEventConnection> connection2 =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle);
+
+ EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size());
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+ EXPECT_EQ(2u,
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+ EXPECT_FALSE(
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, registerMultipleOnDifferentLayers) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2);
+ const sp<IDisplayEventConnection> connection1 =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer1->getHandle());
+ const sp<IDisplayEventConnection> connection2 =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer2->getHandle());
+
+ EXPECT_EQ(2u, mScheduler->mutableAttachedChoreographers().size());
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer1->getSequence()));
+ EXPECT_EQ(1u,
+ mScheduler->mutableAttachedChoreographers()[layer1->getSequence()]
+ .connections.size());
+ EXPECT_FALSE(
+ mScheduler->mutableAttachedChoreographers()[layer1->getSequence()].frameRate.isValid());
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer2->getSequence()));
+ EXPECT_EQ(1u,
+ mScheduler->mutableAttachedChoreographers()[layer2->getSequence()]
+ .connections.size());
+ EXPECT_FALSE(
+ mScheduler->mutableAttachedChoreographers()[layer2->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, removedWhenConnectionIsGone) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+ EXPECT_EQ(1u,
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+
+ // The connection is used all over this test, so it is quite hard to release it from here.
+ // Instead, we just do a small shortcut.
+ {
+ EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
+ sp<MockEventThreadConnection> mockConnection =
+ sp<MockEventThreadConnection>::make(mEventThread);
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.clear();
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.emplace(
+ mockConnection);
+ }
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+ mScheduler->updateAttachedChoreographers(hierarchy, 60_Hz);
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+}
+
+TEST_F(AttachedChoreographerTest, removedWhenLayerIsGone) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ const sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ layer.clear();
+ mFlinger.mutableLayersPendingRemoval().clear();
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+}
+
+void AttachedChoreographerTest::frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility,
+ Fps displayFps,
+ Fps expectedChoreographerFps) {
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+
+ layerState.frameRate = layerFps.getValue();
+ layerState.frameRateCompatibility = frameRateCompatibility;
+
+ mScheduler->updateAttachedChoreographers(hierarchy, displayFps);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+ EXPECT_EQ(expectedChoreographerFps,
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+ EXPECT_EQ(expectedChoreographerFps, mEventThreadConnection->frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateDefault) {
+ Fps layerFps = 30_Hz;
+ int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+ Fps displayFps = 60_Hz;
+ Fps expectedChoreographerFps = 30_Hz;
+
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+ layerFps = Fps::fromValue(32.7f);
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateExact) {
+ Fps layerFps = 30_Hz;
+ int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_EXACT;
+ Fps displayFps = 60_Hz;
+ Fps expectedChoreographerFps = 30_Hz;
+
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+ layerFps = Fps::fromValue(32.7f);
+ expectedChoreographerFps = {};
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateExactOrMultiple) {
+ Fps layerFps = 30_Hz;
+ int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+ Fps displayFps = 60_Hz;
+ Fps expectedChoreographerFps = 30_Hz;
+
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+ layerFps = Fps::fromValue(32.7f);
+ expectedChoreographerFps = {};
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParent) {
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+ layerState.frameRate = (30_Hz).getValue();
+ layerState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+ EXPECT_EQ(30_Hz, mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParent2Children) {
+ const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layer1State(LayerCreationArgs(layer1->getSequence()));
+ LayerHierarchy layer1Hierarchy(&layer1State);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&layer1Hierarchy, LayerHierarchy::Variant::Attached));
+
+ RequestedLayerState layer2State(LayerCreationArgs(layer1->getSequence()));
+ LayerHierarchy layer2Hierarchy(&layer2State);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&layer2Hierarchy, LayerHierarchy::Variant::Attached));
+
+ layer1State.frameRate = (30_Hz).getValue();
+ layer1State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ layer2State.frameRate = (20_Hz).getValue();
+ layer2State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+ EXPECT_EQ(60_Hz, mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParentConflictingChildren) {
+ const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layer1State(LayerCreationArgs(layer1->getSequence()));
+ LayerHierarchy layer1Hierarchy(&layer1State);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&layer1Hierarchy, LayerHierarchy::Variant::Attached));
+
+ RequestedLayerState layer2State(LayerCreationArgs(layer1->getSequence()));
+ LayerHierarchy layer2Hierarchy(&layer2State);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&layer2Hierarchy, LayerHierarchy::Variant::Attached));
+
+ layer1State.frameRate = (30_Hz).getValue();
+ layer1State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ layer2State.frameRate = (25_Hz).getValue();
+ layer2State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+ EXPECT_EQ(Fps(), mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateChild) {
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+ parentState.frameRate = (30_Hz).getValue();
+ parentState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+
+ EXPECT_EQ(30_Hz, mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateChildNotOverriddenByParent) {
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+ parentState.frameRate = (30_Hz).getValue();
+ parentState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ layerState.frameRate = (60_Hz).getValue();
+ layerState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+
+ EXPECT_EQ(60_Hz, mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index e176546..703bdda 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -49,9 +49,17 @@
mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
mFlinger.configureAndCommit();
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(
+ TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
.setRefreshRateSelector(std::move(selectorPtr))
- .inject();
+ .inject(std::move(vsyncController), std::move(vsyncTracker));
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
// will call setActiveConfig instead of setActiveConfigWithConstraints.
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index 4e9f293..22b72f9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -23,12 +23,12 @@
#include "DisplayTransactionTestHelpers.h"
#include "FakeDisplayInjector.h"
-#include <android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Boost.h>
namespace android {
namespace {
-using android::hardware::power::Boost;
+using aidl::android::hardware::power::Boost;
TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
using namespace std::chrono_literals;
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index a30f7e0..f3c9d0d 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -39,7 +39,7 @@
TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
- /* modulatorPtr */ nullptr, callback) {}
+ sp<VsyncModulator>::make(VsyncConfigSet{}), callback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
@@ -104,6 +104,7 @@
auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
auto& mutableLayerHistory() { return mLayerHistory; }
+ auto& mutableAttachedChoreographers() { return mAttachedChoreographers; }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
@@ -168,13 +169,22 @@
: VsyncSchedule::HwVsyncState::Disabled;
}
+ void updateAttachedChoreographers(
+ const surfaceflinger::frontend::LayerHierarchy& layerHierarchy,
+ Fps displayRefreshRate) {
+ Scheduler::updateAttachedChoreographers(layerHierarchy, displayRefreshRate);
+ }
+
using Scheduler::onHardwareVsyncRequest;
private:
// ICompositor overrides:
void configure() override {}
- bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
- void composite(TimePoint, VsyncId) override {}
+ bool commit(const scheduler::FrameTarget&) override { return false; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
};
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 945e488..9b3a893 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -20,6 +20,11 @@
#include <chrono>
#include <variant>
+#include <ftl/fake_guard.h>
+#include <ftl/match.h>
+#include <gui/ScreenCaptureResults.h>
+#include <ui/DynamicDisplayInfo.h>
+
#include <compositionengine/Display.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
@@ -27,11 +32,7 @@
#include <compositionengine/impl/Display.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/DisplaySurface.h>
-#include <ftl/fake_guard.h>
-#include <ftl/match.h>
-#include <gui/ScreenCaptureResults.h>
-#include <ui/DynamicDisplayInfo.h>
#include "DisplayDevice.h"
#include "FakeVsyncConfiguration.h"
#include "FrameTracer/FrameTracer.h"
@@ -44,7 +45,6 @@
#include "Scheduler/RefreshRateSelector.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
-#include "SurfaceFlingerDefaultFactory.h"
#include "TestableScheduler.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
@@ -55,6 +55,12 @@
#include "mock/MockSchedulerCallback.h"
#include "mock/system/window/MockNativeWindow.h"
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
+
namespace android {
struct DisplayStatInfo;
@@ -353,32 +359,58 @@
* Forwarding for functions being tested
*/
- void configure() { mFlinger->configure(); }
+ void configure() {
+ ftl::FakeGuard guard(kMainThreadContext);
+ mFlinger->configure();
+ }
void configureAndCommit() {
configure();
commitTransactionsLocked(eDisplayTransactionNeeded);
}
- TimePoint commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
- mFlinger->commit(frameTime, vsyncId, expectedVsyncTime);
- return frameTime;
+ void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime,
+ bool composite = false) {
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ const auto displayIdOpt = mScheduler->pacesetterDisplayId();
+ LOG_ALWAYS_FATAL_IF(!displayIdOpt);
+ const auto displayId = *displayIdOpt;
+
+ constexpr bool kBackpressureGpuComposition = true;
+ scheduler::FrameTargeter frameTargeter(displayId, kBackpressureGpuComposition);
+
+ frameTargeter.beginFrame({.frameBeginTime = frameTime,
+ .vsyncId = vsyncId,
+ .expectedVsyncTime = expectedVsyncTime,
+ .sfWorkDuration = 10ms},
+ *mScheduler->getVsyncSchedule());
+
+ mFlinger->commit(frameTargeter.target());
+
+ if (composite) {
+ mFlinger->composite(displayId, ftl::init::map(displayId, &frameTargeter));
+ }
}
- TimePoint commit(TimePoint frameTime, VsyncId vsyncId) {
- return commit(frameTime, vsyncId, frameTime + Period(10ms));
+ void commit(TimePoint frameTime, VsyncId vsyncId, bool composite = false) {
+ return commit(frameTime, vsyncId, frameTime + Period(10ms), composite);
}
- TimePoint commit() {
+ void commit(bool composite = false) {
const TimePoint frameTime = scheduler::SchedulerClock::now();
- return commit(frameTime, kVsyncId);
+ commit(frameTime, kVsyncId, composite);
}
void commitAndComposite(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
- mFlinger->composite(commit(frameTime, vsyncId, expectedVsyncTime), vsyncId);
+ constexpr bool kComposite = true;
+ commit(frameTime, vsyncId, expectedVsyncTime, kComposite);
}
- void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
+ void commitAndComposite() {
+ constexpr bool kComposite = true;
+ commit(kComposite);
+ }
auto createDisplay(const String8& displayName, bool secure, float requestedRefreshRate = 0.0f) {
return mFlinger->createDisplay(displayName, secure, requestedRefreshRate);
@@ -605,6 +637,10 @@
return SurfaceFlinger::sActiveDisplayRotationFlags;
}
+ auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
+
+ auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
+
auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
~TestableSurfaceFlinger() {
@@ -615,6 +651,7 @@
mutableDisplays().clear();
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
+ mFlinger->mLayersPendingRemoval.clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
@@ -889,6 +926,13 @@
}
sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
+ return inject(std::make_unique<mock::VsyncController>(),
+ std::make_shared<mock::VSyncTracker>());
+ }
+
+ sp<DisplayDevice> inject(std::unique_ptr<android::scheduler::VsyncController> controller,
+ std::shared_ptr<android::scheduler::VSyncTracker> tracker)
+ NO_THREAD_SAFETY_ANALYSIS {
const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
auto& modes = mDisplayModes;
@@ -953,7 +997,9 @@
if (mFlinger.scheduler() && mRegisterDisplay) {
mFlinger.scheduler()->registerDisplay(physicalId,
- display->holdRefreshRateSelector());
+ display->holdRefreshRateSelector(),
+ std::move(controller),
+ std::move(tracker));
}
display->setActiveMode(activeModeId, fps, fps);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a9ae1d3..86ed233 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -1079,7 +1079,6 @@
constexpr size_t TOTAL_FRAMES = 5;
constexpr size_t MISSED_FRAMES = 4;
constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
- constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
constexpr nsecs_t APP_DEADLINE_DELTA = 3'000'000;
@@ -1100,7 +1099,6 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
- mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->recordFrameDuration(1000000, 3000000);
mTimeStats->recordRenderEngineDuration(2000000, 4000000);
@@ -1157,7 +1155,6 @@
EXPECT_EQ(atom.client_composition_frames(), CLIENT_COMPOSITION_FRAMES);
// Display on millis is not checked.
EXPECT_EQ(atom.animation_millis(), 2);
- EXPECT_EQ(atom.event_connection_count(), DISPLAY_EVENT_CONNECTIONS);
EXPECT_THAT(atom.frame_duration(), HistogramEq(buildExpectedHistogram({2}, {1})));
EXPECT_THAT(atom.render_engine_timing(), HistogramEq(buildExpectedHistogram({1, 2}, {1, 1})));
EXPECT_EQ(atom.total_timeline_frames(), 9);
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index afb8efb..1f2a1ed 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -15,7 +15,7 @@
*/
#undef LOG_TAG
-#define LOG_TAG "CompositionTest"
+#define LOG_TAG "TransactionApplicationTest"
#include <compositionengine/Display.h>
#include <compositionengine/mock/DisplaySurface.h>
@@ -306,6 +306,47 @@
~FakeExternalTexture() = default;
};
+TEST_F(TransactionApplicationTest, ApplyTokensUseDifferentQueues) {
+ auto applyToken1 = sp<BBinder>::make();
+ auto applyToken2 = sp<BBinder>::make();
+
+ // Transaction 1 has a buffer with an unfired fence. It should not be ready to be applied.
+ TransactionState transaction1;
+ transaction1.applyToken = applyToken1;
+ transaction1.id = 42069;
+ transaction1.states.emplace_back();
+ transaction1.states[0].state.what |= layer_state_t::eBufferChanged;
+ transaction1.states[0].state.bufferData =
+ std::make_shared<fake::BufferData>(/* bufferId */ 1, /* width */ 1, /* height */ 1,
+ /* pixelFormat */ 0, /* outUsage */ 0);
+ transaction1.states[0].externalTexture =
+ std::make_shared<FakeExternalTexture>(*transaction1.states[0].state.bufferData);
+ transaction1.states[0].state.surface =
+ sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
+ ->getHandle();
+ auto fence = sp<mock::MockFence>::make();
+ EXPECT_CALL(*fence, getStatus()).WillRepeatedly(Return(Fence::Status::Unsignaled));
+ transaction1.states[0].state.bufferData->acquireFence = std::move(fence);
+ transaction1.states[0].state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
+ transaction1.isAutoTimestamp = true;
+
+ // Transaction 2 should be ready to be applied.
+ TransactionState transaction2;
+ transaction2.applyToken = applyToken2;
+ transaction2.id = 2;
+ transaction2.isAutoTimestamp = true;
+
+ mFlinger.setTransactionStateInternal(transaction1);
+ mFlinger.setTransactionStateInternal(transaction2);
+ mFlinger.flushTransactionQueues();
+ auto transactionQueues = mFlinger.getPendingTransactionQueue();
+
+ // Transaction 1 is still in its queue.
+ EXPECT_EQ(transactionQueues[applyToken1].size(), 1u);
+ // Transaction 2 has been dequeued.
+ EXPECT_EQ(transactionQueues[applyToken2].size(), 0u);
+}
+
class LatchUnsignaledTest : public TransactionApplicationTest {
public:
void TearDown() override {
@@ -855,233 +896,13 @@
kExpectedTransactionsPending);
}
-class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest {
-public:
- void SetUp() override {
- LatchUnsignaledTest::SetUp();
- SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
- }
-};
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken,
- {createComposerState(kLayerId, fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged)});
- setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueue) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken,
- {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged)});
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto mixedTransaction =
- createTransactionInfo(kApplyToken,
- {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- createComposerState(kLayerId, fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged)});
- setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto mixedTransaction =
- createTransactionInfo(kApplyToken,
- {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- createComposerState(kLayerId2, fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged)});
- setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- const auto signaledTransaction2 =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) {
- const sp<IBinder> kApplyToken1 =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken2,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
- setTransactionStates({signaledTransaction, unsignaledTransaction},
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueueSameApplyToken) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- setTransactionStates({unsignaledTransaction, signaledTransaction},
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueue) {
- const sp<IBinder> kApplyToken1 =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
- const auto unsignaledTransaction2 =
- createTransactionInfo(kApplyToken2,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
- setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, RespectsBackPressureFlag) {
- const sp<IBinder> kApplyToken1 =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
- const auto kLayerId1 = 1;
- const auto kExpectedTransactionsPending = 1u;
- auto layer =
- sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}));
- auto layerHandle = layer->getHandle();
- const auto setBackPressureFlagTransaction =
- createTransactionInfo(kApplyToken1,
- {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged |
- layer_state_t::eFlagsChanged,
- {layerHandle})});
- setTransactionStates({setBackPressureFlagTransaction}, 0u);
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged,
- {layerHandle}),
- });
- const auto unsignaledTransaction2 =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged,
- {layerHandle}),
- });
- setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
-
- modulateVsync();
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
-}
-
TEST(TransactionHandlerTest, QueueTransaction) {
TransactionHandler handler;
TransactionState transaction;
transaction.applyToken = sp<BBinder>::make();
transaction.id = 42;
handler.queueTransaction(std::move(transaction));
+ handler.collectTransactions();
std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 92411a7..809966f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -37,7 +37,7 @@
static constexpr size_t SMALL_BUFFER_SIZE = 1024;
TransactionTracing mTracing;
- void flush(int64_t vsyncId) { mTracing.flush(vsyncId); }
+ void flush() { mTracing.flush(); }
proto::TransactionTraceFile writeToProto() { return mTracing.writeToProto(); }
proto::TransactionTraceEntry bufferFront() {
@@ -57,7 +57,7 @@
std::vector<TransactionState> transactions;
update.transactions.emplace_back(transaction);
mTracing.addCommittedTransactions(vsyncId, 0, update, {}, false);
- flush(vsyncId);
+ flush();
}
void verifyEntry(const proto::TransactionTraceEntry& actualProto,
@@ -116,7 +116,7 @@
secondUpdate.transactions =
std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
mTracing.addCommittedTransactions(secondTransactionSetVsyncId, 0, secondUpdate, {}, false);
- flush(secondTransactionSetVsyncId);
+ flush();
proto::TransactionTraceFile proto = writeToProto();
ASSERT_EQ(proto.entry().size(), 2);
@@ -158,7 +158,7 @@
VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
mTracing.addCommittedTransactions(VSYNC_ID_FIRST_LAYER_CHANGE, 0, update, {}, false);
- flush(VSYNC_ID_FIRST_LAYER_CHANGE);
+ flush();
}
// add transactions that modify the layer state further so we can test that layer state
@@ -178,7 +178,7 @@
update.transactions.emplace_back(transaction);
VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
mTracing.addCommittedTransactions(VSYNC_ID_SECOND_LAYER_CHANGE, 0, update, {}, false);
- flush(VSYNC_ID_SECOND_LAYER_CHANGE);
+ flush();
}
// remove child layer
@@ -290,7 +290,7 @@
update.transactions.emplace_back(transaction);
mTracing.addCommittedTransactions(mVsyncId, 0, update, {}, false);
- flush(mVsyncId);
+ flush();
}
}
diff --git a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
index af4971b..c7b845e 100644
--- a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
@@ -15,35 +15,23 @@
WindowInfosListenerInvokerTest() : mInvoker(sp<WindowInfosListenerInvoker>::make()) {}
~WindowInfosListenerInvokerTest() {
- std::mutex mutex;
- std::condition_variable cv;
- bool flushComplete = false;
// Flush the BackgroundExecutor thread to ensure any scheduled tasks are complete.
// Otherwise, references those tasks hold may go out of scope before they are done
// executing.
- BackgroundExecutor::getInstance().sendCallbacks({[&]() {
- std::scoped_lock lock{mutex};
- flushComplete = true;
- cv.notify_one();
- }});
- std::unique_lock<std::mutex> lock{mutex};
- cv.wait(lock, [&]() { return flushComplete; });
+ BackgroundExecutor::getInstance().flushQueue();
}
sp<WindowInfosListenerInvoker> mInvoker;
};
-using WindowInfosUpdateConsumer = std::function<void(const gui::WindowInfosUpdate&,
- const sp<gui::IWindowInfosReportedListener>&)>;
+using WindowInfosUpdateConsumer = std::function<void(const gui::WindowInfosUpdate&)>;
class Listener : public gui::BnWindowInfosListener {
public:
Listener(WindowInfosUpdateConsumer consumer) : mConsumer(std::move(consumer)) {}
- binder::Status onWindowInfosChanged(
- const gui::WindowInfosUpdate& update,
- const sp<gui::IWindowInfosReportedListener>& reportedListener) override {
- mConsumer(update, reportedListener);
+ binder::Status onWindowInfosChanged(const gui::WindowInfosUpdate& update) override {
+ mConsumer(update);
return binder::Status::ok();
}
@@ -58,15 +46,17 @@
int callCount = 0;
- mInvoker->addWindowInfosListener(
- sp<Listener>::make([&](const gui::WindowInfosUpdate&,
- const sp<gui::IWindowInfosReportedListener>& reportedListener) {
- std::scoped_lock lock{mutex};
- callCount++;
- cv.notify_one();
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate& update) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
- reportedListener->onWindowInfosReported();
- }));
+ listenerInfo.windowInfosPublisher
+ ->ackWindowInfosReceived(update.vsyncId,
+ listenerInfo.listenerId);
+ }),
+ &listenerInfo);
BackgroundExecutor::getInstance().sendCallbacks(
{[this]() { mInvoker->windowInfosChanged({}, {}, false); }});
@@ -81,21 +71,27 @@
std::mutex mutex;
std::condition_variable cv;
- int callCount = 0;
- const int expectedCallCount = 3;
+ size_t callCount = 0;
+ const size_t expectedCallCount = 3;
+ std::vector<gui::WindowInfosListenerInfo> listenerInfos{expectedCallCount,
+ gui::WindowInfosListenerInfo{}};
- for (int i = 0; i < expectedCallCount; i++) {
- mInvoker->addWindowInfosListener(sp<Listener>::make(
- [&](const gui::WindowInfosUpdate&,
- const sp<gui::IWindowInfosReportedListener>& reportedListener) {
- std::scoped_lock lock{mutex};
- callCount++;
- if (callCount == expectedCallCount) {
- cv.notify_one();
- }
+ for (size_t i = 0; i < expectedCallCount; i++) {
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&, &listenerInfo = listenerInfos[i]](
+ const gui::WindowInfosUpdate&
+ update) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ if (callCount == expectedCallCount) {
+ cv.notify_one();
+ }
- reportedListener->onWindowInfosReported();
- }));
+ listenerInfo.windowInfosPublisher
+ ->ackWindowInfosReceived(update.vsyncId,
+ listenerInfo
+ .listenerId);
+ }),
+ &listenerInfos[i]);
}
BackgroundExecutor::getInstance().sendCallbacks(
@@ -114,17 +110,20 @@
int callCount = 0;
- // Simulate a slow ack by not calling the WindowInfosReportedListener.
- mInvoker->addWindowInfosListener(sp<Listener>::make(
- [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
- std::scoped_lock lock{mutex};
- callCount++;
- cv.notify_one();
- }));
+ // Simulate a slow ack by not calling IWindowInfosPublisher.ackWindowInfosReceived
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+ }),
+ &listenerInfo);
BackgroundExecutor::getInstance().sendCallbacks({[&]() {
- mInvoker->windowInfosChanged({}, {}, false);
- mInvoker->windowInfosChanged({}, {}, false);
+ mInvoker->windowInfosChanged(gui::WindowInfosUpdate{{}, {}, /* vsyncId= */ 0, 0}, {},
+ false);
+ mInvoker->windowInfosChanged(gui::WindowInfosUpdate{{}, {}, /* vsyncId= */ 1, 0}, {},
+ false);
}});
{
@@ -134,7 +133,7 @@
EXPECT_EQ(callCount, 1);
// Ack the first message.
- mInvoker->onWindowInfosReported();
+ listenerInfo.windowInfosPublisher->ackWindowInfosReceived(0, listenerInfo.listenerId);
{
std::unique_lock lock{mutex};
@@ -152,19 +151,21 @@
int callCount = 0;
const int expectedCallCount = 2;
- // Simulate a slow ack by not calling the WindowInfosReportedListener.
- mInvoker->addWindowInfosListener(sp<Listener>::make(
- [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
- std::scoped_lock lock{mutex};
- callCount++;
- if (callCount == expectedCallCount) {
- cv.notify_one();
- }
- }));
+ // Simulate a slow ack by not calling IWindowInfosPublisher.ackWindowInfosReceived
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ if (callCount == expectedCallCount) {
+ cv.notify_one();
+ }
+ }),
+ &listenerInfo);
BackgroundExecutor::getInstance().sendCallbacks({[&]() {
- mInvoker->windowInfosChanged({}, {}, false);
- mInvoker->windowInfosChanged({}, {}, true);
+ mInvoker->windowInfosChanged(gui::WindowInfosUpdate{{}, {}, /* vsyncId= */ 0, 0}, {},
+ false);
+ mInvoker->windowInfosChanged(gui::WindowInfosUpdate{{}, {}, /* vsyncId= */ 1, 0}, {}, true);
}});
{
@@ -182,14 +183,14 @@
int64_t lastUpdateId = -1;
- // Simulate a slow ack by not calling the WindowInfosReportedListener.
- mInvoker->addWindowInfosListener(
- sp<Listener>::make([&](const gui::WindowInfosUpdate& update,
- const sp<gui::IWindowInfosReportedListener>&) {
- std::scoped_lock lock{mutex};
- lastUpdateId = update.vsyncId;
- cv.notify_one();
- }));
+ // Simulate a slow ack by not calling IWindowInfosPublisher.ackWindowInfosReceived
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate& update) {
+ std::scoped_lock lock{mutex};
+ lastUpdateId = update.vsyncId;
+ cv.notify_one();
+ }),
+ &listenerInfo);
BackgroundExecutor::getInstance().sendCallbacks({[&]() {
mInvoker->windowInfosChanged({{}, {}, /* vsyncId= */ 1, 0}, {}, false);
@@ -204,7 +205,7 @@
EXPECT_EQ(lastUpdateId, 1);
// Ack the first message. The third update should be sent.
- mInvoker->onWindowInfosReported();
+ listenerInfo.windowInfosPublisher->ackWindowInfosReceived(1, listenerInfo.listenerId);
{
std::unique_lock lock{mutex};
@@ -225,14 +226,17 @@
// delayed.
BackgroundExecutor::getInstance().sendCallbacks({[&]() {
mInvoker->windowInfosChanged({}, {}, false);
- mInvoker->addWindowInfosListener(sp<Listener>::make(
- [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
- std::scoped_lock lock{mutex};
- callCount++;
- cv.notify_one();
- }));
- mInvoker->windowInfosChanged({}, {}, false);
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+ }),
+ &listenerInfo);
}});
+ BackgroundExecutor::getInstance().flushQueue();
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[&]() { mInvoker->windowInfosChanged({}, {}, false); }});
{
std::unique_lock lock{mutex};
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index d3fb9fc..95004a4 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -51,6 +51,7 @@
~Composer() override;
MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override));
+ MOCK_METHOD(bool, getDisplayConfigurationsSupported, (), (const, override));
MOCK_METHOD0(getCapabilities,
std::vector<aidl::android::hardware::graphics::composer3::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
@@ -70,6 +71,8 @@
MOCK_METHOD4(getDisplayAttribute,
Error(Display, Config config, IComposerClient::Attribute, int32_t*));
MOCK_METHOD2(getDisplayConfigs, Error(Display, std::vector<Config>*));
+ MOCK_METHOD3(getDisplayConfigurations,
+ Error(Display, int32_t, std::vector<DisplayConfiguration>*));
MOCK_METHOD2(getDisplayName, Error(Display, std::string*));
MOCK_METHOD4(getDisplayRequests,
Error(Display, uint32_t*, std::vector<Layer>*, std::vector<uint32_t>*));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
index 0ddc90d..a088aab 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -18,14 +18,14 @@
#include "binder/Status.h"
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
#include <gmock/gmock.h>
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
using android::binder::Status;
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
namespace android::Hwc2::mock {
@@ -33,18 +33,19 @@
public:
MockIPower();
- MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
- MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
- MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
- MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
- MOCK_METHOD(Status, createHintSession,
+ MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos, sp<IPowerHintSession>* session),
+ int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
(override));
- MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index f4ded21..2b9520f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -18,14 +18,14 @@
#include "binder/Status.h"
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
#include <gmock/gmock.h>
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionHint;
using android::binder::Status;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::SessionHint;
-using namespace android::hardware::power;
+using namespace aidl::android::hardware::power;
namespace android::Hwc2::mock {
@@ -33,16 +33,18 @@
public:
MockIPowerHintSession();
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
- MOCK_METHOD(Status, pause, (), (override));
- MOCK_METHOD(Status, resume, (), (override));
- MOCK_METHOD(Status, close, (), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override));
- MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override));
- MOCK_METHOD(Status, sendHint, (SessionHint), (override));
- MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, pause, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, resume, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, close, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, updateTargetWorkDuration, (int64_t), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, reportActualWorkDuration, (const ::std::vector<WorkDuration>&),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index 3caa2b9..d635508 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -32,6 +32,7 @@
MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
(override));
MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
+ MOCK_METHOD(void, notifyCpuLoadUp, (), (override));
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
index 358395d..68fe3c5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -31,18 +31,20 @@
namespace android::Hwc2::mock {
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::power::HalResult;
class MockPowerHalController : public power::PowerHalController {
public:
MockPowerHalController();
~MockPowerHalController() override;
+ MOCK_METHOD(void, init, (), (override));
MOCK_METHOD(HalResult<void>, setBoost, (Boost, int32_t), (override));
MOCK_METHOD(HalResult<void>, setMode, (Mode, bool), (override));
- MOCK_METHOD(HalResult<sp<hardware::power::IPowerHintSession>>, createHintSession,
- (int32_t, int32_t, const std::vector<int32_t>&, int64_t), (override));
+ MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>,
+ createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t),
+ (override));
MOCK_METHOD(HalResult<int64_t>, getHintSessionPreferredRate, (), (override));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 8d57049..9a1a16d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -49,7 +49,6 @@
(const sp<android::EventThreadConnection>&), (const, override));
MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&));
MOCK_METHOD(void, pauseVsyncCallback, (bool));
- MOCK_METHOD(size_t, getEventThreadConnectionCount, (), (override));
MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 306eb4d..22b2ccc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -27,6 +27,7 @@
MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
+ MOCK_METHOD(void, onChoreographerAttached, (), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -34,6 +35,7 @@
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
+ void onChoreographerAttached() override {}
};
} // namespace android::scheduler::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 86fbadc..c82e45b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -34,7 +34,6 @@
MOCK_METHOD0(incrementTotalFrames, void());
MOCK_METHOD0(incrementMissedFrames, void());
MOCK_METHOD0(incrementRefreshRateSwitches, void());
- MOCK_METHOD1(recordDisplayEventConnectionCount, void(int32_t));
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
diff --git a/services/vibratorservice/OWNERS b/services/vibratorservice/OWNERS
index d073e2b..031b333 100644
--- a/services/vibratorservice/OWNERS
+++ b/services/vibratorservice/OWNERS
@@ -1 +1,3 @@
+# Bug component: 345036
+
include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
index b033adb..7e382a3 100644
--- a/services/vibratorservice/TEST_MAPPING
+++ b/services/vibratorservice/TEST_MAPPING
@@ -1,7 +1,27 @@
{
"presubmit": [
{
+ "name": "libvibratorservice_test",
+ "options": [
+ // TODO(b/293603710): Fix flakiness
+ {
+ "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleRunsOnlyAfterDelay"
+ },
+ // TODO(b/293623689): Fix flakiness
+ {
+ "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleMultipleCallbacksRunsInDelayOrder"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
"name": "libvibratorservice_test"
}
+ ],
+ "imports": [
+ {
+ "path": "cts/tests/vibrator"
+ }
]
}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 5965953..07b9569 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,6 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <android/hardware/graphics/common/1.0/types.h>
#include <grallocusage/GrallocUsageConversion.h>
#include <graphicsenv/GraphicsEnv.h>
@@ -25,8 +26,6 @@
#include <sync/sync.h>
#include <system/window.h>
#include <ui/BufferQueueDefs.h>
-#include <ui/DebugUtils.h>
-#include <ui/PixelFormat.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -37,6 +36,7 @@
#include "driver.h"
+using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
using android::hardware::graphics::common::V1_0::BufferUsage;
namespace vulkan {
@@ -503,27 +503,27 @@
*count = num_copied;
}
-android::PixelFormat GetNativePixelFormat(VkFormat format) {
- android::PixelFormat native_format = android::PIXEL_FORMAT_RGBA_8888;
+PixelFormat GetNativePixelFormat(VkFormat format) {
+ PixelFormat native_format = PixelFormat::RGBA_8888;
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SRGB:
- native_format = android::PIXEL_FORMAT_RGBA_8888;
+ native_format = PixelFormat::RGBA_8888;
break;
case VK_FORMAT_R5G6B5_UNORM_PACK16:
- native_format = android::PIXEL_FORMAT_RGB_565;
+ native_format = PixelFormat::RGB_565;
break;
case VK_FORMAT_R16G16B16A16_SFLOAT:
- native_format = android::PIXEL_FORMAT_RGBA_FP16;
+ native_format = PixelFormat::RGBA_FP16;
break;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
- native_format = android::PIXEL_FORMAT_RGBA_1010102;
+ native_format = PixelFormat::RGBA_1010102;
break;
case VK_FORMAT_R8_UNORM:
- native_format = android::PIXEL_FORMAT_R_8;
+ native_format = PixelFormat::R_8;
break;
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
- native_format = android::PIXEL_FORMAT_RGBA_10101010;
+ native_format = PixelFormat::RGBA_10101010;
break;
default:
ALOGV("unsupported swapchain format %d", format);
@@ -532,7 +532,8 @@
return native_format;
}
-android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) {
+android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace,
+ PixelFormat pixelFormat) {
switch (colorspace) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
return HAL_DATASPACE_V0_SRGB;
@@ -551,7 +552,14 @@
case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
return HAL_DATASPACE_V0_SRGB;
case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
- return HAL_DATASPACE_BT2020_LINEAR;
+ if (pixelFormat == PixelFormat::RGBA_FP16) {
+ return static_cast<android_dataspace>(
+ HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_LINEAR |
+ HAL_DATASPACE_RANGE_EXTENDED);
+ } else {
+ return HAL_DATASPACE_BT2020_LINEAR;
+ }
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
return static_cast<android_dataspace>(
HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
@@ -561,9 +569,7 @@
HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
HAL_DATASPACE_RANGE_FULL);
case VK_COLOR_SPACE_HDR10_HLG_EXT:
- return static_cast<android_dataspace>(
- HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_HLG |
- HAL_DATASPACE_RANGE_FULL);
+ return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT:
return static_cast<android_dataspace>(
HAL_DATASPACE_STANDARD_ADOBE_RGB |
@@ -877,6 +883,7 @@
int width, height;
int transform_hint;
int max_buffer_count;
+ int min_undequeued_buffers;
if (surface == VK_NULL_HANDLE) {
const InstanceData& instance_data = GetData(physicalDevice);
ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query;
@@ -929,17 +936,24 @@
return VK_ERROR_SURFACE_LOST_KHR;
}
+ err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &min_undequeued_buffers);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+
if (pPresentMode && IsSharedPresentMode(pPresentMode->presentMode)) {
capabilities->minImageCount = 1;
capabilities->maxImageCount = 1;
} else if (pPresentMode && pPresentMode->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
- // TODO: use undequeued buffer requirement for more precise bound
- capabilities->minImageCount = std::min(max_buffer_count, 4);
+ capabilities->minImageCount =
+ std::min(max_buffer_count, min_undequeued_buffers + 2);
capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
} else {
- // TODO: if we're able to, provide better bounds on the number of buffers
- // for other modes as well.
- capabilities->minImageCount = std::min(max_buffer_count, 3);
+ capabilities->minImageCount =
+ std::min(max_buffer_count, min_undequeued_buffers + 1);
capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
}
}
@@ -1104,12 +1118,17 @@
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
imageFormatInfo.format =
pSurfaceFormats[i].surfaceFormat.format;
+ imageFormatInfo.type = VK_IMAGE_TYPE_2D;
+ imageFormatInfo.usage =
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imageFormatInfo.pNext = nullptr;
VkImageCompressionControlEXT compressionControl = {};
compressionControl.sType =
VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT;
compressionControl.pNext = imageFormatInfo.pNext;
+ compressionControl.flags =
+ VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT;
imageFormatInfo.pNext = &compressionControl;
@@ -1348,10 +1367,10 @@
if (!allocator)
allocator = &GetData(device).allocator;
- android::PixelFormat native_pixel_format =
+ PixelFormat native_pixel_format =
GetNativePixelFormat(create_info->imageFormat);
android_dataspace native_dataspace =
- GetNativeDataspace(create_info->imageColorSpace);
+ GetNativeDataspace(create_info->imageColorSpace, native_pixel_format);
if (native_dataspace == HAL_DATASPACE_UNKNOWN) {
ALOGE(
"CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) "
@@ -1449,10 +1468,11 @@
const auto& dispatch = GetData(device).driver;
- err = native_window_set_buffers_format(window, native_pixel_format);
+ err = native_window_set_buffers_format(
+ window, static_cast<int>(native_pixel_format));
if (err != android::OK) {
ALOGE("native_window_set_buffers_format(%s) failed: %s (%d)",
- decodePixelFormat(native_pixel_format).c_str(), strerror(-err), err);
+ toString(native_pixel_format).c_str(), strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}