Merge "SF: Pass the release fence to the owning layer instead of the clone" into udc-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c71c4a0..39eb4ab 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -14,6 +14,7 @@
include/powermanager/
libs/binder/fuzzer/
libs/binder/
+ libs/binderdebug/
libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 2e0c95a..e66cc41 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -297,12 +297,23 @@
write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
# allow creating event triggers
- chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger
+ chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
+
+ # allow enabling rss_stat_throttled
+ chmod 0666 /sys/kernel/tracing/events/synthetic/rss_stat_throttled/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/synthetic/rss_stat_throttled/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
@@ -394,6 +405,130 @@
chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace
chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace
+# Handle hyp tracing instance
+on late-init && property:ro.boot.hypervisor.vm.supported=1
+
+# Hypervisor tracing instance doesn't support changing trace_clock
+ chmod 0440 /sys/kernel/debug/tracing/hyp/trace_clock
+ chmod 0440 /sys/kernel/tracing/hyp/trace_clock
+
+ chmod 0660 /sys/kernel/debug/tracing/hyp/buffer_size_kb
+ chmod 0660 /sys/kernel/tracing/hyp/buffer_size_kb
+
+ chmod 0660 /sys/kernel/debug/tracing/hyp/tracing_on
+ chmod 0660 /sys/kernel/tracing/hyp/tracing_on
+
+# Tracing disabled by default
+ write /sys/kernel/debug/tracing/hyp/tracing_on 0
+ write /sys/kernel/tracing/hyp/tracing_on 0
+
+# Read and truncate the hyp trace.
+ chmod 0660 /sys/kernel/debug/tracing/hyp/trace
+ chmod 0660 /sys/kernel/tracing/hyp/trace
+
+# Read and truncate the per-CPU kernel trace.
+# Cannot use wildcards in .rc files. Update this if there is a phone with
+# TODO(b/249050813, ioffe): introduce per-cpu wildcard
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu0/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu0/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu1/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu1/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu2/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu2/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu3/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu3/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu4/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu4/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu5/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu5/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu6/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu6/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu7/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu7/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu8/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu8/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu9/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu9/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu10/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu10/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu11/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu11/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu12/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu12/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu13/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu13/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu14/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu14/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu15/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu15/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu16/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu16/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu17/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu17/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu18/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu18/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu19/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu19/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu20/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu20/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu21/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu21/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu22/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu22/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu23/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu23/trace
+
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/header_page
+ chmod 0440 /sys/kernel/tracing/hyp/events/header_page
+
+# Hyp events start here
+
+# hyp_enter event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/id
+
+# hyp_exit event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/id
+
+# host_hcall event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_hcall/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_hcall/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_hcall/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_hcall/id
+
+# host_smc event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_smc/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_smc/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_smc/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_smc/id
+
+# host_mem_abort event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/format
+ 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
@@ -405,6 +540,13 @@
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/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 5f56531..ab81ecf 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -3,3 +3,4 @@
gavincorkery@google.com
nandana@google.com
jsharkey@android.com
+smoreland@google.com
\ No newline at end of file
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a48313a..6f84e7c 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1254,8 +1254,9 @@
dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
dumpsys.writeDumpFooter(STDOUT_FILENO, service, std::chrono::milliseconds(1));
} else {
- status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
- if (status == OK) {
+ status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP | Dumpsys::TYPE_PID,
+ service, args);
+ if (status == OK) {
dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
std::chrono::duration<double> elapsed_seconds;
if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH &&
@@ -1902,6 +1903,9 @@
}
ds.AddDir(PREREBOOT_DATA_DIR, false);
add_mountinfo();
+ for (const char* path : {"/proc/cpuinfo", "/proc/meminfo"}) {
+ ds.AddZipEntry(ZIP_ROOT_DIR + path, path);
+ }
DumpIpTablesAsRoot();
DumpDynamicPartitionInfo();
ds.AddDir(OTA_METADATA_DIR, true);
@@ -2062,6 +2066,8 @@
SEC_TO_MSEC(10));
RunDumpsys("DUMPSYS", {"telephony.registry"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"telecom"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
if (include_sensitive_info) {
// Contains raw IP addresses, omit from reports on user builds.
RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 34ea759..1693ed5 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -442,6 +442,16 @@
static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
const std::string& location, bool read_write, bool is_secondary_dex) {
std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
+ if (read_write && GetBoolProperty("dalvik.vm.useartservice", false)) {
+ // ART Service doesn't use flock and instead assumes profile files are
+ // immutable, so ensure we don't open a file for writing when it's
+ // active.
+ // TODO(b/251921228): Normally installd isn't called at all in that
+ // case, but OTA is still an exception that uses the legacy code.
+ LOG(ERROR) << "Opening ref profile " << profile
+ << " for writing is unsafe when ART Service is enabled.";
+ return invalid_unique_fd();
+ }
return open_profile(
uid,
profile,
@@ -450,14 +460,13 @@
}
static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
- const std::string& location, bool read_write, bool is_secondary_dex) {
+ const std::string& location,
+ bool is_secondary_dex) {
std::string profile_path = create_reference_profile_path(package_name, location,
is_secondary_dex);
- unique_fd ufd = open_profile(
- uid,
- profile_path,
- read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
- S_IRUSR | S_IWUSR | S_IRGRP); // so that ART can also read it when apps run.
+ unique_fd ufd = open_profile(uid, profile_path, O_RDONLY,
+ S_IRUSR | S_IWUSR |
+ S_IRGRP); // so that ART can also read it when apps run.
return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
clear_profile(path);
@@ -1104,8 +1113,7 @@
location = profile_name;
}
}
- return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
- is_secondary_dex);
+ return open_reference_profile_as_unique_file(uid, pkgname, location, is_secondary_dex);
}
// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
@@ -1909,10 +1917,11 @@
// Open the reference profile if needed.
UniqueFile reference_profile = maybe_open_reference_profile(
pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
-
- if (reference_profile.fd() == -1) {
- // We don't create an app image without reference profile since there is no speedup from
- // loading it in that case and instead will be a small overhead.
+ struct stat sbuf;
+ if (reference_profile.fd() == -1 ||
+ (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) {
+ // We don't create an app image with empty or non existing reference profile since there
+ // is no speedup from loading it in that case and instead will be a small overhead.
generate_app_image = false;
}
@@ -1933,7 +1942,8 @@
RUNTIME_NATIVE_BOOT_NAMESPACE,
ENABLE_JITZYGOTE_IMAGE,
/*default_value=*/ "");
- bool use_jitzygote_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable();
+ bool compile_without_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable() ||
+ force_compile_without_image();
// Decide whether to use dex2oat64.
bool use_dex2oat64 = false;
@@ -1955,7 +1965,7 @@
in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context,
join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
debuggable, boot_complete, for_restore, target_sdk_version,
- enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
+ enable_hidden_api_checks, generate_compact_dex, compile_without_image,
background_job_compile, compilation_reason);
bool cancelled = false;
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index b5bc28c..4f691c9 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -65,6 +65,10 @@
return create_cache_path_default(path, src, instruction_set);
}
+bool force_compile_without_image() {
+ return false;
+}
+
static bool initialize_globals() {
return init_globals_from_data_and_root();
}
diff --git a/cmds/installd/installd_deps.h b/cmds/installd/installd_deps.h
index 5093178..0d0a7fa 100644
--- a/cmds/installd/installd_deps.h
+++ b/cmds/installd/installd_deps.h
@@ -57,6 +57,9 @@
const char *src,
const char *instruction_set);
+// If true, pass "--force-jit-zygote" to dex2oat (i.e., compile without a boot image).
+extern bool force_compile_without_image();
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 6a3120c..7cabdb0 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -308,7 +308,7 @@
// This is different from the normal installd. We only do the base
// directory, the rest will be created on demand when each app is compiled.
if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) {
- LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
+ PLOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
return false;
}
@@ -460,7 +460,7 @@
// this tool will wipe the OTA artifact cache and try again (for robustness after
// a failed OTA with remaining cache artifacts).
if (access(apk_path, F_OK) != 0) {
- LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
+ PLOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
return true;
}
@@ -711,6 +711,11 @@
return true;
}
+bool force_compile_without_image() {
+ // We don't have a boot image anyway. Compile without a boot image.
+ return true;
+}
+
static int log_callback(int type, const char *fmt, ...) {
va_list ap;
int priority;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c62734a..1b7acab 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -45,6 +45,10 @@
namespace android {
namespace installd {
+// We don't know the filesystem types of the partitions in the update package,
+// so just try the possibilities one by one.
+static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
+
static void CloseDescriptor(int fd) {
if (fd >= 0) {
int result = close(fd);
@@ -82,6 +86,27 @@
}
}
+static bool TryMountWithFstypes(const char* block_device, const char* target) {
+ for (int i = 0; i < kTryMountFsTypes.size(); ++i) {
+ const char* fstype = kTryMountFsTypes[i];
+ int mount_result = mount(block_device, target, fstype, MS_RDONLY, /* data */ nullptr);
+ if (mount_result == 0) {
+ return true;
+ }
+ if (errno == EINVAL && i < kTryMountFsTypes.size() - 1) {
+ // Only try the next fstype if mounting failed due to the current one
+ // being invalid.
+ LOG(WARNING) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype << " - trying " << kTryMountFsTypes[i + 1];
+ } else {
+ PLOG(ERROR) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype;
+ return false;
+ }
+ }
+ __builtin_unreachable();
+}
+
static void TryExtraMount(const char* name, const char* slot, const char* target) {
std::string partition_name = StringPrintf("%s%s", name, slot);
@@ -91,12 +116,7 @@
if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
std::string path;
if (dm.GetDmDevicePathByName(partition_name, &path)) {
- int mount_result = mount(path.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- if (mount_result == 0) {
+ if (TryMountWithFstypes(path.c_str(), target)) {
return;
}
}
@@ -105,12 +125,7 @@
// Fall back and attempt a direct mount.
std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
- int mount_result = mount(block_device.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- UNUSED(mount_result);
+ (void)TryMountWithFstypes(block_device.c_str(), target);
}
// Entry for otapreopt_chroot. Expected parameters are:
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index f950276..db5c34e 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -60,6 +60,11 @@
i=0
while ((i<MAXIMUM_PACKAGES)) ; do
+ DONE=$(cmd otadexopt done)
+ if [ "$DONE" = "OTA complete." ] ; then
+ break
+ fi
+
DEXOPT_PARAMS=$(cmd otadexopt next)
/system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
@@ -67,13 +72,8 @@
PROGRESS=$(cmd otadexopt progress)
print -u${STATUS_FD} "global_progress $PROGRESS"
- DONE=$(cmd otadexopt done)
- if [ "$DONE" = "OTA incomplete." ] ; then
- sleep 1
- i=$((i+1))
- continue
- fi
- break
+ sleep 1
+ i=$((i+1))
done
DONE=$(cmd otadexopt done)
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 4976646..a00c2c7 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -67,6 +67,10 @@
return false;
}
+bool force_compile_without_image() {
+ return false;
+}
+
static void mkdir(const char* path) {
const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
::mkdir(fullPath.c_str(), 0755);
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 3b589dc..be4ca43 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -73,6 +73,10 @@
return create_cache_path_default(path, src, instruction_set);
}
+bool force_compile_without_image() {
+ return false;
+}
+
static void run_cmd(const std::string& cmd) {
system(cmd.c_str());
}
@@ -185,7 +189,7 @@
std::optional<std::string> volume_uuid_;
std::string package_name_;
std::string apk_path_;
- std::string empty_dm_file_;
+ std::string dm_file_;
std::string app_apk_dir_;
std::string app_private_dir_ce_;
std::string app_private_dir_de_;
@@ -248,26 +252,6 @@
<< " : " << error_msg;
}
- // Create an empty dm file.
- empty_dm_file_ = apk_path_ + ".dm";
- {
- int fd = open(empty_dm_file_.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- return ::testing::AssertionFailure() << "Could not open " << empty_dm_file_;
- }
- FILE* file = fdopen(fd, "wb");
- if (file == nullptr) {
- return ::testing::AssertionFailure() << "Null file for " << empty_dm_file_
- << " fd=" << fd;
- }
- ZipWriter writer(file);
- // Add vdex to zip.
- writer.StartEntry("primary.prof", ZipWriter::kCompress);
- writer.FinishEntry();
- writer.Finish();
- fclose(file);
- }
-
// Create the app user data.
binder::Status status = service_->createAppData(
volume_uuid_,
@@ -316,6 +300,46 @@
<< secondary_dex_de_ << " : " << error_msg;
}
+ // Create a non-empty dm file.
+ dm_file_ = apk_path_ + ".dm";
+ {
+ android::base::unique_fd fd(open(dm_file_.c_str(),
+ O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR));
+ if (fd.get() < 0) {
+ return ::testing::AssertionFailure() << "Could not open " << dm_file_;
+ }
+ FILE* file = fdopen(fd.release(), "wb");
+ if (file == nullptr) {
+ return ::testing::AssertionFailure() << "Null file for " << dm_file_
+ << " fd=" << fd.get();
+ }
+
+ // Create a profile file.
+ std::string profile_file = app_private_dir_ce_ + "/primary.prof";
+ run_cmd("profman --generate-test-profile=" + profile_file);
+
+ // Add profile to zip.
+ ZipWriter writer(file);
+ writer.StartEntry("primary.prof", ZipWriter::kCompress);
+ android::base::unique_fd profile_fd(open(profile_file.c_str(), O_RDONLY));
+ if (profile_fd.get() < 0) {
+ return ::testing::AssertionFailure() << "Failed to open profile '"
+ << profile_file << "'";
+ }
+ std::string profile_content;
+ if (!android::base::ReadFdToString(profile_fd, &profile_content)) {
+ return ::testing::AssertionFailure() << "Failed to read profile "
+ << profile_file << "'";
+ }
+ writer.WriteBytes(profile_content.c_str(), profile_content.length());
+ writer.FinishEntry();
+ writer.Finish();
+ fclose(file);
+
+ // Delete the temp file.
+ unlink(profile_file.c_str());
+ }
+
// Fix app data uid.
status = service_->fixupAppData(volume_uuid_, kTestUserId);
if (!status.isOk()) {
@@ -608,7 +632,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
int64_t odex_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_,
@@ -657,13 +681,13 @@
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC |
DEXOPT_GENERATE_APP_IMAGE,
oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
- /*binder_result=*/nullptr, empty_dm_file_.c_str());
+ /*binder_result=*/nullptr, dm_file_.c_str());
checkVisibility(in_dalvik_cache, ODEX_IS_PUBLIC);
CompilePrimaryDexOk("speed-profile",
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE,
oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
- /*binder_result=*/nullptr, empty_dm_file_.c_str());
+ /*binder_result=*/nullptr, dm_file_.c_str());
checkVisibility(in_dalvik_cache, ODEX_IS_PRIVATE);
}
};
@@ -787,7 +811,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryProfilePublic) {
@@ -799,7 +823,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) {
@@ -811,7 +835,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptBlockPrimary) {
@@ -874,7 +898,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -893,7 +917,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -926,7 +950,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
// Enable the property and use dex2oat64.
ASSERT_TRUE(android::base::SetProperty(property, "true")) << property;
CompilePrimaryDexOk("speed-profile",
@@ -936,7 +960,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
class PrimaryDexReCompilationTest : public DexoptTest {
@@ -1143,7 +1167,7 @@
service_->prepareAppProfile(package_name, has_user_id ? kTestUserId : USER_NULL,
kTestAppId, profile_name, apk_path_,
has_dex_metadata ? std::make_optional<std::string>(
- empty_dm_file_)
+ dm_file_)
: std::nullopt,
&result));
ASSERT_EQ(expected_result, result);
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index f86f1d5..858a92c 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -113,6 +113,10 @@
return create_cache_path_default(path, src, instruction_set);
}
+bool force_compile_without_image() {
+ return false;
+}
+
static std::string get_full_path(const std::string& path) {
return StringPrintf("%s/%s", kTestPath.c_str(), path.c_str());
}
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index d5ca725..5e8ef5d 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -75,7 +75,7 @@
ProcessState::initWithDriver("/dev/vndbinder");
#endif
#ifndef __ANDROID__
- setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
#endif
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 07809e2..4da0cd6 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -228,8 +228,13 @@
#endif // !VENDORSERVICEMANAGER
ServiceManager::Service::~Service() {
- if (!hasClients) {
- // only expected to happen on process death
+ if (hasClients) {
+ // only expected to happen on process death, we don't store the service
+ // name this late (it's in the map that holds this service), but if it
+ // is happening, we might want to change 'unlinkToDeath' to explicitly
+ // clear this bit so that we can abort in other cases, where it would
+ // mean inconsistent logic in servicemanager (unexpected and tested, but
+ // the original lazy service impl here had that bug).
LOG(WARNING) << "a service was removed when there are clients";
}
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index a737bd3..bdd5172 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -167,12 +167,6 @@
}
prebuilt_etc {
- name: "android.hardware.telephony.satellite.prebuilt.xml",
- src: "android.hardware.telephony.satellite.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"],
diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml
deleted file mode 100644
index 5966cba..0000000
--- a/data/etc/android.hardware.telephony.satellite.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-
-<!-- Feature for devices that support Satellite communication via Satellite HAL APIs. -->
-<permissions>
- <feature name="android.hardware.telephony.satellite" />
-</permissions>
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
index ce30b41..165a868 100644
--- a/headers/media_plugin/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -561,6 +561,7 @@
case OMX_IndexConfigPriority: return "ConfigPriority";
case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate";
case OMX_IndexParamConsumerUsageBits: return "ParamConsumerUsageBits";
+ case OMX_IndexParamConsumerUsageBits64: return "ParamConsumerUsageBits64";
case OMX_IndexConfigLatency: return "ConfigLatency";
default: return asString((OMX_INDEXTYPE)i, def);
}
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index 0af40dd..5ddd719 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -105,6 +105,7 @@
OMX_IndexConfigLowLatency, /**< reference: OMX_CONFIG_BOOLEANTYPE */
OMX_IndexConfigAndroidTunnelPeek, /**< reference: OMX_CONFIG_BOOLEANTYPE */
OMX_IndexConfigAndroidTunnelPeekLegacyMode, /**< reference: OMX_CONFIG_BOOLEANTYPE */
+ OMX_IndexParamConsumerUsageBits64, /**< reference: OMX_PARAM_U64TYPE */
OMX_IndexExtOtherEndUnused,
/* Time configurations */
diff --git a/include/android/OWNERS b/include/android/OWNERS
new file mode 100644
index 0000000..38f9c55
--- /dev/null
+++ b/include/android/OWNERS
@@ -0,0 +1 @@
+per-file input.h, keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index cd8e63d..f999708 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -219,12 +219,16 @@
*
* Note that this time should \b not be used to advance animation clocks.
* Instead, see AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos().
+ *
+ * Available since API level 33.
*/
int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
/**
* The number of possible frame timelines.
+ *
+ * Available since API level 33.
*/
size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
@@ -233,15 +237,20 @@
* Gets the index of the platform-preferred frame timeline.
* The preferred frame timeline is the default
* by which the platform scheduled the app, based on the device configuration.
+ *
+ * Available since API level 33.
*/
size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
/**
* Gets the token used by the platform to identify the frame timeline at the given \c index.
+ * q
+ * Available since API level 33.
*
* \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
* AChoreographerFrameCallbackData_getFrameTimelinesLength()
+ *
*/
AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
@@ -250,6 +259,8 @@
* Gets the time in nanoseconds at which the frame described at the given \c index is expected to
* be presented. This time should be used to advance any animation clocks.
*
+ * Available since API level 33.
+ *
* \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
* AChoreographerFrameCallbackData_getFrameTimelinesLength()
*/
@@ -260,6 +271,8 @@
* Gets the time in nanoseconds at which the frame described at the given \c index needs to be
* ready by in order to be presented on time.
*
+ * Available since API level 33.
+ *
* \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
* AChoreographerFrameCallbackData_getFrameTimelinesLength()
*/
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 79c59f2..e4926a6 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -524,6 +524,8 @@
* Sets the desired extended range brightness for the layer. This only applies for layers whose
* dataspace has RANGE_EXTENDED set on it.
*
+ * Available since API level 34.
+ *
* @param surface_control The layer whose extended range brightness is being specified
* @param currentBufferRatio The current hdr/sdr ratio of the current buffer as represented as
* peakHdrBrightnessInNits / targetSdrWhitePointInNits. For example if the
@@ -653,6 +655,8 @@
* and pushing buffers earlier for server side queuing will be advantageous
* in such cases.
*
+ * Available since API level 31.
+ *
* \param transaction The transaction in which to make the change.
* \param surface_control The ASurfaceControl on which to control buffer backpressure behavior.
* \param enableBackPressure Whether to enable back pressure.
@@ -674,6 +678,8 @@
* AChoreographer_postVsyncCallback(). The \c vsyncId can then be extracted from the
* callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId().
*
+ * Available since API level 33.
+ *
* \param vsyncId The vsync ID received from AChoreographer, setting the frame's presentation target
* to the corresponding expected presentation time and deadline from the frame to be rendered. A
* stale or invalid value will be ignored.
diff --git a/include/ftl/details/optional.h b/include/ftl/details/optional.h
index bff7c1e..e45c1f5 100644
--- a/include/ftl/details/optional.h
+++ b/include/ftl/details/optional.h
@@ -54,5 +54,15 @@
template <typename F, typename T>
using and_then_result_t = typename and_then_result<F, T>::type;
+template <typename F, typename T>
+struct or_else_result {
+ using type = remove_cvref_t<std::invoke_result_t<F>>;
+ static_assert(std::is_same_v<type, std::optional<T>> || std::is_same_v<type, Optional<T>>,
+ "or_else function must return an optional T");
+};
+
+template <typename F, typename T>
+using or_else_result_t = typename or_else_result<F, T>::type;
+
} // namespace details
} // namespace android::ftl
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
index a818128..94d8e3d 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -96,13 +96,25 @@
return R();
}
+ // Returns this Optional<T> if not nullopt, or else the Optional<T> returned by the function F.
+ template <typename F>
+ constexpr auto or_else(F&& f) const& -> details::or_else_result_t<F, T> {
+ if (has_value()) return *this;
+ return std::forward<F>(f)();
+ }
+
+ template <typename F>
+ constexpr auto or_else(F&& f) && -> details::or_else_result_t<F, T> {
+ if (has_value()) return std::move(*this);
+ return std::forward<F>(f)();
+ }
+
// Delete new for this class. Its base doesn't have a virtual destructor, and
// if it got deleted via base class pointer, it would cause undefined
// behavior. There's not a good reason to allocate this object on the heap
// anyway.
static void* operator new(size_t) = delete;
static void* operator new[](size_t) = delete;
-
};
template <typename T, typename U>
diff --git a/include/input/Input.h b/include/input/Input.h
index 608519b..e8af5f7 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -855,6 +855,8 @@
const PointerCoords&);
static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
const PointerCoords&);
+ // The rounding precision for transformed motion events.
+ static constexpr float ROUNDING_PRECISION = 0.001f;
protected:
int32_t mAction;
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 4668fce..9dedd2b 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -30,6 +30,12 @@
int value;
};
+struct EvdevEventLabel {
+ std::string type;
+ std::string code;
+ std::string value;
+};
+
// NOTE: If you want a new key code, axis code, led code or flag code in keylayout file,
// then you must add it to InputEventLabels.cpp.
@@ -52,6 +58,8 @@
static std::optional<int> getLedByLabel(const char* label);
+ static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value);
+
private:
static const std::unordered_map<std::string, int> KEYCODES;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 1c52792..a1be542 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -38,6 +38,7 @@
#include <binder/IBinder.h>
#include <binder/Parcelable.h>
#include <input/Input.h>
+#include <input/InputVerifier.h>
#include <sys/stat.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
@@ -444,6 +445,7 @@
private:
std::shared_ptr<InputChannel> mChannel;
+ InputVerifier mInputVerifier;
};
/*
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
new file mode 100644
index 0000000..d4589f5
--- /dev/null
+++ b/include/input/InputVerifier.h
@@ -0,0 +1,49 @@
+/*
+ * 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 <input/Input.h>
+#include <map>
+
+namespace android {
+
+/*
+ * Crash if the provided touch stream is inconsistent.
+ *
+ * TODO(b/211379801): Add support for hover events:
+ * - No hover move without enter
+ * - No touching pointers when hover enter
+ * - No hovering pointers when touching
+ * - Only 1 hovering pointer max
+ */
+class InputVerifier {
+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);
+
+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;
+};
+
+} // namespace android
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index b5e6f65..c67310e 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -235,7 +235,7 @@
KeyedVector<int32_t, Key*> mKeys;
KeyboardType mType;
std::string mLoadFileName;
- bool mLayoutOverlayApplied;
+ bool mLayoutOverlayApplied = false;
std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping;
std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode;
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 68ebf75..de8ddca 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -22,6 +22,7 @@
#include <string>
#include <unordered_map>
+#include <android-base/result.h>
#include <android-base/thread_annotations.h>
#include <android/sysprop/InputProperties.sysprop.h>
#include <input/Input.h>
@@ -66,21 +67,27 @@
* checkEnableMotionPredition: the function to check whether the prediction should run. Used to
* provide an additional way of turning prediction on and off. Can be toggled at runtime.
*/
- MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath = nullptr,
+ MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled);
- void record(const MotionEvent& event);
- std::vector<std::unique_ptr<MotionEvent>> predict(nsecs_t timestamp);
+ /**
+ * Record the actual motion received by the view. This event will be used for calculating the
+ * predictions.
+ *
+ * @return empty result if the event was processed correctly, error if the event is not
+ * 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:
const nsecs_t mPredictionTimestampOffsetNanos;
- const std::string mModelPath;
const std::function<bool()> mCheckMotionPredictionEnabled;
std::unique_ptr<TfLiteMotionPredictorModel> mModel;
- // Buffers/events for each device seen by record().
- std::unordered_map</*deviceId*/ int32_t, TfLiteMotionPredictorBuffers> mDeviceBuffers;
- std::unordered_map</*deviceId*/ int32_t, MotionEvent> mLastEvents;
+
+ std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
+ std::optional<MotionEvent> mLastEvent;
};
} // namespace android
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
index 28e4816..2e44142 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -18,7 +18,11 @@
#include <android-base/result.h>
#include <utils/Tokenizer.h>
+
+#include <optional>
+#include <string>
#include <unordered_map>
+#include <unordered_set>
namespace android {
@@ -57,14 +61,18 @@
*/
void addProperty(const std::string& key, const std::string& value);
- /* Gets the value of a property and parses it.
- * Returns true and sets outValue if the key was found and its value was parsed successfully.
- * Otherwise returns false and does not modify outValue. (Also logs a warning.)
+ /* Returns a set of all property keys starting with the given prefix. */
+ std::unordered_set<std::string> getKeysWithPrefix(const std::string& prefix) const;
+
+ /* Gets the value of a property and parses it. Returns nullopt if the key wasn't found or
+ * couldn't be parsed as the requested type. (Warnings are also logged in the case of parsing
+ * failures.)
*/
- bool tryGetProperty(const std::string& key, std::string& outValue) const;
- bool tryGetProperty(const std::string& key, bool& outValue) const;
- bool tryGetProperty(const std::string& key, int32_t& outValue) const;
- bool tryGetProperty(const std::string& key, float& outValue) const;
+ std::optional<std::string> getString(const std::string& key) const;
+ std::optional<bool> getBool(const std::string& key) const;
+ std::optional<int32_t> getInt(const std::string& key) const;
+ std::optional<float> getFloat(const std::string& key) const;
+ std::optional<double> getDouble(const std::string& key) const;
/* Adds all values from the specified property map. */
void addAll(const PropertyMap* map);
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 54e2851..7de551b41 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -99,7 +99,7 @@
class TfLiteMotionPredictorModel {
public:
// Creates a model from an encoded Flatbuffer model.
- static std::unique_ptr<TfLiteMotionPredictorModel> create(const char* modelPath);
+ static std::unique_ptr<TfLiteMotionPredictorModel> create();
~TfLiteMotionPredictorModel();
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
new file mode 100644
index 0000000..13ffb58
--- /dev/null
+++ b/include/input/VirtualInputDevice.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <android-base/unique_fd.h>
+
+namespace android {
+
+enum class UinputAction {
+ RELEASE = 0,
+ PRESS = 1,
+ MOVE = 2,
+ CANCEL = 3,
+};
+
+class VirtualInputDevice {
+public:
+ VirtualInputDevice(android::base::unique_fd fd);
+ virtual ~VirtualInputDevice();
+
+protected:
+ const android::base::unique_fd mFd;
+ bool writeInputEvent(uint16_t type, uint16_t code, int32_t value);
+ bool writeEvKeyEvent(int32_t androidCode, int32_t androidAction,
+ const std::map<int, int>& evKeyCodeMapping,
+ const std::map<int, UinputAction>& actionMapping);
+};
+
+class VirtualKeyboard : public VirtualInputDevice {
+public:
+ static const std::map<int, int> KEY_CODE_MAPPING;
+ // Expose to share with VirtualDpad.
+ static const std::map<int, UinputAction> KEY_ACTION_MAPPING;
+ VirtualKeyboard(android::base::unique_fd fd);
+ virtual ~VirtualKeyboard() override;
+ bool writeKeyEvent(int32_t androidKeyCode, int32_t androidAction);
+};
+
+class VirtualDpad : public VirtualInputDevice {
+public:
+ static const std::map<int, int> DPAD_KEY_CODE_MAPPING;
+ VirtualDpad(android::base::unique_fd fd);
+ virtual ~VirtualDpad() override;
+ bool writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction);
+};
+
+class VirtualMouse : public VirtualInputDevice {
+public:
+ VirtualMouse(android::base::unique_fd fd);
+ virtual ~VirtualMouse() override;
+ bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction);
+ // TODO(b/259554911): changing float parameters to int32_t.
+ bool writeRelativeEvent(float relativeX, float relativeY);
+ bool writeScrollEvent(float xAxisMovement, float yAxisMovement);
+
+private:
+ static const std::map<int, UinputAction> BUTTON_ACTION_MAPPING;
+ static const std::map<int, int> BUTTON_CODE_MAPPING;
+};
+
+class VirtualTouchscreen : public VirtualInputDevice {
+public:
+ VirtualTouchscreen(android::base::unique_fd fd);
+ virtual ~VirtualTouchscreen() override;
+ // TODO(b/259554911): changing float parameters to int32_t.
+ bool writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action, float locationX,
+ float locationY, float pressure, float majorAxisSize);
+
+private:
+ static const std::map<int, UinputAction> TOUCH_ACTION_MAPPING;
+ static const std::map<int, int> TOOL_TYPE_MAPPING;
+
+ /* The set of active touch pointers on this device.
+ * We only allow pointer id to go up to MAX_POINTERS because the maximum slots of virtual
+ * touchscreen is set up with MAX_POINTERS. Note that in other cases Android allows pointer id
+ * to go up to MAX_POINTERS_ID.
+ */
+ std::bitset<MAX_POINTERS> mActivePointers{};
+ bool isValidPointerId(int32_t pointerId, UinputAction uinputAction);
+ bool handleTouchDown(int32_t pointerId);
+ bool handleTouchUp(int32_t pointerId);
+};
+} // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index c4c8ffb..3f19371 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -74,9 +74,6 @@
name: "libbinder_common_defaults",
host_supported: true,
- // for vndbinder and binderRpcTest
- vendor_available: true,
-
srcs: [
"Binder.cpp",
"BpBinder.cpp",
@@ -194,13 +191,13 @@
"google-*",
"misc-*",
"performance*",
+ "-performance-move-const-arg", // b/273486801
"portability*",
],
}
cc_library_headers {
name: "trusty_mock_headers",
- vendor_available: true,
host_supported: true,
export_include_dirs: [
@@ -215,7 +212,6 @@
cc_defaults {
name: "trusty_mock_defaults",
- vendor_available: true,
host_supported: true,
header_libs: [
@@ -309,6 +305,8 @@
version_script: "libbinder.map",
+ // for vndbinder
+ vendor_available: true,
vndk: {
enabled: true,
},
@@ -467,7 +465,6 @@
cc_library_static {
name: "libbinder_tls_static",
defaults: ["libbinder_tls_defaults"],
- vendor_available: true,
visibility: [
":__subpackages__",
],
@@ -547,8 +544,10 @@
// Do not expand the visibility.
visibility: [
":__subpackages__",
- "//packages/modules/Virtualization:__subpackages__",
+ "//packages/modules/Virtualization/javalib/jni",
+ "//packages/modules/Virtualization/vm_payload",
"//device/google/cuttlefish/shared/minidroid:__subpackages__",
+ "//system/software_defined_vehicle:__subpackages__",
],
}
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index f954e74..bb17683 100644
--- a/libs/binder/OWNERS
+++ b/libs/binder/OWNERS
@@ -1,6 +1,4 @@
# Bug component: 32456
-ctate@google.com
-hackbod@google.com
maco@google.com
smoreland@google.com
tkjos@google.com
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 2e70304..ef58ed3 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -16,6 +16,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
#include <android-base/unique_fd.h>
#include <binder/RecordedTransaction.h>
#include <sys/mman.h>
@@ -126,8 +127,7 @@
t.mData.mInterfaceName = std::string(String8(interfaceName).string());
if (interfaceName.size() != t.mData.mInterfaceName.size()) {
LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
- "utf-8: "
- << interfaceName;
+ "utf-8.";
return std::nullopt;
}
@@ -176,13 +176,33 @@
RecordedTransaction t;
ChunkDescriptor chunk;
const long pageSize = sysconf(_SC_PAGE_SIZE);
+ struct stat fileStat;
+ if (fstat(fd.get(), &fileStat) != 0) {
+ LOG(ERROR) << "Unable to get file information";
+ return std::nullopt;
+ }
+
+ off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
do {
+ if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
+ LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
+ return std::nullopt;
+ }
transaction_checksum_t checksum = 0;
if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) {
LOG(ERROR) << "Failed to read chunk descriptor.";
return std::nullopt;
}
- off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+
+ fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
@@ -194,14 +214,24 @@
size_t chunkPayloadSize =
chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
+ if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
+ LOG(ERROR) << "Chunk payload exceeds remaining file size.";
+ return std::nullopt;
+ }
+
if (PADDING8(chunkPayloadSize) != 0) {
LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
return std::nullopt;
}
- transaction_checksum_t* payloadMap = reinterpret_cast<transaction_checksum_t*>(
- mmap(NULL, chunkPayloadSize + mmapPayloadStartOffset, PROT_READ, MAP_SHARED,
- fd.get(), mmapPageAlignedStart));
+ size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset;
+ void* mappedMemory =
+ mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart);
+ auto mmap_guard = android::base::make_scope_guard(
+ [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); });
+
+ transaction_checksum_t* payloadMap =
+ reinterpret_cast<transaction_checksum_t*>(mappedMemory);
payloadMap += mmapPayloadStartOffset /
sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
// page-alignment
@@ -218,7 +248,12 @@
LOG(ERROR) << "Checksum failed.";
return std::nullopt;
}
- lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+
+ fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
switch (chunk.chunkType) {
case HEADER_CHUNK: {
@@ -255,7 +290,7 @@
break;
default:
LOG(INFO) << "Unrecognized chunk.";
- continue;
+ break;
}
} while (chunk.chunkType != END_CHUNK);
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index ce6ef2b..fbad0f7 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -20,6 +20,7 @@
#include <dlfcn.h>
#include <inttypes.h>
+#include <netinet/tcp.h>
#include <poll.h>
#include <unistd.h>
@@ -90,16 +91,16 @@
return mMaxIncomingThreads;
}
-void RpcSession::setMaxOutgoingThreads(size_t threads) {
+void RpcSession::setMaxOutgoingConnections(size_t connections) {
RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mStartedSetup,
"Must set max outgoing threads before setting up connections");
- mMaxOutgoingThreads = threads;
+ mMaxOutgoingConnections = connections;
}
size_t RpcSession::getMaxOutgoingThreads() {
RpcMutexLockGuard _l(mMutex);
- return mMaxOutgoingThreads;
+ return mMaxOutgoingConnections;
}
bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) {
@@ -558,11 +559,11 @@
return status;
}
- size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
- ALOGI_IF(outgoingThreads != numThreadsAvailable,
+ size_t outgoingConnections = std::min(numThreadsAvailable, mMaxOutgoingConnections);
+ ALOGI_IF(outgoingConnections != numThreadsAvailable,
"Server hints client to start %zu outgoing threads, but client will only start %zu "
"because it is preconfigured to start at most %zu outgoing threads.",
- numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads);
+ numThreadsAvailable, outgoingConnections, mMaxOutgoingConnections);
// TODO(b/189955605): we should add additional sessions dynamically
// instead of all at once - the other side should be responsible for setting
@@ -571,10 +572,10 @@
// any requests at all.
// we've already setup one client
- LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu "
- "incoming threads",
- outgoingThreads, numThreadsAvailable, mMaxIncomingThreads);
- for (size_t i = 0; i + 1 < outgoingThreads; i++) {
+ LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing connections (server max: "
+ "%zu) and %zu incoming threads",
+ outgoingConnections, numThreadsAvailable, mMaxIncomingThreads);
+ for (size_t i = 0; i + 1 < outgoingConnections; i++) {
if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
}
@@ -608,6 +609,18 @@
return -savedErrno;
}
+ if (addr.addr()->sa_family == AF_INET || addr.addr()->sa_family == AF_INET6) {
+ int noDelay = 1;
+ int result =
+ setsockopt(serverFd.get(), IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay));
+ if (result < 0) {
+ int savedErrno = errno;
+ ALOGE("Could not set TCP_NODELAY on %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return -savedErrno;
+ }
+ }
+
RpcTransportFd transportFd(std::move(serverFd));
if (0 != TEMP_FAILURE_RETRY(connect(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
@@ -932,7 +945,8 @@
(session->server()
? "This is a server session, so see RpcSession::setMaxIncomingThreads "
"for the corresponding client"
- : "This is a client session, so see RpcSession::setMaxOutgoingThreads "
+ : "This is a client session, so see "
+ "RpcSession::setMaxOutgoingConnections "
"for this client or RpcServer::setMaxThreads for the corresponding "
"server"));
return WOULD_BLOCK;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index b27f102..ed3ce24 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -262,8 +262,10 @@
}
void RpcState::clear() {
- RpcMutexUniqueLock _l(mNodeMutex);
+ return clear(RpcMutexUniqueLock(mNodeMutex));
+}
+void RpcState::clear(RpcMutexUniqueLock nodeLock) {
if (mTerminated) {
LOG_ALWAYS_FATAL_IF(!mNodeForAddress.empty(),
"New state should be impossible after terminating!");
@@ -292,7 +294,7 @@
auto temp = std::move(mNodeForAddress);
mNodeForAddress.clear(); // RpcState isn't reusable, but for future/explicit
- _l.unlock();
+ nodeLock.unlock();
temp.clear(); // explicit
}
@@ -557,13 +559,12 @@
.parcelDataSize = static_cast<uint32_t>(data.dataSize()),
};
- constexpr size_t kWaitMaxUs = 1000000;
- constexpr size_t kWaitLogUs = 10000;
- size_t waitUs = 0;
-
// Oneway calls have no sync point, so if many are sent before, whether this
// is a twoway or oneway transaction, they may have filled up the socket.
// So, make sure we drain them before polling
+ constexpr size_t kWaitMaxUs = 1000000;
+ constexpr size_t kWaitLogUs = 10000;
+ size_t waitUs = 0;
iovec iovs[]{
{&command, sizeof(RpcWireHeader)},
@@ -591,8 +592,9 @@
},
rpcFields->mFds.get());
status != OK) {
- // TODO(b/167966510): need to undo onBinderLeaving - we know the
- // refcount isn't successfully transferred.
+ // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate
+ // errors here, then we may need to undo the binder-sent counts for the transaction as
+ // well as for the binder objects in the Parcel
return status;
}
@@ -704,7 +706,7 @@
};
{
- RpcMutexLockGuard _l(mNodeMutex);
+ RpcMutexUniqueLock _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(addr);
LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
@@ -720,8 +722,9 @@
body.amount = it->second.timesRecd - target;
it->second.timesRecd = target;
- LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(it),
+ LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(session, std::move(_l), it),
"Bad state. RpcState shouldn't own received binder");
+ // LOCK ALREADY RELEASED
}
RpcWireHeader cmd = {
@@ -1036,8 +1039,8 @@
return DEAD_OBJECT;
}
- if (it->second.asyncTodo.size() == 0) return OK;
- if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+ if (it->second.asyncTodo.size() != 0 &&
+ it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64,
it->second.asyncNumber, addr);
@@ -1164,8 +1167,8 @@
it->second.timesSent);
it->second.timesSent -= body.amount;
- sp<IBinder> tempHold = tryEraseNode(it);
- _l.unlock();
+ sp<IBinder> tempHold = tryEraseNode(session, std::move(_l), it);
+ // LOCK ALREADY RELEASED
tempHold = nullptr; // destructor may make binder calls on this session
return OK;
@@ -1229,7 +1232,10 @@
return OK;
}
-sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) {
+sp<IBinder> RpcState::tryEraseNode(const sp<RpcSession>& session, RpcMutexUniqueLock nodeLock,
+ std::map<uint64_t, BinderNode>::iterator& it) {
+ bool shouldShutdown = false;
+
sp<IBinder> ref;
if (it->second.timesSent == 0) {
@@ -1239,9 +1245,27 @@
LOG_ALWAYS_FATAL_IF(!it->second.asyncTodo.empty(),
"Can't delete binder w/ pending async transactions");
mNodeForAddress.erase(it);
+
+ if (mNodeForAddress.size() == 0) {
+ shouldShutdown = true;
+ }
}
}
+ // If we shutdown, prevent RpcState from being re-used. This prevents another
+ // thread from getting the root object again.
+ if (shouldShutdown) {
+ clear(std::move(nodeLock));
+ } else {
+ nodeLock.unlock(); // explicit
+ }
+ // LOCK IS RELEASED
+
+ if (shouldShutdown) {
+ ALOGI("RpcState has no binders left, so triggering shutdown...");
+ (void)session->shutdownAndWait(false);
+ }
+
return ref;
}
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index ac86585..0e23ea7 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -168,6 +168,7 @@
void clear();
private:
+ void clear(RpcMutexUniqueLock nodeLock);
void dumpLocked();
// Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
@@ -268,11 +269,20 @@
std::string toString() const;
};
- // checks if there is any reference left to a node and erases it. If erase
- // happens, and there is a strong reference to the binder kept by
- // binderNode, this returns that strong reference, so that it can be
- // dropped after any locks are removed.
- sp<IBinder> tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it);
+ // Checks if there is any reference left to a node and erases it. If this
+ // is the last node, shuts down the session.
+ //
+ // Node lock is passed here for convenience, so that we can release it
+ // and terminate the session, but we could leave it up to the caller
+ // by returning a continuation if we needed to erase multiple specific
+ // nodes. It may be tempting to allow the client to keep on holding the
+ // lock and instead just return whether or not we should shutdown, but
+ // this introduces the posssibility that another thread calls
+ // getRootBinder and thinks it is valid, rather than immediately getting
+ // an error.
+ sp<IBinder> tryEraseNode(const sp<RpcSession>& session, RpcMutexUniqueLock nodeLock,
+ std::map<uint64_t, BinderNode>::iterator& it);
+
// true - success
// false - session shutdown, halt
[[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node);
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 194254a..2b67f03 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -159,8 +159,8 @@
LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
auto rpcSession = RpcSession::make();
- if (options.maxOutgoingThreads.has_value()) {
- rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads);
+ if (options.maxOutgoingConnections.has_value()) {
+ rpcSession->setMaxOutgoingConnections(*options.maxOutgoingConnections);
}
if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort());
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 1488400..07b38d7 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -119,5 +119,10 @@
{
"name": "memunreachable_binder_test"
}
+ ],
+ "imports": [
+ {
+ "path": "packages/modules/Virtualization"
+ }
]
}
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index c78f870..55167a7 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -224,12 +224,12 @@
// }
// Resources are cleaned up when the object is destroyed.
//
-// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated.
-// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked
-// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads.
-// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|.
+// For each returned binder object, at most |maxOutgoingConnections| outgoing connections are
+// instantiated, depending on how many the service on the device is configured with.
+// Hence, only |maxOutgoingConnections| calls can be made simultaneously.
+// See also RpcSession::setMaxOutgoingConnections.
struct RpcDelegateServiceManagerOptions {
- std::optional<size_t> maxOutgoingThreads;
+ std::optional<size_t> maxOutgoingConnections;
};
sp<IServiceManager> createRpcDelegateServiceManager(
const RpcDelegateServiceManagerOptions& options);
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 25193a3..1001b64 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -119,7 +119,10 @@
[[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
/**
- * This must be called before adding a client session.
+ * This must be called before adding a client session. This corresponds
+ * to the number of incoming connections to RpcSession objects in the
+ * server, which will correspond to the number of outgoing connections
+ * in client RpcSession objects.
*
* If this is not specified, this will be a single-threaded server.
*
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 40faf2c..a323feb 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -51,11 +51,12 @@
* This represents a session (group of connections) between a client
* and a server. Multiple connections are needed for multiple parallel "binder"
* calls which may also have nested calls.
+ *
+ * Once a binder exists in the session, if all references to all binders are dropped,
+ * the session shuts down.
*/
class RpcSession final : public virtual RefBase {
public:
- static constexpr size_t kDefaultMaxOutgoingThreads = 10;
-
// Create an RpcSession with default configuration (raw sockets).
static sp<RpcSession> make();
@@ -67,26 +68,30 @@
/**
* Set the maximum number of incoming threads allowed to be made (for things like callbacks).
* By default, this is 0. This must be called before setting up this connection as a client.
- * Server sessions will inherits this value from RpcServer.
+ * Server sessions will inherits this value from RpcServer. Each thread will serve a
+ * connection to the remote RpcSession.
*
* If this is called, 'shutdown' on this session must also be called.
* Otherwise, a threadpool will leak.
*
- * TODO(b/189955605): start these dynamically
+ * TODO(b/189955605): start these lazily - currently all are started
*/
void setMaxIncomingThreads(size_t threads);
size_t getMaxIncomingThreads();
/**
- * Set the maximum number of outgoing threads allowed to be made.
- * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this
- * connection as a client.
+ * Set the maximum number of outgoing connections allowed to be made.
+ * By default, this is |kDefaultMaxOutgoingConnections|. This must be called before setting up
+ * this connection as a client.
*
- * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession
- * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where
- * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|.
+ * For an RpcSession client, if you are connecting to a server which starts N threads,
+ * then this must be set to >= N. If you set the maximum number of outgoing connections
+ * to 1, but the server requests 10, then it would be considered an error. If you set a
+ * maximum number of connections to 10, and the server requests 1, then only 1 will be
+ * created. This API is used to limit the amount of resources a server can request you
+ * create.
*/
- void setMaxOutgoingThreads(size_t threads);
+ void setMaxOutgoingConnections(size_t connections);
size_t getMaxOutgoingThreads();
/**
@@ -219,6 +224,8 @@
friend RpcState;
explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
+ static constexpr size_t kDefaultMaxOutgoingConnections = 10;
+
// internal version of setProtocolVersion that
// optionally skips the mStartedSetup check
[[nodiscard]] bool setProtocolVersionInternal(uint32_t version, bool checkStarted);
@@ -368,7 +375,7 @@
bool mStartedSetup = false;
size_t mMaxIncomingThreads = 0;
- size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
+ size_t mMaxOutgoingConnections = kDefaultMaxOutgoingConnections;
std::optional<uint32_t> mProtocolVersion;
FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE;
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 42d226b..a157792 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -126,11 +126,11 @@
void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
ARpcSession_FileDescriptorTransportMode mode);
-// Sets the maximum number of incoming threads.
+// Sets the maximum number of incoming threads, to service connections.
void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads);
-// Sets the maximum number of outgoing threads.
-void ARpcSession_setMaxOutgoingThreads(ARpcSession* session, size_t threads);
+// Sets the maximum number of outgoing connections.
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* session, size_t connections);
// Decrements the refcount of the underlying RpcSession object.
void ARpcSession_free(ARpcSession* session);
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index daff8c1..a167f23 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -265,8 +265,8 @@
session->setMaxIncomingThreads(threads);
}
-void ARpcSession_setMaxOutgoingThreads(ARpcSession* handle, size_t threads) {
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* handle, size_t connections) {
auto session = handleToStrongPointer<RpcSession>(handle);
- session->setMaxOutgoingThreads(threads);
+ session->setMaxOutgoingConnections(connections);
}
}
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index f68612c..d833b83 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,11 +26,11 @@
#pragma once
+#include <android/binder_status.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/cdefs.h>
-
-#include <android/binder_status.h>
+#include <uchar.h>
struct AIBinder;
typedef struct AIBinder AIBinder;
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 86d5ed2..43159d8 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -22,6 +22,16 @@
__BEGIN_DECLS
+enum AServiceManager_AddServiceFlag : uint32_t {
+ /**
+ * This allows processes with AID_ISOLATED to get the binder of the service added.
+ *
+ * Services with methods that perform file IO, web socket creation or ways to egress data must
+ * not be added with this flag for privacy concerns.
+ */
+ ADD_SERVICE_ALLOW_ISOLATED = 1,
+};
+
/**
* This registers the service with the default service manager under this instance name. This does
* not take ownership of binder.
@@ -46,12 +56,13 @@
*
* \param binder object to register globally with the service manager.
* \param instance identifier of the service. This will be used to lookup the service.
- * \param allowIsolated allows if this service can be isolated.
+ * \param flag an AServiceManager_AddServiceFlag enum to denote how the service should be added.
*
* \return EX_NONE on success.
*/
-__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServiceWithAllowIsolated(
- AIBinder* binder, const char* instance, bool allowIsolated) __INTRODUCED_IN(34);
+__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServiceWithFlag(
+ AIBinder* binder, const char* instance, const AServiceManager_AddServiceFlag flag)
+ __INTRODUCED_IN(34);
/**
* Gets a binder object with this specific instance name. Will return nullptr immediately if the
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index ffcad55..3fbe90d 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -32,7 +32,7 @@
* 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.
*/
-void ABinderProcess_startThreadPool();
+void ABinderProcess_startThreadPool(void);
/**
* 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
@@ -48,7 +48,7 @@
* you should use this in a library to abort if the threadpool is not started.
* Programs should configure binder threadpools once at the beginning.
*/
-bool ABinderProcess_isThreadPoolStarted();
+bool ABinderProcess_isThreadPoolStarted(void);
/**
* This adds the current thread to the threadpool. This may cause the threadpool to exceed the
* maximum size.
@@ -56,7 +56,7 @@
* 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.
*/
-void ABinderProcess_joinThreadPool();
+void ABinderProcess_joinThreadPool(void);
/**
* This gives you an fd to wait on. Whenever data is available on the fd,
@@ -79,6 +79,6 @@
*
* \return STATUS_OK on success
*/
-__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands() __INTRODUCED_IN(31);
+__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands(void) __INTRODUCED_IN(31);
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 5f2f617..1078fb2 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -158,12 +158,12 @@
AServiceManager_getUpdatableApexName; # systemapi
AServiceManager_registerForServiceNotifications; # systemapi llndk
AServiceManager_NotificationRegistration_delete; # systemapi llndk
+ AServiceManager_addServiceWithFlag; # systemapi llndk
};
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
- AServiceManager_addServiceWithAllowIsolated;
extern "C++" {
AIBinder_fromPlatformBinder*;
AIBinder_toPlatformBinder*;
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index bc6610e..0fea57b 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -24,17 +24,17 @@
using ::android::IPCThreadState;
using ::android::ProcessState;
-void ABinderProcess_startThreadPool() {
+void ABinderProcess_startThreadPool(void) {
ProcessState::self()->startThreadPool();
ProcessState::self()->giveThreadPoolName();
}
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) {
return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0;
}
-bool ABinderProcess_isThreadPoolStarted() {
+bool ABinderProcess_isThreadPoolStarted(void) {
return ProcessState::self()->isThreadPoolStarted();
}
-void ABinderProcess_joinThreadPool() {
+void ABinderProcess_joinThreadPool(void) {
IPCThreadState::self()->joinThreadPool();
}
@@ -42,6 +42,6 @@
return IPCThreadState::self()->setupPolling(fd);
}
-binder_status_t ABinderProcess_handlePolledCommands() {
+binder_status_t ABinderProcess_handlePolledCommands(void) {
return IPCThreadState::self()->handlePolledCommands();
}
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 2763ddb..84da459 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -42,14 +42,15 @@
return PruneException(exception);
}
-binder_exception_t AServiceManager_addServiceWithAllowIsolated(AIBinder* binder,
- const char* instance,
- bool allowIsolated) {
+binder_exception_t AServiceManager_addServiceWithFlag(AIBinder* binder, const char* instance,
+ const AServiceManager_AddServiceFlag flag) {
if (binder == nullptr || instance == nullptr) {
return EX_ILLEGAL_ARGUMENT;
}
sp<IServiceManager> sm = defaultServiceManager();
+
+ bool allowIsolated = flag & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED;
status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated);
return PruneException(exception);
}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 5b2532a..882f1d6 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -52,7 +52,7 @@
constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService";
constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
-constexpr unsigned int kShutdownWaitTime = 10;
+constexpr unsigned int kShutdownWaitTime = 11;
constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715;
class MyTestFoo : public IFoo {
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index afd414a..d36ebac 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -21,6 +21,7 @@
],
host_supported: true,
vendor_available: true,
+ product_available: true,
target: {
darwin: {
enabled: false,
@@ -72,6 +73,7 @@
],
host_supported: true,
vendor_available: true,
+ product_available: true,
target: {
darwin: {
enabled: false,
@@ -129,6 +131,7 @@
],
host_supported: true,
vendor_available: true,
+ product_available: true,
// Currently necessary for host builds
// TODO(b/31559095): bionic on host should define this
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index da9797b..0067a20 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -26,6 +26,7 @@
visibility: [
"//device/google/cuttlefish/shared/minidroid/sample",
"//packages/modules/Virtualization:__subpackages__",
+ "//system/software_defined_vehicle:__subpackages__",
],
apex_available: [
"//apex_available:platform",
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 0b517cf..28c5390 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -75,11 +75,14 @@
};
}
- /// Sets the maximum number of outgoing threads.
- pub fn set_max_outgoing_threads(&self, threads: usize) {
+ /// 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.
unsafe {
- binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingThreads(self.as_ptr(), threads)
+ binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections(
+ self.as_ptr(),
+ connections,
+ )
};
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 976f54d..d0e35de 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -100,22 +100,17 @@
/// An interface can promise to be a stable vendor interface ([`Vintf`]), or
/// makes no stability guarantees ([`Local`]). [`Local`] is
/// currently the default stability.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Stability {
/// Default stability, visible to other modules in the same compilation
/// context (e.g. modules on system.img)
+ #[default]
Local,
/// A Vendor Interface Object, which promises to be stable
Vintf,
}
-impl Default for Stability {
- fn default() -> Self {
- Stability::Local
- }
-}
-
impl From<Stability> for i32 {
fn from(stability: Stability) -> i32 {
use Stability::*;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 6f686fb..5557168 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -209,8 +209,8 @@
}
/// Mark this binder object with local stability, which is vendor if we are
- /// building for the VNDK and system otherwise.
- #[cfg(any(vendor_ndk, android_vndk))]
+ /// building for android_vendor and system otherwise.
+ #[cfg(android_vendor)]
fn mark_local_stability(&mut self) {
unsafe {
// Safety: Self always contains a valid `AIBinder` pointer, so
@@ -220,8 +220,8 @@
}
/// Mark this binder object with local stability, which is vendor if we are
- /// building for the VNDK and system otherwise.
- #[cfg(not(any(vendor_ndk, android_vndk)))]
+ /// building for android_vendor and system otherwise.
+ #[cfg(not(android_vendor))]
fn mark_local_stability(&mut self) {
unsafe {
// Safety: Self always contains a valid `AIBinder` pointer, so
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 6f4c375..4b658fc 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -771,7 +771,13 @@
#[macro_export]
macro_rules! impl_serialize_for_parcelable {
($parcelable:ident) => {
- impl $crate::binder_impl::Serialize for $parcelable {
+ $crate::impl_serialize_for_parcelable!($parcelable < >);
+ };
+ ($parcelable:ident < $( $param:ident ),* , >) => {
+ $crate::impl_serialize_for_parcelable!($parcelable < $($param),* >);
+ };
+ ($parcelable:ident < $( $param:ident ),* > ) => {
+ impl < $($param),* > $crate::binder_impl::Serialize for $parcelable < $($param),* > {
fn serialize(
&self,
parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
@@ -780,9 +786,9 @@
}
}
- impl $crate::binder_impl::SerializeArray for $parcelable {}
+ impl < $($param),* > $crate::binder_impl::SerializeArray for $parcelable < $($param),* > {}
- impl $crate::binder_impl::SerializeOption for $parcelable {
+ impl < $($param),* > $crate::binder_impl::SerializeOption for $parcelable < $($param),* > {
fn serialize_option(
this: Option<&Self>,
parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
@@ -808,7 +814,13 @@
#[macro_export]
macro_rules! impl_deserialize_for_parcelable {
($parcelable:ident) => {
- impl $crate::binder_impl::Deserialize for $parcelable {
+ $crate::impl_deserialize_for_parcelable!($parcelable < >);
+ };
+ ($parcelable:ident < $( $param:ident ),* , >) => {
+ $crate::impl_deserialize_for_parcelable!($parcelable < $($param),* >);
+ };
+ ($parcelable:ident < $( $param:ident ),* > ) => {
+ impl < $($param: Default),* > $crate::binder_impl::Deserialize for $parcelable < $($param),* > {
fn deserialize(
parcel: &$crate::binder_impl::BorrowedParcel<'_>,
) -> std::result::Result<Self, $crate::StatusCode> {
@@ -830,9 +842,9 @@
}
}
- impl $crate::binder_impl::DeserializeArray for $parcelable {}
+ impl < $($param: Default),* > $crate::binder_impl::DeserializeArray for $parcelable < $($param),* > {}
- impl $crate::binder_impl::DeserializeOption for $parcelable {
+ impl < $($param: Default),* > $crate::binder_impl::DeserializeOption for $parcelable < $($param),* > {
fn deserialize_option(
parcel: &$crate::binder_impl::BorrowedParcel<'_>,
) -> std::result::Result<Option<Self>, $crate::StatusCode> {
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index e609987..0f0d64a 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -138,7 +138,6 @@
aidl_interface {
name: "binderRpcTestIface",
- vendor_available: true,
host_supported: true,
unstable: true,
srcs: [
@@ -159,7 +158,6 @@
cc_library_static {
name: "libbinder_tls_test_utils",
- vendor_available: true,
host_supported: true,
target: {
darwin: {
@@ -213,7 +211,6 @@
defaults: [
"binderRpcTest_common_defaults",
],
- vendor_available: true,
gtest: false,
auto_gen_config: false,
srcs: [
@@ -224,18 +221,10 @@
cc_defaults {
name: "binderRpcTest_defaults",
- vendor_available: true,
target: {
android: {
test_suites: ["vts"],
},
-
- vendor: {
- shared_libs: [
- "libbinder_trusty",
- "libtrusty",
- ],
- },
},
defaults: [
"binderRpcTest_common_defaults",
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index a3ed571..1164767 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -80,4 +80,8 @@
// get queued.
oneway void blockingSendFdOneway(in ParcelFileDescriptor fd);
ParcelFileDescriptor blockingRecvFd();
+
+ // Same as blockingSendFdOneway, but with integers.
+ oneway void blockingSendIntOneway(int n);
+ int blockingRecvInt();
}
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 464da60..77a5fa8 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -66,7 +66,7 @@
void initHostRpcServiceManagerOnce() {
static std::once_flag gSmOnce;
std::call_once(gSmOnce, [] {
- setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
});
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 955c650..8974ad7 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -507,7 +507,13 @@
}
EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
- EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+
+ // b/268232063 - succeeds ~0.08% of the time
+ {
+ auto ret = IPCThreadState::self()->freeze(pid, true, 0);
+ EXPECT_TRUE(ret == -EAGAIN || ret == OK);
+ }
+
EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
@@ -1370,7 +1376,7 @@
}));
}
- data.writeInt32(100);
+ data.writeInt32(500);
// Give a chance for all threads to be used
EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR);
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 52ba9b0..5939273 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -102,9 +102,11 @@
}
static sp<RpcSession> gSession = RpcSession::make();
+static sp<IBinder> gRpcBinder;
// Certificate validation happens during handshake and does not affect the result of benchmarks.
// Skip certificate validation to simplify the setup process.
static sp<RpcSession> gSessionTls = RpcSession::make(makeFactoryTls());
+static sp<IBinder> gRpcTlsBinder;
#ifdef __BIONIC__
static const String16 kKernelBinderInstance = String16(u"binderRpcBenchmark-control");
static sp<IBinder> gKernelBinder;
@@ -118,9 +120,9 @@
return gKernelBinder;
#endif
case RPC:
- return gSession->getRootObject();
+ return gRpcBinder;
case RPC_TLS:
- return gSessionTls->getRootObject();
+ return gRpcTlsBinder;
default:
LOG(FATAL) << "Unknown transport value: " << transport;
return nullptr;
@@ -254,11 +256,13 @@
(void)unlink(addr.c_str());
forkRpcServer(addr.c_str(), RpcServer::make(RpcTransportCtxFactoryRaw::make()));
setupClient(gSession, addr.c_str());
+ gRpcBinder = gSession->getRootObject();
std::string tlsAddr = tmp + "/binderRpcTlsBenchmark";
(void)unlink(tlsAddr.c_str());
forkRpcServer(tlsAddr.c_str(), RpcServer::make(makeFactoryTls()));
setupClient(gSessionTls, tlsAddr.c_str());
+ gRpcTlsBinder = gSessionTls->getRootObject();
::benchmark::RunSpecifiedBenchmarks();
return 0;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 84c93dd..d563cfc 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -129,7 +129,7 @@
static std::string allocateSocketAddress() {
static size_t id = 0;
std::string temp = getenv("TMPDIR") ?: "/tmp";
- auto ret = temp + "/binderRpcTest_" + std::to_string(id++);
+ auto ret = temp + "/binderRpcTest_" + std::to_string(getpid()) + "_" + std::to_string(id++);
unlink(ret.c_str());
return ret;
};
@@ -163,7 +163,8 @@
session.root = nullptr;
}
- for (auto& info : sessions) {
+ for (size_t sessionNum = 0; sessionNum < sessions.size(); sessionNum++) {
+ auto& info = sessions.at(sessionNum);
sp<RpcSession>& session = info.session;
EXPECT_NE(nullptr, session);
@@ -179,6 +180,7 @@
for (size_t i = 0; i < 3; i++) {
sp<RpcSession> strongSession = weakSession.promote();
EXPECT_EQ(nullptr, strongSession)
+ << "For session " << sessionNum << ". "
<< (debugBacktrace(host.getPid()), debugBacktrace(getpid()),
"Leaked sess: ")
<< strongSession->getStrongCount() << " checked time " << i;
@@ -231,17 +233,8 @@
return std::move(sockClient);
}
-std::string BinderRpc::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) {
- ret += "_single_threaded";
- }
- if (noKernel) {
- ret += "_no_kernel";
- }
- return ret;
+std::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSecurity) {
+ return newTlsFactory(rpcSecurity);
}
// This creates a new process serving an interface on a certain number of
@@ -250,6 +243,10 @@
const BinderRpcOptions& options) {
CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
+ if (options.numIncomingConnectionsBySession.size() != 0) {
+ 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());
@@ -317,7 +314,7 @@
LOG_ALWAYS_FATAL("TIPC socket type only supported on vendor");
#endif
} else {
- factory = newFactory(rpcSecurity, certVerifier);
+ factory = newTlsFactory(rpcSecurity, certVerifier);
}
sessions.emplace_back(RpcSession::make(std::move(factory)));
}
@@ -347,10 +344,16 @@
status_t status;
- for (const auto& session : sessions) {
+ for (size_t i = 0; i < sessions.size(); i++) {
+ const auto& session = sessions.at(i);
+
+ size_t numIncoming = options.numIncomingConnectionsBySession.size() > 0
+ ? options.numIncomingConnectionsBySession.at(i)
+ : 0;
+
CHECK(session->setProtocolVersion(clientVersion));
- session->setMaxIncomingThreads(options.numIncomingConnections);
- session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setMaxIncomingThreads(numIncoming);
+ session->setMaxOutgoingConnections(options.numOutgoingConnections);
session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
switch (socketType) {
@@ -435,8 +438,7 @@
for (auto& t : ts) t.join();
}
-static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
- size_t sleepMs = 500) {
+static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, size_t sleepMs) {
size_t epochMsBefore = epochMillis();
std::vector<std::thread> ts;
@@ -462,7 +464,7 @@
constexpr size_t kNumThreads = 10;
constexpr size_t kNumCalls = kNumThreads + 3;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
}
TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
@@ -475,7 +477,7 @@
constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
auto proc = createRpcTestSocketServerProcess(
{.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
}
TEST_P(BinderRpc, ThreadingStressTest) {
@@ -483,9 +485,9 @@
GTEST_SKIP() << "This test requires multiple threads";
}
- constexpr size_t kNumClientThreads = 10;
- constexpr size_t kNumServerThreads = 10;
- constexpr size_t kNumCalls = 100;
+ constexpr size_t kNumClientThreads = 5;
+ constexpr size_t kNumServerThreads = 5;
+ constexpr size_t kNumCalls = 50;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
@@ -544,6 +546,8 @@
GTEST_SKIP() << "This test requires multiple threads";
}
+ constexpr size_t kNumServerThreads = 3;
+
// This test forces a oneway transaction to be queued by issuing two
// `blockingSendFdOneway` calls, then drains the queue by issuing two
// `blockingRecvFd` calls.
@@ -552,7 +556,7 @@
// https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY
auto proc = createRpcTestSocketServerProcess({
- .numThreads = 3,
+ .numThreads = kNumServerThreads,
.clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
.serverSupportedFileDescriptorTransportModes =
{RpcSession::FileDescriptorTransportMode::UNIX},
@@ -573,6 +577,8 @@
EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB));
CHECK(android::base::ReadFdToString(fdB.get(), &result));
EXPECT_EQ(result, "b");
+
+ saturateThreadPool(kNumServerThreads, proc.rootIface);
}
TEST_P(BinderRpc, OnewayCallQueueing) {
@@ -580,30 +586,22 @@
GTEST_SKIP() << "This test requires multiple threads";
}
- constexpr size_t kNumSleeps = 10;
+ constexpr size_t kNumQueued = 10;
constexpr size_t kNumExtraServerThreads = 4;
- constexpr size_t kSleepMs = 50;
// make sure calls to the same object happen on the same thread
auto proc = createRpcTestSocketServerProcess({.numThreads = 1 + kNumExtraServerThreads});
- EXPECT_OK(proc.rootIface->lock());
-
- size_t epochMsBefore = epochMillis();
-
- // all these *Async commands should be queued on the server sequentially,
+ // all these *Oneway commands should be queued on the server sequentially,
// even though there are multiple threads.
- for (size_t i = 0; i + 1 < kNumSleeps; i++) {
- proc.rootIface->sleepMsAsync(kSleepMs);
+ for (size_t i = 0; i + 1 < kNumQueued; i++) {
+ proc.rootIface->blockingSendIntOneway(i);
}
- EXPECT_OK(proc.rootIface->unlockInMsAsync(kSleepMs));
-
- // this can only return once the final async call has unlocked
- EXPECT_OK(proc.rootIface->lockUnlock());
-
- size_t epochMsAfter = epochMillis();
-
- EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+ for (size_t i = 0; i + 1 < kNumQueued; i++) {
+ int n;
+ proc.rootIface->blockingRecvInt(&n);
+ EXPECT_EQ(n, i);
+ }
saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
}
@@ -652,6 +650,32 @@
proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
}
+TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) {
+ if (clientOrServerSingleThreaded()) {
+ GTEST_SKIP() << "This test requires multiple threads";
+ }
+
+ // 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});
+
+ wp<RpcSession> session = proc.proc->sessions.at(1).session;
+
+ // remove all references to the second session
+ proc.proc->sessions.at(1).root = nullptr;
+ proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
+
+ // TODO(b/271830568) more efficient way to wait for other incoming threadpool
+ // to drain commands.
+ for (size_t i = 0; i < 100; i++) {
+ usleep(10 * 1000);
+ if (session.promote() == nullptr) break;
+ }
+
+ EXPECT_EQ(nullptr, session.promote());
+}
+
TEST_P(BinderRpc, SingleDeathRecipient) {
if (clientOrServerSingleThreaded()) {
GTEST_SKIP() << "This test requires multiple threads";
@@ -669,7 +693,7 @@
// Death recipient needs to have an incoming connection to be called
auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
+ {.numThreads = 1, .numSessions = 1, .numIncomingConnectionsBySession = {1}});
auto dr = sp<MyDeathRec>::make();
ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
@@ -682,6 +706,10 @@
ASSERT_TRUE(dr->mCv.wait_for(lock, 100ms, [&]() { return dr->dead; }));
// need to wait for the session to shutdown so we don't "Leak session"
+ // can't do this before checking the death recipient by calling
+ // forceShutdown earlier, because shutdownAndWait will also trigger
+ // a death recipient, but if we had a way to wait for the service
+ // to gracefully shutdown, we could use that here.
EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
proc.expectAlreadyShutdown = true;
}
@@ -703,7 +731,7 @@
// Death recipient needs to have an incoming connection to be called
auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
+ {.numThreads = 1, .numSessions = 1, .numIncomingConnectionsBySession = {1}});
auto dr = sp<MyDeathRec>::make();
EXPECT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
@@ -736,8 +764,7 @@
void binderDied(const wp<IBinder>& /* who */) override {}
};
- auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 0});
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 1});
auto dr = sp<MyDeathRec>::make();
EXPECT_EQ(INVALID_OPERATION, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
@@ -756,19 +783,13 @@
// Death recipient needs to have an incoming connection to be called
auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1});
+ {.numThreads = 1, .numSessions = 1, .numIncomingConnectionsBySession = {1}});
auto dr = sp<MyDeathRec>::make();
ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0));
ASSERT_EQ(OK, proc.rootBinder->unlinkToDeath(dr, (void*)1, 0, nullptr));
- if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
- EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
- }
-
- // need to wait for the session to shutdown so we don't "Leak session"
- EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
- proc.expectAlreadyShutdown = true;
+ proc.forceShutdown();
}
TEST_P(BinderRpc, Die) {
@@ -1223,7 +1244,7 @@
};
auto [isStrong1, isStrong2, rpcSecurity] = GetParam();
- auto server = RpcServer::make(newFactory(rpcSecurity));
+ auto server = RpcServer::make(newTlsFactory(rpcSecurity));
auto binder1 = sp<BBinder>::make();
IBinder* binderRaw1 = binder1.get();
setRootObject(isStrong1)(server.get(), binder1);
@@ -1319,7 +1340,7 @@
class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> {
public:
static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) {
- return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" +
+ return std::string(newTlsFactory(std::get<0>(info.param))->toCString()) + "_serverV" +
std::to_string(std::get<1>(info.param));
}
};
@@ -1327,7 +1348,7 @@
TEST_P(BinderRpcServerOnly, SetExternalServerTest) {
base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
int sinkFd = sink.get();
- auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
+ auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
server->setProtocolVersion(std::get<1>(GetParam()));
ASSERT_FALSE(server->hasServer());
ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
@@ -1343,7 +1364,7 @@
}
auto addr = allocateSocketAddress();
- auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
+ auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
server->setProtocolVersion(std::get<1>(GetParam()));
ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
auto joinEnds = std::make_shared<OneOffSignal>();
@@ -1392,7 +1413,7 @@
const Param& param,
std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
- auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
+ auto rpcServer = RpcServer::make(newTlsFactory(rpcSecurity));
rpcServer->setProtocolVersion(serverVersion);
switch (socketType) {
case SocketType::PRECONNECTED: {
@@ -1471,7 +1492,7 @@
}
mFd = rpcServer->releaseServer();
if (!mFd.fd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
- mCtx = newFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx();
+ mCtx = newTlsFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx();
if (mCtx == nullptr) return AssertionFailure() << "newServerCtx";
mSetup = true;
return AssertionSuccess();
@@ -1576,7 +1597,7 @@
auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
(void)serverVersion;
mFdTrigger = FdTrigger::make();
- mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx();
+ mCtx = newTlsFactory(rpcSecurity, mCertVerifier)->newClientCtx();
if (mCtx == nullptr) return AssertionFailure() << "newClientCtx";
return AssertionSuccess();
}
@@ -1648,7 +1669,7 @@
using Client = RpcTransportTestUtils::Client;
static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param;
- auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString();
+ auto ret = PrintToString(socketType) + "_" + newTlsFactory(rpcSecurity)->toCString();
if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat);
ret += "_serverV" + std::to_string(serverVersion);
return ret;
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index a467ee3..c1364dd 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -126,7 +126,11 @@
struct BinderRpcOptions {
size_t numThreads = 1;
size_t numSessions = 1;
- size_t numIncomingConnections = 0;
+ // right now, this can be empty, or length numSessions, where each value
+ // represents the info for the corresponding session, but we should
+ // probably switch this to be a list of sessions options so that other
+ // options can all be specified per session
+ std::vector<size_t> numIncomingConnectionsBySession = {};
size_t numOutgoingConnections = SIZE_MAX;
RpcSession::FileDescriptorTransportMode clientFileDescriptorTransportMode =
RpcSession::FileDescriptorTransportMode::NONE;
@@ -170,7 +174,7 @@
return object;
}
-static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
+static inline std::unique_ptr<RpcTransportCtxFactory> newTlsFactory(
RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr,
std::unique_ptr<RpcAuth> auth = nullptr) {
switch (rpcSecurity) {
@@ -445,6 +449,12 @@
Status blockingRecvFd(android::os::ParcelFileDescriptor* /*fd*/) override {
return Status::fromStatusT(UNKNOWN_TRANSACTION);
}
+
+ Status blockingSendIntOneway(int /*n*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+
+ Status blockingRecvInt(int* /*n*/) override { return Status::fromStatusT(UNKNOWN_TRANSACTION); }
};
} // namespace android
diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
index c99d68a..6cde9f7 100644
--- a/libs/binder/tests/binderRpcTestFixture.h
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -64,6 +64,21 @@
// whether session should be invalidated by end of run
bool expectAlreadyShutdown = false;
+ // TODO(b/271830568): fix this in binderRpcTest, we always use the first session to cause the
+ // remote process to shutdown. Normally, when we shutdown, the default in the destructor is to
+ // check that there are no leaks and shutdown. However, when there are incoming threadpools,
+ // there will be a few extra binder threads there, so we can't shutdown the server. We should
+ // consider an alternative way of doing the test so that we don't need this, some ideas, such as
+ // program in understanding of incoming threadpool into the destructor so that (e.g.
+ // intelligently wait for sessions to shutdown now that they will do this)
+ void forceShutdown() {
+ if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
+ EXPECT_TRUE(proc->sessions.at(0).session->shutdownAndWait(true));
+ expectAlreadyShutdown = true;
+ }
+
BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
~BinderRpcTestProcessSession() {
if (!expectAlreadyShutdown) {
@@ -133,9 +148,26 @@
return ret;
}
- static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info);
+ 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) {
+ ret += "_single_threaded";
+ } else {
+ ret += "_multi_threaded";
+ }
+ if (noKernel) {
+ ret += "_no_kernel";
+ } else {
+ ret += "_with_kernel";
+ }
+ return ret;
+ }
protected:
+ static std::unique_ptr<RpcTransportCtxFactory> newFactory(RpcSecurity rpcSecurity);
+
std::unique_ptr<ProcessSession> createRpcTestSocketServerProcessEtc(
const BinderRpcOptions& options);
};
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 714f063..a9736d5 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -83,9 +83,23 @@
fd->reset(mFdChannel.read());
return Status::ok();
}
+
+ HandoffChannel<int> mIntChannel;
+
+ Status blockingSendIntOneway(int n) override {
+ mIntChannel.write(n);
+ return Status::ok();
+ }
+
+ Status blockingRecvInt(int* n) override {
+ *n = mIntChannel.read();
+ return Status::ok();
+ }
};
-int main(int argc, const char* argv[]) {
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+
LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc);
base::unique_fd writeEnd(atoi(argv[1]));
base::unique_fd readEnd(atoi(argv[2]));
@@ -102,7 +116,7 @@
}
auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
- sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
+ sp<RpcServer> server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier));
server->setProtocolVersion(serverConfig.serverVersion);
server->setMaxThreads(serverConfig.numThreads);
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index b3bb5eb..28be10d 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -39,27 +39,25 @@
void terminate() override { LOG_ALWAYS_FATAL("terminate() not supported"); }
};
-std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
- auto ret = PrintToString(type) + "_clientV" + std::to_string(clientVersion) + "_serverV" +
- std::to_string(serverVersion);
- if (singleThreaded) {
- ret += "_single_threaded";
+std::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSecurity) {
+ switch (rpcSecurity) {
+ case RpcSecurity::RAW:
+ return RpcTransportCtxFactoryTipcTrusty::make();
+ default:
+ LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
}
- if (noKernel) {
- ret += "_no_kernel";
- }
- return ret;
}
// This creates a new process serving an interface on a certain number of
// threads.
std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
const BinderRpcOptions& options) {
- LOG_ALWAYS_FATAL_IF(options.numIncomingConnections != 0,
- "Non-zero incoming connections %zu on Trusty",
- options.numIncomingConnections);
+ LOG_ALWAYS_FATAL_IF(std::any_of(options.numIncomingConnectionsBySession.begin(),
+ options.numIncomingConnectionsBySession.end(),
+ [](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());
@@ -67,11 +65,10 @@
status_t status;
for (size_t i = 0; i < options.numSessions; i++) {
- auto factory = android::RpcTransportCtxFactoryTipcTrusty::make();
- auto session = android::RpcSession::make(std::move(factory));
+ auto session = android::RpcSession::make(newFactory(rpcSecurity));
EXPECT_TRUE(session->setProtocolVersion(clientVersion));
- session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setMaxOutgoingConnections(options.numOutgoingConnections);
session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
status = session->setupPreconnectedClient({}, [&]() {
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 11a22b0..1f46010 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -463,7 +463,7 @@
auto proc = createRpcTestSocketServerProcess(
{.numThreads = 1,
.numSessions = 1,
- .numIncomingConnections = numIncomingConnections});
+ .numIncomingConnectionsBySession = {numIncomingConnections}});
auto cb = sp<MyBinderRpcCallback>::make();
if (callIsOneway) {
@@ -491,16 +491,7 @@
<< "callIsOneway: " << callIsOneway
<< " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
- // since we are severing the connection, we need to go ahead and
- // tell the server to shutdown and exit so that waitpid won't hang
- if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
- EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
- }
-
- // since this session has an incoming connection w/ a threadpool, we
- // need to manually shut it down
- EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
- proc.expectAlreadyShutdown = true;
+ proc.forceShutdown();
}
}
}
diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
index 8ea948c..a881582 100644
--- a/libs/binder/tests/unit_fuzzers/Android.bp
+++ b/libs/binder/tests/unit_fuzzers/Android.bp
@@ -104,3 +104,42 @@
defaults: ["binder_fuzz_defaults"],
srcs: ["MemoryDealerFuzz.cpp"],
}
+
+cc_fuzz {
+ name: "binder_recordedTransactionFileFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["RecordedTransactionFileFuzz.cpp"],
+ corpus: [
+ "recorded_transaction_corpus/*",
+ ],
+}
+
+cc_fuzz {
+ name: "binder_recordedTransactionFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["RecordedTransactionFuzz.cpp"],
+ target: {
+ android: {
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ "libbase",
+ "libbinder",
+ ],
+ static_libs: ["libbinder_random_parcel"],
+ },
+ host: {
+ static_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libbase",
+ "libbinder",
+ "libbinder_random_parcel",
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
new file mode 100644
index 0000000..e494366
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -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.
+ */
+
+#include <android-base/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <filesystem>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::FILE* intermediateFile = std::tmpfile();
+ fwrite(data, sizeof(uint8_t), size, intermediateFile);
+ rewind(intermediateFile);
+ int fileNumber = fileno(intermediateFile);
+
+ android::base::unique_fd fd(dup(fileNumber));
+
+ auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd);
+
+ std::fclose(intermediateFile);
+
+ if (transaction.has_value()) {
+ intermediateFile = std::tmpfile();
+
+ android::base::unique_fd fdForWriting(fileno(intermediateFile));
+ auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+ std::fclose(intermediateFile);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
new file mode 100644
index 0000000..943fb9f
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 <android-base/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <fuzzbinder/random_parcel.h>
+#include <filesystem>
+#include <string>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using android::fillRandomParcel;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+ android::String16 interfaceName =
+ android::String16(provider.ConsumeRandomLengthString().c_str());
+
+ uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ uint32_t flags = provider.ConsumeIntegral<uint32_t>();
+ time_t sec = provider.ConsumeIntegral<time_t>();
+ long nsec = provider.ConsumeIntegral<long>();
+ timespec timestamp = {.tv_sec = sec, .tv_nsec = nsec};
+ android::status_t transactionStatus = provider.ConsumeIntegral<android::status_t>();
+
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
+ provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+
+ // same options so that FDs and binders could be shared in both Parcels
+ android::RandomParcelOptions options;
+
+ android::Parcel p0, p1;
+ fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
+ fillRandomParcel(&p1, std::move(provider), &options);
+
+ auto transaction =
+ android::binder::debug::RecordedTransaction::fromDetails(interfaceName, code, flags,
+ timestamp, p0, p1,
+ transactionStatus);
+
+ if (transaction.has_value()) {
+ std::FILE* intermediateFile = std::tmpfile();
+ android::base::unique_fd fdForWriting(fileno(intermediateFile));
+ auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+ std::fclose(intermediateFile);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
new file mode 100644
index 0000000..79442078
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
Binary files differ
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
new file mode 100644
index 0000000..658addb
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
Binary files differ
diff --git a/libs/binderdebug/Android.bp b/libs/binderdebug/Android.bp
index 3eeaf3e..1454727 100644
--- a/libs/binderdebug/Android.bp
+++ b/libs/binderdebug/Android.bp
@@ -21,6 +21,17 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+cc_benchmark {
+ name: "binder_thread_stats",
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ "libbase",
+ ],
+ static_libs: ["libbinderdebug"],
+ srcs: ["stats.cpp"],
+}
+
cc_library {
name: "libbinderdebug",
vendor_available: true,
diff --git a/libs/binderdebug/stats.cpp b/libs/binderdebug/stats.cpp
new file mode 100644
index 0000000..9c26afa
--- /dev/null
+++ b/libs/binderdebug/stats.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 <android-base/logging.h>
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
+#include <binderdebug/BinderDebug.h>
+#include <utils/Errors.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+extern "C" int main() {
+ // ignore args - we only print csv
+
+ // we should use a csv library here for escaping, because
+ // the name is coming from another process
+ printf("name,binder_threads_in_use,binder_threads_started,client_count\n");
+
+ for (const String16& name : defaultServiceManager()->listServices()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(name);
+ if (binder == nullptr) {
+ fprintf(stderr, "%s is null", String8(name).c_str());
+ continue;
+ }
+
+ BpBinder* remote = binder->remoteBinder();
+ const auto handle = remote->getDebugBinderHandle();
+ CHECK(handle != std::nullopt);
+
+ pid_t pid;
+ CHECK_EQ(OK, binder->getDebugPid(&pid));
+
+ BinderPidInfo info;
+ CHECK_EQ(OK, getBinderPidInfo(BinderDebugContext::BINDER, pid, &info));
+
+ std::vector<pid_t> clientPids;
+ CHECK_EQ(OK,
+ getBinderClientPids(BinderDebugContext::BINDER, getpid(), pid, *handle,
+ &clientPids));
+
+ printf("%s,%" PRIu32 ",%" PRIu32 ",%zu\n", String8(name).c_str(), info.threadUsage,
+ info.threadCount, clientPids.size());
+ }
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 29924ff..96dcce1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -11,7 +11,7 @@
name: "fakeservicemanager_defaults",
host_supported: true,
srcs: [
- "ServiceManager.cpp",
+ "FakeServiceManager.cpp",
],
shared_libs: [
@@ -28,7 +28,7 @@
cc_library {
name: "libfakeservicemanager",
defaults: ["fakeservicemanager_defaults"],
- export_include_dirs: ["include/fakeservicemanager"],
+ export_include_dirs: ["include"],
}
cc_test_host {
@@ -38,5 +38,5 @@
"test_sm.cpp",
],
static_libs: ["libgmock"],
- local_include_dirs: ["include/fakeservicemanager"],
+ local_include_dirs: ["include"],
}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
similarity index 66%
rename from libs/fakeservicemanager/ServiceManager.cpp
rename to libs/fakeservicemanager/FakeServiceManager.cpp
index 1109ad8..3272bbc 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
namespace android {
-ServiceManager::ServiceManager() {}
+FakeServiceManager::FakeServiceManager() {}
-sp<IBinder> ServiceManager::getService( const String16& name) const {
+sp<IBinder> FakeServiceManager::getService( const String16& name) const {
// Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
return checkService(name);
}
-sp<IBinder> ServiceManager::checkService( const String16& name) const {
+sp<IBinder> FakeServiceManager::checkService( const String16& name) const {
auto it = mNameToService.find(name);
if (it == mNameToService.end()) {
return nullptr;
@@ -33,7 +33,7 @@
return it->second;
}
-status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
+status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service,
bool /*allowIsolated*/,
int /*dumpsysFlags*/) {
if (service == nullptr) {
@@ -43,7 +43,7 @@
return NO_ERROR;
}
-Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) {
+Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) {
Vector<String16> services;
for (auto const& [name, service] : mNameToService) {
(void) service;
@@ -52,19 +52,19 @@
return services;
}
-IBinder* ServiceManager::onAsBinder() {
+IBinder* FakeServiceManager::onAsBinder() {
return nullptr;
}
-sp<IBinder> ServiceManager::waitForService(const String16& name) {
+sp<IBinder> FakeServiceManager::waitForService(const String16& name) {
return checkService(name);
}
-bool ServiceManager::isDeclared(const String16& name) {
+bool FakeServiceManager::isDeclared(const String16& name) {
return mNameToService.find(name) != mNameToService.end();
}
-Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) {
Vector<String16> out;
const String16 prefix = name + String16("/");
for (const auto& [registeredName, service] : mNameToService) {
@@ -76,38 +76,38 @@
return out;
}
-std::optional<String16> ServiceManager::updatableViaApex(const String16& name) {
+std::optional<String16> FakeServiceManager::updatableViaApex(const String16& name) {
(void)name;
return std::nullopt;
}
-Vector<String16> ServiceManager::getUpdatableNames(const String16& apexName) {
+Vector<String16> FakeServiceManager::getUpdatableNames(const String16& apexName) {
(void)apexName;
return {};
}
-std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo(
+std::optional<IServiceManager::ConnectionInfo> FakeServiceManager::getConnectionInfo(
const String16& name) {
(void)name;
return std::nullopt;
}
-status_t ServiceManager::registerForNotifications(const String16&,
+status_t FakeServiceManager::registerForNotifications(const String16&,
const sp<LocalRegistrationCallback>&) {
return INVALID_OPERATION;
}
-status_t ServiceManager::unregisterForNotifications(const String16&,
+status_t FakeServiceManager::unregisterForNotifications(const String16&,
const sp<LocalRegistrationCallback>&) {
return INVALID_OPERATION;
}
-std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugInfo() {
+std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDebugInfo() {
std::vector<IServiceManager::ServiceDebugInfo> ret;
return ret;
}
-void ServiceManager::clear() {
+void FakeServiceManager::clear() {
mNameToService.clear();
}
} // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
similarity index 96%
rename from libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
rename to libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index ba6bb7d..97add24 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -28,9 +28,9 @@
* A local host simple implementation of IServiceManager, that does not
* communicate over binder.
*/
-class ServiceManager : public IServiceManager {
+class FakeServiceManager : public IServiceManager {
public:
- ServiceManager();
+ FakeServiceManager();
sp<IBinder> getService( const String16& name) const override;
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
index 8682c1c..6fc21c6 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -21,14 +21,14 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
using android::sp;
using android::BBinder;
using android::IBinder;
using android::OK;
using android::status_t;
-using android::ServiceManager;
+using android::FakeServiceManager;
using android::String16;
using android::IServiceManager;
using testing::ElementsAre;
@@ -45,19 +45,19 @@
}
TEST(AddService, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
}
TEST(AddService, SadNullBinder) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL);
}
TEST(AddService, HappyOverExistingService) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
@@ -65,7 +65,7 @@
}
TEST(AddService, HappyClearAddedService) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
EXPECT_NE(sm->getService(String16("foo")), nullptr);
@@ -74,7 +74,7 @@
}
TEST(GetService, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -84,13 +84,13 @@
}
TEST(GetService, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->getService(String16("foo")), nullptr);
}
TEST(ListServices, AllServices) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
@@ -109,13 +109,13 @@
}
TEST(WaitForService, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
}
TEST(WaitForService, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -125,13 +125,13 @@
}
TEST(IsDeclared, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_FALSE(sm->isDeclared(String16("foo")));
}
TEST(IsDeclared, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 8e57152..ea1b5e4 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -10,9 +10,6 @@
cc_test {
name: "ftl_test",
test_suites: ["device-tests"],
- sanitize: {
- address: true,
- },
srcs: [
"algorithm_test.cpp",
"cast_test.cpp",
diff --git a/libs/ftl/TEST_MAPPING b/libs/ftl/TEST_MAPPING
new file mode 100644
index 0000000..ec0c671
--- /dev/null
+++ b/libs/ftl/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "ftl_test"
+ }
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "ftl_test"
+ }
+ ]
+}
diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp
index 6b3b6c4..91bf7bc 100644
--- a/libs/ftl/optional_test.cpp
+++ b/libs/ftl/optional_test.cpp
@@ -164,6 +164,46 @@
}));
}
+TEST(Optional, OrElse) {
+ // Non-empty.
+ {
+ const Optional opt = false;
+ EXPECT_EQ(false, opt.or_else([] { return Optional(true); }));
+ EXPECT_EQ('x', Optional('x').or_else([] { return std::make_optional('y'); }));
+ }
+
+ // Empty.
+ {
+ const Optional<int> opt;
+ EXPECT_EQ(123, opt.or_else([]() -> Optional<int> { return 123; }));
+ EXPECT_EQ("abc"s, Optional<std::string>().or_else([] { return Optional("abc"s); }));
+ }
+ {
+ bool empty = false;
+ EXPECT_EQ(Optional<float>(), Optional<float>().or_else([&empty]() -> Optional<float> {
+ empty = true;
+ return std::nullopt;
+ }));
+ EXPECT_TRUE(empty);
+ }
+
+ // Chaining.
+ using StringVector = StaticVector<std::string, 3>;
+ EXPECT_EQ(999, Optional(StaticVector{"1"s, "0"s, "0"s})
+ .and_then([](StringVector&& v) -> Optional<StringVector> {
+ if (v.push_back("0"s)) return v;
+ return {};
+ })
+ .or_else([] {
+ return Optional(StaticVector{"9"s, "9"s, "9"s});
+ })
+ .transform([](const StringVector& v) {
+ return std::accumulate(v.begin(), v.end(), std::string());
+ })
+ .and_then(parse_int)
+ .or_else([] { return Optional(-1); }));
+}
+
// Comparison.
namespace {
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
index 347c4e0..1db8cbe 100644
--- a/libs/graphicsenv/OWNERS
+++ b/libs/graphicsenv/OWNERS
@@ -1,10 +1,4 @@
-abdolrashidi@google.com
-cclao@google.com
chrisforbes@google.com
cnorthrop@google.com
ianelliott@google.com
-lfy@google.com
lpy@google.com
-romanl@google.com
-vantablack@google.com
-yuxinhu@google.com
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 9d82c14..821dd37 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -139,6 +139,11 @@
}
}
+void BLASTBufferItemConsumer::resizeFrameEventHistory(size_t newSize) {
+ Mutex::Autolock lock(mMutex);
+ mFrameEventHistory.resize(newSize);
+}
+
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
: mSurfaceControl(nullptr),
mSize(1, 1),
@@ -486,6 +491,17 @@
mSyncedFrameNumbers.erase(callbackId.framenumber);
}
+static ui::Size getBufferSize(const BufferItem& item) {
+ uint32_t bufWidth = item.mGraphicBuffer->getWidth();
+ uint32_t bufHeight = item.mGraphicBuffer->getHeight();
+
+ // Take the buffer's orientation into account
+ if (item.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ return ui::Size(bufWidth, bufHeight);
+}
+
status_t BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
// Check if we have frames available and we have not acquired the maximum number of buffers.
@@ -563,7 +579,12 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
- mSize = mRequestedSize;
+ // Only update mSize for destination bounds if the incoming buffer matches the requested size.
+ // Otherwise, it could cause stretching since the destination bounds will update before the
+ // buffer with the new size is acquired.
+ if (mRequestedSize == getBufferSize(bufferItem)) {
+ mSize = mRequestedSize;
+ }
Rect crop = computeCrop(bufferItem);
mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
@@ -834,14 +855,7 @@
return false;
}
- uint32_t bufWidth = item.mGraphicBuffer->getWidth();
- uint32_t bufHeight = item.mGraphicBuffer->getHeight();
-
- // Take the buffer's orientation into account
- if (item.mTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
- ui::Size bufferSize(bufWidth, bufHeight);
+ ui::Size bufferSize = getBufferSize(item);
if (mRequestedSize != mSize && mRequestedSize == bufferSize) {
return false;
}
@@ -1060,8 +1074,9 @@
// can be non-blocking when the producer is in the client process.
class BBQBufferQueueProducer : public BufferQueueProducer {
public:
- BBQBufferQueueProducer(const sp<BufferQueueCore>& core)
- : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {}
+ BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq)
+ : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/),
+ mBLASTBufferQueue(std::move(bbq)) {}
status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
QueueBufferOutput* output) override {
@@ -1073,6 +1088,26 @@
producerControlledByApp, output);
}
+ // We want to resize the frame history when changing the size of the buffer queue
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override {
+ int maxBufferCount;
+ status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
+ &maxBufferCount);
+ // if we can't determine the max buffer count, then just skip growing the history size
+ if (status == OK) {
+ size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
+ // optimize away resizing the frame history unless it will grow
+ if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (bbq != nullptr) {
+ ALOGV("increasing frame history size to %zu", newFrameHistorySize);
+ bbq->resizeFrameEventHistory(newFrameHistorySize);
+ }
+ }
+ }
+ return status;
+ }
+
int query(int what, int* value) override {
if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
*value = 1;
@@ -1080,6 +1115,9 @@
}
return BufferQueueProducer::query(what, value);
}
+
+private:
+ const wp<BLASTBufferQueue> mBLASTBufferQueue;
};
// Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer.
@@ -1094,7 +1132,7 @@
sp<BufferQueueCore> core(new BufferQueueCore());
LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");
- sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));
+ sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this));
LOG_ALWAYS_FATAL_IF(producer == nullptr,
"BLASTBufferQueue: failed to create BBQBufferQueueProducer");
@@ -1107,6 +1145,16 @@
*outConsumer = consumer;
}
+void BLASTBufferQueue::resizeFrameEventHistory(size_t newSize) {
+ // This can be null during creation of the buffer queue, but resizing won't do anything at that
+ // point in time, so just ignore. This can go away once the class relationships and lifetimes of
+ // objects are cleaned up with a major refactor of BufferQueue as a whole.
+ if (mBufferItemConsumer != nullptr) {
+ std::unique_lock _lock{mMutex};
+ mBufferItemConsumer->resizeFrameEventHistory(newSize);
+ }
+}
+
PixelFormat BLASTBufferQueue::convertBufferFormat(PixelFormat& format) {
PixelFormat convertedFormat = format;
switch (format) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 5fe5e71..9eb1a9f 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -119,6 +119,12 @@
status_t BufferQueueProducer::setMaxDequeuedBufferCount(
int maxDequeuedBuffers) {
+ int maxBufferCount;
+ return setMaxDequeuedBufferCount(maxDequeuedBuffers, &maxBufferCount);
+}
+
+status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers,
+ int* maxBufferCount) {
ATRACE_CALL();
BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
maxDequeuedBuffers);
@@ -134,6 +140,8 @@
return NO_INIT;
}
+ *maxBufferCount = mCore->getMaxBufferCountLocked();
+
if (maxDequeuedBuffers == mCore->mMaxDequeuedBufferCount) {
return NO_ERROR;
}
@@ -183,6 +191,7 @@
return BAD_VALUE;
}
mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
+ *maxBufferCount = mCore->getMaxBufferCountLocked();
VALIDATE_CONSISTENCY();
if (delta < 0) {
listener = mCore->mConsumerListener;
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 99bf6ba..46fb068 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -15,8 +15,10 @@
*/
// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <gui/Choreographer.h>
+#include <gui/TraceUtils.h>
#include <jni.h>
#undef LOG_TAG
@@ -297,6 +299,8 @@
mLastVsyncEventData = vsyncEventData;
for (const auto& cb : callbacks) {
if (cb.vsyncCallback != nullptr) {
+ ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64,
+ vsyncEventData.preferredVsyncId());
const ChoreographerFrameCallbackDataImpl frameCallbackData =
createFrameCallbackData(timestamp);
registerStartTime();
@@ -306,8 +310,10 @@
cb.data);
mInCallback = false;
} else if (cb.callback64 != nullptr) {
+ ATRACE_FORMAT("AChoreographer_frameCallback64");
cb.callback64(timestamp, cb.data);
} else if (cb.callback != nullptr) {
+ ATRACE_FORMAT("AChoreographer_frameCallback");
cb.callback(timestamp, cb.data);
}
}
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index e2ea3f9..f3eb4e8 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -168,10 +168,11 @@
} // namespace
-const size_t FrameEventHistory::MAX_FRAME_HISTORY =
+const size_t FrameEventHistory::INITIAL_MAX_FRAME_HISTORY =
sysprop::LibGuiProperties::frame_event_history_size().value_or(8);
-FrameEventHistory::FrameEventHistory() : mFrames(std::vector<FrameEvents>(MAX_FRAME_HISTORY)) {}
+FrameEventHistory::FrameEventHistory()
+ : mFrames(std::vector<FrameEvents>(INITIAL_MAX_FRAME_HISTORY)) {}
FrameEventHistory::~FrameEventHistory() = default;
@@ -227,7 +228,6 @@
}
}
-
// ============================================================================
// ProducerFrameEventHistory
// ============================================================================
@@ -273,6 +273,13 @@
const FrameEventHistoryDelta& delta) {
mCompositorTiming = delta.mCompositorTiming;
+ // Deltas should have enough reserved capacity for the consumer-side, therefore if there's a
+ // different capacity, we re-sized on the consumer side and now need to resize on the producer
+ // side.
+ if (delta.mDeltas.capacity() > mFrames.capacity()) {
+ resize(delta.mDeltas.capacity());
+ }
+
for (auto& d : delta.mDeltas) {
// Avoid out-of-bounds access.
if (CC_UNLIKELY(d.mIndex >= mFrames.size())) {
@@ -349,13 +356,48 @@
return std::make_shared<FenceTime>(fence);
}
+void ProducerFrameEventHistory::resize(size_t newSize) {
+ // we don't want to drop events by resizing too small, so don't resize in the negative direction
+ if (newSize <= mFrames.size()) {
+ return;
+ }
+
+ // This algorithm for resizing needs to be the same as ConsumerFrameEventHistory::resize,
+ // because the indexes need to match when communicating the FrameEventDeltas.
+
+ // We need to find the oldest frame, because that frame needs to move to index 0 in the new
+ // frame history.
+ size_t oldestFrameIndex = 0;
+ size_t oldestFrameNumber = INT32_MAX;
+ for (size_t i = 0; i < mFrames.size(); ++i) {
+ if (mFrames[i].frameNumber < oldestFrameNumber && mFrames[i].valid) {
+ oldestFrameNumber = mFrames[i].frameNumber;
+ oldestFrameIndex = i;
+ }
+ }
+
+ // move the existing frame information into a new vector, so that the oldest frames are at
+ // index 0, and the latest frames are at the end of the vector
+ std::vector<FrameEvents> newFrames(newSize);
+ size_t oldI = oldestFrameIndex;
+ size_t newI = 0;
+ do {
+ if (mFrames[oldI].valid) {
+ newFrames[newI++] = std::move(mFrames[oldI]);
+ }
+ oldI = (oldI + 1) % mFrames.size();
+ } while (oldI != oldestFrameIndex);
+
+ mFrames = std::move(newFrames);
+ mAcquireOffset = 0; // this is just a hint, so setting this to anything is fine
+}
// ============================================================================
// ConsumerFrameEventHistory
// ============================================================================
ConsumerFrameEventHistory::ConsumerFrameEventHistory()
- : mFramesDirty(std::vector<FrameEventDirtyFields>(MAX_FRAME_HISTORY)) {}
+ : mFramesDirty(std::vector<FrameEventDirtyFields>(INITIAL_MAX_FRAME_HISTORY)) {}
ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
@@ -489,6 +531,36 @@
}
}
+void ConsumerFrameEventHistory::resize(size_t newSize) {
+ // we don't want to drop events by resizing too small, so don't resize in the negative direction
+ if (newSize <= mFrames.size()) {
+ return;
+ }
+
+ // This algorithm for resizing needs to be the same as ProducerFrameEventHistory::resize,
+ // because the indexes need to match when communicating the FrameEventDeltas.
+
+ // move the existing frame information into a new vector, so that the oldest frames are at
+ // index 0, and the latest frames are towards the end of the vector
+ std::vector<FrameEvents> newFrames(newSize);
+ std::vector<FrameEventDirtyFields> newFramesDirty(newSize);
+ size_t oldestFrameIndex = mQueueOffset;
+ size_t oldI = oldestFrameIndex;
+ size_t newI = 0;
+ do {
+ if (mFrames[oldI].valid) {
+ newFrames[newI] = std::move(mFrames[oldI]);
+ newFramesDirty[newI] = mFramesDirty[oldI];
+ newI += 1;
+ }
+ oldI = (oldI + 1) % mFrames.size();
+ } while (oldI != oldestFrameIndex);
+
+ mFrames = std::move(newFrames);
+ mFramesDirty = std::move(newFramesDirty);
+ mQueueOffset = newI;
+ mCompositionOffset = 0; // this is just a hint, so setting this to anything is fine
+}
// ============================================================================
// FrameEventsDelta
@@ -558,8 +630,7 @@
return NO_MEMORY;
}
- if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
- mIndex > std::numeric_limits<uint16_t>::max()) {
+ if (mIndex >= UINT8_MAX || mIndex < 0) {
return BAD_VALUE;
}
@@ -601,7 +672,7 @@
uint16_t temp16 = 0;
FlattenableUtils::read(buffer, size, temp16);
mIndex = temp16;
- if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (mIndex >= UINT8_MAX) {
return BAD_VALUE;
}
uint8_t temp8 = 0;
@@ -627,6 +698,25 @@
return NO_ERROR;
}
+uint64_t FrameEventsDelta::getFrameNumber() const {
+ return mFrameNumber;
+}
+
+bool FrameEventsDelta::getLatchTime(nsecs_t* latchTime) const {
+ if (mLatchTime == FrameEvents::TIMESTAMP_PENDING) {
+ return false;
+ }
+ *latchTime = mLatchTime;
+ return true;
+}
+
+bool FrameEventsDelta::getDisplayPresentFence(sp<Fence>* fence) const {
+ if (mDisplayPresentFence.fence == Fence::NO_FENCE) {
+ return false;
+ }
+ *fence = mDisplayPresentFence.fence;
+ return true;
+}
// ============================================================================
// FrameEventHistoryDelta
@@ -665,7 +755,7 @@
status_t FrameEventHistoryDelta::flatten(
void*& buffer, size_t& size, int*& fds, size_t& count) const {
- if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (mDeltas.size() > UINT8_MAX) {
return BAD_VALUE;
}
if (size < getFlattenedSize()) {
@@ -695,7 +785,7 @@
uint32_t deltaCount = 0;
FlattenableUtils::read(buffer, size, deltaCount);
- if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (deltaCount > UINT8_MAX) {
return BAD_VALUE;
}
mDeltas.resize(deltaCount);
@@ -708,5 +798,12 @@
return NO_ERROR;
}
+std::vector<FrameEventsDelta>::const_iterator FrameEventHistoryDelta::begin() const {
+ return mDeltas.begin();
+}
+
+std::vector<FrameEventsDelta>::const_iterator FrameEventHistoryDelta::end() const {
+ return mDeltas.end();
+}
} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index a6276e5..b391337 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -74,7 +74,7 @@
surfaceDamageRegion(),
api(-1),
colorTransform(mat4()),
- bgColorAlpha(0),
+ bgColor(0),
bgColorDataspace(ui::Dataspace::UNKNOWN),
colorSpaceAgnostic(false),
shadowRadius(0.0f),
@@ -140,7 +140,10 @@
SAFE_PARCEL(output.writeFloat, cornerRadius);
SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
SAFE_PARCEL(output.writeParcelable, metadata);
- SAFE_PARCEL(output.writeFloat, bgColorAlpha);
+ SAFE_PARCEL(output.writeFloat, bgColor.r);
+ SAFE_PARCEL(output.writeFloat, bgColor.g);
+ SAFE_PARCEL(output.writeFloat, bgColor.b);
+ SAFE_PARCEL(output.writeFloat, bgColor.a);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace));
SAFE_PARCEL(output.writeBool, colorSpaceAgnostic);
SAFE_PARCEL(output.writeVectorSize, listeners);
@@ -189,6 +192,7 @@
SAFE_PARCEL(output.writeParcelable, trustedPresentationListener);
SAFE_PARCEL(output.writeFloat, currentSdrHdrRatio);
SAFE_PARCEL(output.writeFloat, desiredSdrHdrRatio);
+ SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint))
return NO_ERROR;
}
@@ -258,7 +262,14 @@
SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
SAFE_PARCEL(input.readParcelable, &metadata);
- SAFE_PARCEL(input.readFloat, &bgColorAlpha);
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ bgColor.r = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ bgColor.g = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ bgColor.b = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ bgColor.a = tmpFloat;
SAFE_PARCEL(input.readUint32, &tmpUint32);
bgColorDataspace = static_cast<ui::Dataspace>(tmpUint32);
SAFE_PARCEL(input.readBool, &colorSpaceAgnostic);
@@ -328,6 +339,10 @@
SAFE_PARCEL(input.readFloat, &tmpFloat);
desiredSdrHdrRatio = tmpFloat;
+ int32_t tmpInt32;
+ SAFE_PARCEL(input.readInt32, &tmpInt32);
+ cachingHint = static_cast<gui::CachingHint>(tmpInt32);
+
return NO_ERROR;
}
@@ -580,6 +595,10 @@
desiredSdrHdrRatio = other.desiredSdrHdrRatio;
currentSdrHdrRatio = other.currentSdrHdrRatio;
}
+ if (other.what & eCachingHintChanged) {
+ what |= eCachingHintChanged;
+ cachingHint = other.cachingHint;
+ }
if (other.what & eHdrMetadataChanged) {
what |= eHdrMetadataChanged;
hdrMetadata = other.hdrMetadata;
@@ -609,8 +628,7 @@
}
if (other.what & eBackgroundColorChanged) {
what |= eBackgroundColorChanged;
- color.rgb = other.color.rgb;
- bgColorAlpha = other.bgColorAlpha;
+ bgColor = other.bgColor;
bgColorDataspace = other.bgColorDataspace;
}
if (other.what & eMetadataChanged) {
@@ -731,6 +749,7 @@
CHECK_DIFF(diff, eDataspaceChanged, other, dataspace);
CHECK_DIFF2(diff, eExtendedRangeBrightnessChanged, other, currentSdrHdrRatio,
desiredSdrHdrRatio);
+ CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint);
CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
if (other.what & eSurfaceDamageRegionChanged &&
(!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
@@ -742,7 +761,7 @@
CHECK_DIFF(diff, eColorTransformChanged, other, colorTransform);
if (other.what & eHasListenerCallbacksChanged) diff |= eHasListenerCallbacksChanged;
if (other.what & eInputInfoChanged) diff |= eInputInfoChanged;
- CHECK_DIFF3(diff, eBackgroundColorChanged, other, color.rgb, bgColorAlpha, bgColorDataspace);
+ CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace);
if (other.what & eMetadataChanged) diff |= eMetadataChanged;
CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c508917..2f5830d 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1363,7 +1363,8 @@
(mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
(mask & layer_state_t::eEnableBackpressure) ||
(mask & layer_state_t::eIgnoreDestinationFrame) ||
- (mask & layer_state_t::eLayerIsDisplayDecoration)) {
+ (mask & layer_state_t::eLayerIsDisplayDecoration) ||
+ (mask & layer_state_t::eLayerIsRefreshRateIndicator)) {
s->what |= layer_state_t::eFlagsChanged;
}
s->flags &= ~mask;
@@ -1560,8 +1561,8 @@
}
s->what |= layer_state_t::eBackgroundColorChanged;
- s->color.rgb = color;
- s->bgColorAlpha = alpha;
+ s->bgColor.rgb = color;
+ s->bgColor.a = alpha;
s->bgColorDataspace = dataspace;
registerSurfaceControlForCallback(sc);
@@ -1729,6 +1730,20 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachingHint(
+ const sp<SurfaceControl>& sc, gui::CachingHint cachingHint) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eCachingHintChanged;
+ s->cachingHint = cachingHint;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata(
const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) {
layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/VsyncEventData.cpp b/libs/gui/VsyncEventData.cpp
index 23f0921..76c60c2 100644
--- a/libs/gui/VsyncEventData.cpp
+++ b/libs/gui/VsyncEventData.cpp
@@ -23,6 +23,9 @@
namespace android::gui {
+static_assert(VsyncEventData::kFrameTimelinesLength == 7,
+ "Must update value in DisplayEventReceiver.java#FRAME_TIMELINES_LENGTH (and here)");
+
int64_t VsyncEventData::preferredVsyncId() const {
return frameTimelines[preferredFrameTimelineIndex].vsyncId;
}
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 01e865d..2b34a0f 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -62,6 +62,10 @@
status_t status = OK;
{
std::scoped_lock lock(mListenersMutex);
+ if (mWindowInfosListeners.find(windowInfosListener) == mWindowInfosListeners.end()) {
+ return status;
+ }
+
if (mWindowInfosListeners.size() == 1) {
binder::Status s = surfaceComposer->removeWindowInfosListener(this);
status = statusTFromBinderStatus(s);
diff --git a/libs/gui/aidl/android/gui/CachingHint.aidl b/libs/gui/aidl/android/gui/CachingHint.aidl
new file mode 100644
index 0000000..b35c795
--- /dev/null
+++ b/libs/gui/aidl/android/gui/CachingHint.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+/*
+ * Hint for configuring caching behavior for a layer
+ * @hide
+ */
+@Backing(type="int")
+enum CachingHint {
+ // Caching is disabled. A layer may explicitly disable caching for
+ // improving image quality for some scenes.
+ Disabled = 0,
+ // Caching is enabled. A layer is cacheable by default.
+ Enabled = 1
+}
diff --git a/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
index fc809c4..e8c36ee 100644
--- a/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
+++ b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
@@ -19,7 +19,9 @@
/** @hide */
oneway interface IHdrLayerInfoListener {
// Callback with the total number of HDR layers, the dimensions of the largest layer,
- // and a placeholder flags
+ // a placeholder flags, and the max desired HDR/SDR ratio. The max desired HDR/SDR
+ // ratio may be positive infinity to indicate an unbounded ratio.
// TODO (b/182312559): Define the flags (likely need an indicator that a UDFPS layer is present)
- void onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH, int flags);
+ void onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH,
+ int flags, float maxDesiredHdrSdrRatio);
}
\ No newline at end of file
diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
index 55462c3..9667954 100644
--- a/libs/gui/bufferqueue/1.0/Conversion.cpp
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -725,12 +725,7 @@
// These were written as uint8_t for alignment.
uint8_t temp = 0;
FlattenableUtils::read(buffer, size, temp);
- size_t index = static_cast<size_t>(temp);
- if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
- return BAD_VALUE;
- }
- t->index = static_cast<uint32_t>(index);
-
+ t->index = static_cast<uint32_t>(temp);
FlattenableUtils::read(buffer, size, temp);
t->addPostCompositeCalled = static_cast<bool>(temp);
FlattenableUtils::read(buffer, size, temp);
@@ -786,8 +781,7 @@
status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
void*& buffer, size_t& size, int*& fds, size_t numFds) {
// Check that t.index is within a valid range.
- if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
- || t.index > std::numeric_limits<uint8_t>::max()) {
+ if (t.index > UINT8_MAX || t.index < 0) {
return BAD_VALUE;
}
@@ -887,8 +881,7 @@
uint32_t deltaCount = 0;
FlattenableUtils::read(buffer, size, deltaCount);
- if (static_cast<size_t>(deltaCount) >
- ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (deltaCount > UINT8_MAX) {
return BAD_VALUE;
}
t->deltas.resize(deltaCount);
@@ -919,7 +912,7 @@
status_t flatten(
HGraphicBufferProducer::FrameEventHistoryDelta const& t,
void*& buffer, size_t& size, int*& fds, size_t& numFds) {
- if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (t.deltas.size() > UINT8_MAX) {
return BAD_VALUE;
}
if (size < getFlattenedSize(t)) {
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index cee1b81..f684874 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -725,8 +725,7 @@
std::vector<native_handle_t*>* nh,
void*& buffer, size_t& size, int*& fds, size_t numFds) {
// Check that t.index is within a valid range.
- if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
- || t.index > std::numeric_limits<uint8_t>::max()) {
+ if (t.index > UINT8_MAX || t.index < 0) {
return BAD_VALUE;
}
@@ -829,7 +828,7 @@
HGraphicBufferProducer::FrameEventHistoryDelta const& t,
std::vector<std::vector<native_handle_t*> >* nh,
void*& buffer, size_t& size, int*& fds, size_t& numFds) {
- if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ if (t.deltas.size() > UINT8_MAX) {
return BAD_VALUE;
}
if (size < getFlattenedSize(t)) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index b9e0647..69e9f8a 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -54,6 +54,8 @@
nsecs_t dequeueReadyTime) EXCLUDES(mMutex);
void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect) EXCLUDES(mMutex);
+ void resizeFrameEventHistory(size_t newSize);
+
protected:
void onSidebandStreamChanged() override EXCLUDES(mMutex);
@@ -123,6 +125,7 @@
private:
friend class BLASTBufferQueueHelper;
+ friend class BBQBufferQueueProducer;
// can't be copied
BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
@@ -130,6 +133,8 @@
void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer);
+ void resizeFrameEventHistory(size_t newSize);
+
status_t acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 0ad3075..1d13dab 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -202,6 +202,11 @@
// See IGraphicBufferProducer::setAutoPrerotation
virtual status_t setAutoPrerotation(bool autoPrerotation);
+protected:
+ // see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the
+ // total maximum buffer count for the buffer queue (dequeued AND acquired)
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount);
+
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index c08a9b1..3d1be4d 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -97,9 +97,11 @@
void checkFencesForCompletion();
void dump(std::string& outString) const;
- static const size_t MAX_FRAME_HISTORY;
+ static const size_t INITIAL_MAX_FRAME_HISTORY;
protected:
+ void resize(size_t newSize);
+
std::vector<FrameEvents> mFrames;
CompositorTiming mCompositorTiming;
@@ -138,6 +140,8 @@
virtual std::shared_ptr<FenceTime> createFenceTime(
const sp<Fence>& fence) const;
+ void resize(size_t newSize);
+
size_t mAcquireOffset{0};
// The consumer updates it's timelines in Layer and SurfaceFlinger since
@@ -208,6 +212,8 @@
void getAndResetDelta(FrameEventHistoryDelta* delta);
+ void resize(size_t newSize);
+
private:
void getFrameDelta(FrameEventHistoryDelta* delta,
const std::vector<FrameEvents>::iterator& frame);
@@ -250,6 +256,10 @@
status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
size_t& count);
+ uint64_t getFrameNumber() const;
+ bool getLatchTime(nsecs_t* latchTime) const;
+ bool getDisplayPresentFence(sp<Fence>* fence) const;
+
private:
static constexpr size_t minFlattenedSize();
@@ -310,6 +320,9 @@
status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
size_t& count);
+ std::vector<FrameEventsDelta>::const_iterator begin() const;
+ std::vector<FrameEventsDelta>::const_iterator end() const;
+
private:
static constexpr size_t minFlattenedSize();
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index ae56f9f..1e67225 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/gui/CachingHint.h>
#include <android/gui/DisplayBrightness.h>
#include <android/gui/FrameTimelineInfo.h>
#include <android/gui/IDisplayEventConnection.h>
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index ddaf473..6e3be5c 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -160,6 +160,7 @@
// This is needed to maintain compatibility for SurfaceView scaling behavior.
// See SurfaceView scaling behavior for more details.
eIgnoreDestinationFrame = 0x400,
+ eLayerIsRefreshRateIndicator = 0x800, // REFRESH_RATE_INDICATOR
};
enum {
@@ -172,7 +173,7 @@
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
eFlushJankData = 0x00000100,
- /* unused = 0x00000200, */
+ eCachingHintChanged = 0x00000200,
eDimmingEnabledChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
eRenderBorderChanged = 0x00001000,
@@ -211,7 +212,8 @@
eStretchChanged = 0x2000'00000000,
eTrustedOverlayChanged = 0x4000'00000000,
eDropInputModeChanged = 0x8000'00000000,
- eExtendedRangeBrightnessChanged = 0x10000'00000000
+ eExtendedRangeBrightnessChanged = 0x10000'00000000,
+
};
layer_state_t();
@@ -225,9 +227,9 @@
bool hasBufferChanges() const;
// Layer hierarchy updates.
- static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eBackgroundColorChanged |
- layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
- layer_state_t::eReparent;
+ static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged |
+ layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent |
+ layer_state_t::eLayerStackChanged;
// Geometry updates.
static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged |
@@ -263,9 +265,8 @@
static constexpr uint64_t AFFECTS_CHILDREN = layer_state_t::GEOMETRY_CHANGES |
layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
- layer_state_t::eFlagsChanged | layer_state_t::eLayerStackChanged |
- layer_state_t::eTrustedOverlayChanged | layer_state_t::eFrameRateChanged |
- layer_state_t::eFixedTransformHintChanged;
+ layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged |
+ layer_state_t::eFrameRateChanged | layer_state_t::eFixedTransformHintChanged;
// Changes affecting data sent to input.
static constexpr uint64_t INPUT_CHANGES = layer_state_t::GEOMETRY_CHANGES |
@@ -332,7 +333,7 @@
// The following refer to the alpha, and dataspace, respectively of
// the background color layer
- float bgColorAlpha;
+ half4 bgColor;
ui::Dataspace bgColorDataspace;
// A color space agnostic layer means the color of this layer can be
@@ -391,6 +392,8 @@
float currentSdrHdrRatio = 1.f;
float desiredSdrHdrRatio = 1.f;
+ gui::CachingHint cachingHint = gui::CachingHint::Enabled;
+
TrustedPresentationThresholds trustedPresentationThresholds;
TrustedPresentationListener trustedPresentationListener;
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 44e78ec..d431b43 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -566,6 +566,7 @@
Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc,
float currentBufferRatio, float desiredRatio);
+ Transaction& setCachingHint(const sp<SurfaceControl>& sc, gui::CachingHint cachingHint);
Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
const Region& surfaceDamageRegion);
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 1bfe462..198908d 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -16,17 +16,59 @@
#define LOG_TAG "Surface"
-#include <gui/view/Surface.h>
-
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
#include <binder/Parcel.h>
-
-#include <utils/Log.h>
-
#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+#include <system/window.h>
+#include <utils/Log.h>
namespace android {
namespace view {
+// Since this is a parcelable utility and we want to keep the wire format stable, only build this
+// when building the system libgui to detect any issues loading the wrong libgui from
+// libnativewindow
+
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__))
+
+extern "C" status_t android_view_Surface_writeToParcel(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel) {
+ int value;
+ int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
+ if (err != OK || value != NATIVE_WINDOW_SURFACE) {
+ ALOGE("Error: ANativeWindow is not backed by Surface");
+ return STATUS_BAD_VALUE;
+ }
+ // Use a android::view::Surface to parcelize the window
+ android::view::Surface shimSurface;
+ shimSurface.graphicBufferProducer = android::Surface::getIGraphicBufferProducer(window);
+ shimSurface.surfaceControlHandle = android::Surface::getSurfaceControlHandle(window);
+ return shimSurface.writeToParcel(parcel);
+}
+
+extern "C" status_t android_view_Surface_readFromParcel(
+ const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+ // Use a android::view::Surface to unparcel the window
+ android::view::Surface shimSurface;
+ status_t ret = shimSurface.readFromParcel(parcel);
+ if (ret != OK) {
+ ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
+ return STATUS_BAD_VALUE;
+ }
+ auto surface = sp<android::Surface>::make(shimSurface.graphicBufferProducer, false,
+ shimSurface.surfaceControlHandle);
+ ANativeWindow* anw = surface.get();
+ ANativeWindow_acquire(anw);
+ *outWindow = anw;
+ return STATUS_OK;
+}
+
+#endif
+
status_t Surface::writeToParcel(Parcel* parcel) const {
return writeToParcel(parcel, false);
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index fd4fc16..869458c 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -47,6 +47,7 @@
"Input.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
+ "InputVerifier.cpp",
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
@@ -57,6 +58,7 @@
"TouchVideoFrame.cpp",
"VelocityControl.cpp",
"VelocityTracker.cpp",
+ "VirtualInputDevice.cpp",
"VirtualKeyMap.cpp",
],
@@ -67,6 +69,10 @@
],
export_header_lib_headers: ["jni_headers"],
+ generated_headers: [
+ "toolbox_input_labels",
+ ],
+
shared_libs: [
"libbase",
"libcutils",
@@ -124,6 +130,8 @@
enabled: false,
},
include_dirs: [
+ "bionic/libc/kernel/android/uapi/",
+ "bionic/libc/kernel/uapi",
"frameworks/native/libs/arect/include",
],
},
@@ -159,6 +167,7 @@
cc_defaults {
name: "libinput_fuzz_defaults",
+ cpp_std: "c++20",
host_supported: true,
shared_libs: [
"libutils",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 133b260..53b22cb 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -151,10 +151,23 @@
// --- InputEvent ---
+// Due to precision limitations when working with floating points, transforming - namely
+// scaling - floating points can lead to minute errors. We round transformed values to approximately
+// three decimal places so that values like 0.99997 show up as 1.0.
+inline float roundTransformedCoords(float val) {
+ // Use a power to two to approximate three decimal places to potentially reduce some cycles.
+ // This should be at least as precise as MotionEvent::ROUNDING_PRECISION.
+ return std::round(val * 1024.f) / 1024.f;
+}
+
+inline vec2 roundTransformedCoords(vec2 p) {
+ return {roundTransformedCoords(p.x), roundTransformedCoords(p.y)};
+}
+
vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
const vec2 transformedXy = transform.transform(xy);
const vec2 transformedOrigin = transform.transform(0, 0);
- return transformedXy - transformedOrigin;
+ return roundTransformedCoords(transformedXy - transformedOrigin);
}
float transformAngle(const ui::Transform& transform, float angleRadians) {
@@ -606,12 +619,12 @@
float MotionEvent::getXCursorPosition() const {
vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
- return vals.x;
+ return roundTransformedCoords(vals.x);
}
float MotionEvent::getYCursorPosition() const {
vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
- return vals.y;
+ return roundTransformedCoords(vals.y);
}
void MotionEvent::setCursorPosition(float x, float y) {
@@ -933,7 +946,7 @@
static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
const vec2& xy) {
return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
- : transform.transform(xy);
+ : roundTransformedCoords(transform.transform(xy));
}
vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index d97c1bb..4a19227 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -16,6 +16,9 @@
#include <input/InputEventLabels.h>
+#include <linux/input-event-codes.h>
+#include <linux/input.h>
+
#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
#define DEFINE_LED(led) { #led, ALED_##led }
@@ -480,4 +483,85 @@
return lookupValueByLabel(LEDS, label);
}
+namespace {
+
+struct label {
+ const char* name;
+ int value;
+};
+
+#define LABEL(constant) \
+ { #constant, constant }
+#define LABEL_END \
+ { nullptr, -1 }
+
+static struct label ev_key_value_labels[] = {
+ {"UP", 0},
+ {"DOWN", 1},
+ {"REPEAT", 2},
+ LABEL_END,
+};
+
+#include "input.h-labels.h"
+
+#undef LABEL
+#undef LABEL_END
+
+std::string getLabel(const label* labels, int value) {
+ if (labels == nullptr) return std::to_string(value);
+ while (labels->name != nullptr && value != labels->value) {
+ labels++;
+ }
+ return labels->name != nullptr ? labels->name : std::to_string(value);
+}
+
+const label* getCodeLabelsForType(int32_t type) {
+ switch (type) {
+ case EV_SYN:
+ return syn_labels;
+ case EV_KEY:
+ return key_labels;
+ case EV_REL:
+ return rel_labels;
+ case EV_ABS:
+ return abs_labels;
+ case EV_SW:
+ return sw_labels;
+ case EV_MSC:
+ return msc_labels;
+ case EV_LED:
+ return led_labels;
+ case EV_REP:
+ return rep_labels;
+ case EV_SND:
+ return snd_labels;
+ case EV_FF:
+ return ff_labels;
+ case EV_FF_STATUS:
+ return ff_status_labels;
+ default:
+ return nullptr;
+ }
+}
+
+const label* getValueLabelsForTypeAndCode(int32_t type, int32_t code) {
+ if (type == EV_KEY) {
+ return ev_key_value_labels;
+ }
+ if (type == EV_MSC && code == ABS_MT_TOOL_TYPE) {
+ return mt_tool_labels;
+ }
+ return nullptr;
+}
+
+} // namespace
+
+EvdevEventLabel InputEventLookup::getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value) {
+ return {
+ .type = getLabel(ev_labels, type),
+ .code = getLabel(getCodeLabelsForType(type), code),
+ .value = getLabel(getValueLabelsForTypeAndCode(type, code), value),
+ };
+}
+
} // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 9f0a314..311b244 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -5,20 +5,6 @@
//
#define LOG_TAG "InputTransport"
-//#define LOG_NDEBUG 0
-
-// Log debug messages about channel messages (send message, receive message)
-#define DEBUG_CHANNEL_MESSAGES 0
-
-// Log debug messages whenever InputChannel objects are created/destroyed
-static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false;
-
-// Log debug messages about transport actions
-static constexpr bool DEBUG_TRANSPORT_ACTIONS = false;
-
-// Log debug messages about touch event resampling
-#define DEBUG_RESAMPLING 0
-
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -27,6 +13,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
#include <cutils/properties.h>
@@ -36,6 +23,63 @@
#include <input/InputTransport.h>
+namespace {
+
+/**
+ * Log debug messages about channel messages (send message, receive message).
+ * Enable this via "adb shell setprop log.tag.InputTransportMessages DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_CHANNEL_MESSAGES =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Messages", ANDROID_LOG_INFO);
+
+/**
+ * Log debug messages whenever InputChannel objects are created/destroyed.
+ * Enable this via "adb shell setprop log.tag.InputTransportLifecycle DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_CHANNEL_LIFECYCLE =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Lifecycle", ANDROID_LOG_INFO);
+
+/**
+ * Log debug messages relating to the consumer end of the transport channel.
+ * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
+ */
+
+const bool DEBUG_TRANSPORT_CONSUMER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO);
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+/**
+ * Log debug messages relating to the producer end of the transport channel.
+ * Enable this via "adb shell setprop log.tag.InputTransportPublisher DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
+ */
+bool debugTransportPublisher() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_TRANSPORT_PUBLISHER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO);
+ return DEBUG_TRANSPORT_PUBLISHER;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO);
+}
+
+/**
+ * Log debug messages about touch event resampling.
+ * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG" (requires restart)
+ */
+const bool DEBUG_RESAMPLING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+
+} // namespace
+
using android::base::StringPrintf;
namespace android {
@@ -76,6 +120,14 @@
*/
static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling";
+/**
+ * Crash if the events that are getting sent to the InputPublisher are inconsistent.
+ * Enable this via "adb shell setprop log.tag.InputTransportVerifyEvents DEBUG"
+ */
+static bool verifyEvents() {
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VerifyEvents", ANDROID_LOG_INFO);
+}
+
template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
@@ -132,7 +184,7 @@
return valid;
}
}
- ALOGE("Invalid message type: %" PRIu32, header.type);
+ ALOGE("Invalid message type: %s", ftl::enum_string(header.type).c_str());
return false;
}
@@ -322,15 +374,13 @@
InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token)
: mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {
- if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get());
- }
+ ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel constructed: name='%s', fd=%d",
+ getName().c_str(), getFd().get());
}
InputChannel::~InputChannel() {
- if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get());
- }
+ ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel destroyed: name='%s', fd=%d",
+ getName().c_str(), getFd().get());
}
status_t InputChannel::openInputChannelPair(const std::string& name,
@@ -375,10 +425,8 @@
if (nWrite < 0) {
int error = errno;
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
- msg->header.type, strerror(error));
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ error sending message of type %s, %s",
+ mName.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error));
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
@@ -389,16 +437,14 @@
}
if (size_t(nWrite) != msgLength) {
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
- mName.c_str(), msg->header.type);
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
+ "channel '%s' ~ error sending message type %s, send was incomplete", mName.c_str(),
+ ftl::enum_string(msg->header.type).c_str());
return DEAD_OBJECT;
}
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ sent message of type %d", mName.c_str(), msg->header.type);
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(),
+ ftl::enum_string(msg->header.type).c_str());
return OK;
}
@@ -410,9 +456,8 @@
if (nRead < 0) {
int error = errno;
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.c_str(), errno);
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d",
+ mName.c_str(), errno);
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
@@ -423,9 +468,8 @@
}
if (nRead == 0) { // check for EOF
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.c_str());
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
+ "channel '%s' ~ receive message failed because peer was closed", mName.c_str());
return DEAD_OBJECT;
}
@@ -434,9 +478,8 @@
return BAD_VALUE;
}
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ received message of type %d", mName.c_str(), msg->header.type);
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(),
+ ftl::enum_string(msg->header.type).c_str());
return OK;
}
@@ -492,7 +535,8 @@
// --- InputPublisher ---
-InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
+InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel)
+ : mChannel(channel), mInputVerifier(channel->getName()) {}
InputPublisher::~InputPublisher() {
}
@@ -504,17 +548,19 @@
int32_t metaState, int32_t repeatCount, nsecs_t downTime,
nsecs_t eventTime) {
if (ATRACE_ENABLED()) {
- std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
- mChannel->getName().c_str(), keyCode);
+ std::string message =
+ StringPrintf("publishKeyEvent(inputChannel=%s, action=%s, keyCode=%s)",
+ mChannel->getName().c_str(), KeyEvent::actionToString(action),
+ KeyEvent::getLabel(keyCode));
ATRACE_NAME(message.c_str());
}
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
- "downTime=%" PRId64 ", eventTime=%" PRId64,
- mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode,
- metaState, repeatCount, downTime, eventTime);
- }
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: seq=%u, id=%d, deviceId=%d, source=%s, "
+ "action=%s, flags=0x%x, keyCode=%s, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+ "downTime=%" PRId64 ", eventTime=%" PRId64,
+ mChannel->getName().c_str(), __func__, seq, eventId, deviceId,
+ inputEventSourceToString(source).c_str(), KeyEvent::actionToString(action), flags,
+ KeyEvent::getLabel(keyCode), scanCode, metaState, repeatCount, downTime, eventTime);
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
@@ -550,24 +596,29 @@
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
- std::string message = StringPrintf(
- "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
- mChannel->getName().c_str(), action);
+ std::string message = StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)",
+ mChannel->getName().c_str(),
+ MotionEvent::actionToString(action).c_str());
ATRACE_NAME(message.c_str());
}
- if (DEBUG_TRANSPORT_ACTIONS) {
+ if (verifyEvents()) {
+ mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
+ pointerCoords, flags);
+ }
+ if (debugTransportPublisher()) {
std::string transformString;
transform.dump(transformString, "transform", " ");
- ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
+ ALOGD("channel '%s' publisher ~ %s: seq=%u, id=%d, deviceId=%d, source=%s, "
"displayId=%" PRId32 ", "
- "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
+ "action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
"metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
"pointerCount=%" PRIu32 " \n%s",
- mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
- flags, edgeFlags, metaState, buttonState,
- motionClassificationToString(classification), xPrecision, yPrecision, downTime,
- eventTime, pointerCount, transformString.c_str());
+ mChannel->getName().c_str(), __func__, seq, eventId, deviceId,
+ inputEventSourceToString(source).c_str(), displayId,
+ MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags,
+ metaState, buttonState, motionClassificationToString(classification), xPrecision,
+ yPrecision, downTime, eventTime, pointerCount, transformString.c_str());
}
if (!seq) {
@@ -629,6 +680,8 @@
mChannel->getName().c_str(), toString(hasFocus));
ATRACE_NAME(message.c_str());
}
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: seq=%u, id=%d, hasFocus=%s",
+ mChannel->getName().c_str(), __func__, seq, eventId, toString(hasFocus));
InputMessage msg;
msg.header.type = InputMessage::Type::FOCUS;
@@ -646,6 +699,9 @@
mChannel->getName().c_str(), toString(pointerCaptureEnabled));
ATRACE_NAME(message.c_str());
}
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: seq=%u, id=%d, pointerCaptureEnabled=%s",
+ mChannel->getName().c_str(), __func__, seq, eventId, toString(pointerCaptureEnabled));
InputMessage msg;
msg.header.type = InputMessage::Type::CAPTURE;
@@ -663,6 +719,9 @@
mChannel->getName().c_str(), x, y, toString(isExiting));
ATRACE_NAME(message.c_str());
}
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: seq=%u, id=%d, x=%f, y=%f, isExiting=%s",
+ mChannel->getName().c_str(), __func__, seq, eventId, x, y, toString(isExiting));
InputMessage msg;
msg.header.type = InputMessage::Type::DRAG;
@@ -681,6 +740,9 @@
mChannel->getName().c_str(), toString(isInTouchMode));
ATRACE_NAME(message.c_str());
}
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: seq=%u, id=%d, isInTouchMode=%s",
+ mChannel->getName().c_str(), __func__, seq, eventId, toString(isInTouchMode));
InputMessage msg;
msg.header.type = InputMessage::Type::TOUCH_MODE;
@@ -691,16 +753,18 @@
}
android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
- }
-
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
if (result) {
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: %s",
+ mChannel->getName().c_str(), __func__, strerror(result));
return android::base::Error(result);
}
if (msg.header.type == InputMessage::Type::FINISHED) {
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: finished: seq=%u, handled=%s",
+ mChannel->getName().c_str(), __func__, msg.header.seq,
+ toString(msg.body.finished.handled));
return Finished{
.seq = msg.header.seq,
.handled = msg.body.finished.handled,
@@ -709,6 +773,8 @@
}
if (msg.header.type == InputMessage::Type::TIMELINE) {
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: timeline: id=%d",
+ mChannel->getName().c_str(), __func__, msg.body.timeline.eventId);
return Timeline{
.inputEventId = msg.body.timeline.eventId,
.graphicsTimeline = msg.body.timeline.graphicsTimeline,
@@ -738,10 +804,9 @@
status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
- mChannel->getName().c_str(), toString(consumeBatches), frameTime);
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
+ mChannel->getName().c_str(), toString(consumeBatches), frameTime);
*outSeq = 0;
*outEvent = nullptr;
@@ -767,10 +832,9 @@
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
if (*outEvent) {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed batch event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
break;
}
}
@@ -786,11 +850,10 @@
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.header.seq;
*outEvent = keyEvent;
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
- break;
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed key event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ break;
}
case InputMessage::Type::MOTION: {
@@ -799,11 +862,10 @@
Batch& batch = mBatches[batchIndex];
if (canAddSample(batch, &mMsg)) {
batch.samples.push_back(mMsg);
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ appended to batch event",
- mChannel->getName().c_str());
- }
- break;
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ appended to batch event",
+ mChannel->getName().c_str());
+ break;
} else if (isPointerEvent(mMsg.body.motion.source) &&
mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
// No need to process events that we are going to cancel anyways
@@ -824,12 +886,11 @@
if (result) {
return result;
}
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed batch event and "
- "deferred current event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
- break;
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed batch event and "
+ "deferred current event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ break;
}
}
@@ -839,10 +900,9 @@
Batch batch;
batch.samples.push_back(mMsg);
mBatches.push_back(batch);
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ started batch event",
- mChannel->getName().c_str());
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ started batch event",
+ mChannel->getName().c_str());
break;
}
@@ -854,10 +914,9 @@
*outSeq = mMsg.header.seq;
*outEvent = motionEvent;
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed motion event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
break;
}
@@ -1074,11 +1133,9 @@
state.recentCoordinatesAreIdentical(id)) {
PointerCoords& msgCoords = msg.body.motion.pointers[i].coords;
const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
- resampleCoords.getX(), resampleCoords.getY(),
- msgCoords.getX(), msgCoords.getY());
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
+ resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(),
+ msgCoords.getY());
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
msgCoords.isResampled = true;
@@ -1099,17 +1156,13 @@
ssize_t index = findTouchState(event->getDeviceId(), event->getSource());
if (index < 0) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, no touch state for device.");
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, no touch state for device.");
return;
}
TouchState& touchState = mTouchStates[index];
if (touchState.historySize < 1) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, no history for device.");
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, no history for device.");
return;
}
@@ -1119,9 +1172,7 @@
for (size_t i = 0; i < pointerCount; i++) {
uint32_t id = event->getPointerId(i);
if (!current->idBits.hasBit(id)) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, missing id %d", id);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, missing id %d", id);
return;
}
}
@@ -1137,9 +1188,8 @@
other = &future;
nsecs_t delta = future.eventTime - current->eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too small: %" PRId64 " ns.",
+ delta);
return;
}
alpha = float(sampleTime - current->eventTime) / delta;
@@ -1149,30 +1199,25 @@
other = touchState.getHistory(1);
nsecs_t delta = current->eventTime - other->eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too small: %" PRId64 " ns.",
+ delta);
return;
} else if (delta > RESAMPLE_MAX_DELTA) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, delta time is too large: %" PRId64 " ns.", delta);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too large: %" PRId64 " ns.",
+ delta);
return;
}
nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION);
if (sampleTime > maxPredict) {
-#if DEBUG_RESAMPLING
- ALOGD("Sample time is too far in the future, adjusting prediction "
- "from %" PRId64 " to %" PRId64 " ns.",
- sampleTime - current->eventTime, maxPredict - current->eventTime);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING,
+ "Sample time is too far in the future, adjusting prediction "
+ "from %" PRId64 " to %" PRId64 " ns.",
+ sampleTime - current->eventTime, maxPredict - current->eventTime);
sampleTime = maxPredict;
}
alpha = float(current->eventTime - sampleTime) / delta;
} else {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, insufficient data.");
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, insufficient data.");
return;
}
@@ -1207,28 +1252,22 @@
PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
const PointerCoords& currentCoords = current->getPointerById(id);
resampledCoords.copyFrom(currentCoords);
- if (other->idBits.hasBit(id)
- && shouldResampleTool(event->getToolType(i))) {
+ if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
const PointerCoords& otherCoords = other->getPointerById(id);
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
- lerp(currentCoords.getX(), otherCoords.getX(), alpha));
+ lerp(currentCoords.getX(), otherCoords.getX(), alpha));
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
- lerp(currentCoords.getY(), otherCoords.getY(), alpha));
+ lerp(currentCoords.getY(), otherCoords.getY(), alpha));
resampledCoords.isResampled = true;
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
- "other (%0.3f, %0.3f), alpha %0.3f",
- id, resampledCoords.getX(), resampledCoords.getY(),
- currentCoords.getX(), currentCoords.getY(),
- otherCoords.getX(), otherCoords.getY(),
- alpha);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING,
+ "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
+ "other (%0.3f, %0.3f), alpha %0.3f",
+ id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
+ currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha);
} else {
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)",
- id, resampledCoords.getX(), resampledCoords.getY(),
- currentCoords.getX(), currentCoords.getY());
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id,
+ resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
+ currentCoords.getY());
}
}
@@ -1241,10 +1280,9 @@
}
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
- mChannel->getName().c_str(), seq, toString(handled));
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+ mChannel->getName().c_str(), seq, toString(handled));
if (!seq) {
ALOGE("Attempted to send a finished signal with sequence number 0.");
@@ -1291,13 +1329,12 @@
status_t InputConsumer::sendTimeline(int32_t inputEventId,
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
- ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
- mChannel->getName().c_str(), inputEventId,
- graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
- graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
+ mChannel->getName().c_str(), inputEventId,
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
InputMessage msg;
msg.header.type = InputMessage::Type::TIMELINE;
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
new file mode 100644
index 0000000..eb75804
--- /dev/null
+++ b/libs/input/InputVerifier.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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_TAG "InputVerifier"
+
+#include <android-base/logging.h>
+#include <input/InputVerifier.h>
+
+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){};
+
+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;
+ }
+
+ 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;
+ }
+ }
+}
+
+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;
+ }
+ }
+};
+
+} // namespace android
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 3f8467d..0b5c7ff 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -16,9 +16,10 @@
#define LOG_TAG "Keyboard"
+#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
-#include <limits.h>
+#include <optional>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
@@ -49,23 +50,25 @@
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
- std::string keyLayoutName;
- if (deviceConfiguration->tryGetProperty("keyboard.layout", keyLayoutName)) {
- status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str());
+ std::optional<std::string> keyLayoutName =
+ deviceConfiguration->getString("keyboard.layout");
+ if (keyLayoutName.has_value()) {
+ status_t status = loadKeyLayout(deviceIdentifier, *keyLayoutName);
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
"it was not found.",
- deviceIdentifier.name.c_str(), keyLayoutName.c_str());
+ deviceIdentifier.name.c_str(), keyLayoutName->c_str());
}
}
- std::string keyCharacterMapName;
- if (deviceConfiguration->tryGetProperty("keyboard.characterMap", keyCharacterMapName)) {
- status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str());
+ std::optional<std::string> keyCharacterMapName =
+ deviceConfiguration->getString("keyboard.characterMap");
+ if (keyCharacterMapName.has_value()) {
+ status_t status = loadKeyCharacterMap(deviceIdentifier, *keyCharacterMapName);
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
- deviceIdentifier.name.c_str(), keyCharacterMapName.c_str());
+ deviceIdentifier.name.c_str(), keyCharacterMapName->c_str());
}
}
@@ -162,9 +165,7 @@
if (config == nullptr) {
return false;
}
- bool isSpecialFunction = false;
- config->tryGetProperty("keyboard.specialFunction", isSpecialFunction);
- return isSpecialFunction;
+ return config->getBool("keyboard.specialFunction").value_or(false);
}
bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
@@ -177,8 +178,7 @@
}
if (deviceConfiguration) {
- bool builtIn = false;
- if (deviceConfiguration->tryGetProperty("keyboard.builtIn", builtIn) && builtIn) {
+ if (deviceConfiguration->getBool("keyboard.builtIn").value_or(false)) {
return true;
}
}
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 7d11ef2..b4151c6 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -35,7 +35,6 @@
namespace android {
namespace {
-const char DEFAULT_MODEL_PATH[] = "/system/etc/motion_predictor_model.fb";
const int64_t PREDICTION_INTERVAL_NANOS =
12500000 / 3; // TODO(b/266747937): Get this from the model.
@@ -62,48 +61,58 @@
// --- MotionPredictor ---
-MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath,
+MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
std::function<bool()> checkMotionPredictionEnabled)
: mPredictionTimestampOffsetNanos(predictionTimestampOffsetNanos),
- mModelPath(modelPath == nullptr ? DEFAULT_MODEL_PATH : modelPath),
mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)) {}
-void MotionPredictor::record(const MotionEvent& event) {
+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.
+ LOG(ERROR) << "Inconsistent event stream: last event is " << *mLastEvent << ", but "
+ << __func__ << " is called with " << event;
+ return android::base::Error()
+ << "Inconsistent event stream: still have an active gesture from device "
+ << mLastEvent->getDeviceId() << ", but received " << event;
+ }
if (!isPredictionAvailable(event.getDeviceId(), event.getSource())) {
ALOGE("Prediction not supported for device %d's %s source", event.getDeviceId(),
inputEventSourceToString(event.getSource()).c_str());
- return;
+ return {};
}
// Initialise the model now that it's likely to be used.
if (!mModel) {
- mModel = TfLiteMotionPredictorModel::create(mModelPath.c_str());
+ mModel = TfLiteMotionPredictorModel::create();
}
- TfLiteMotionPredictorBuffers& buffers =
- mDeviceBuffers.try_emplace(event.getDeviceId(), mModel->inputLength()).first->second;
+ if (mBuffers == nullptr) {
+ mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+ }
const int32_t action = event.getActionMasked();
- if (action == AMOTION_EVENT_ACTION_UP) {
+ if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
ALOGD_IF(isDebug(), "End of event stream");
- buffers.reset();
- return;
+ mBuffers->reset();
+ mLastEvent.reset();
+ return {};
} else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) {
ALOGD_IF(isDebug(), "Skipping unsupported %s action",
MotionEvent::actionToString(action).c_str());
- return;
+ return {};
}
if (event.getPointerCount() != 1) {
ALOGD_IF(isDebug(), "Prediction not supported for multiple pointers");
- return;
+ return {};
}
const int32_t toolType = event.getPointerProperties(0)->toolType;
if (toolType != AMOTION_EVENT_TOOL_TYPE_STYLUS) {
ALOGD_IF(isDebug(), "Prediction not supported for non-stylus tool: %s",
motionToolTypeToString(toolType));
- return;
+ return {};
}
for (size_t i = 0; i <= event.getHistorySize(); ++i) {
@@ -111,100 +120,98 @@
continue;
}
const PointerCoords* coords = event.getHistoricalRawPointerCoords(0, i);
- buffers.pushSample(event.getHistoricalEventTime(i),
- {
- .position.x = coords->getAxisValue(AMOTION_EVENT_AXIS_X),
- .position.y = coords->getAxisValue(AMOTION_EVENT_AXIS_Y),
- .pressure = event.getHistoricalPressure(0, i),
- .tilt = event.getHistoricalAxisValue(AMOTION_EVENT_AXIS_TILT, 0,
- i),
- .orientation = event.getHistoricalOrientation(0, i),
- });
+ mBuffers->pushSample(event.getHistoricalEventTime(i),
+ {
+ .position.x = coords->getAxisValue(AMOTION_EVENT_AXIS_X),
+ .position.y = coords->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ .pressure = event.getHistoricalPressure(0, i),
+ .tilt = event.getHistoricalAxisValue(AMOTION_EVENT_AXIS_TILT,
+ 0, i),
+ .orientation = event.getHistoricalOrientation(0, i),
+ });
}
- mLastEvents.try_emplace(event.getDeviceId())
- .first->second.copyFrom(&event, /*keepHistory=*/false);
+ if (!mLastEvent) {
+ mLastEvent = MotionEvent();
+ }
+ mLastEvent->copyFrom(&event, /*keepHistory=*/false);
+ return {};
}
-std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict(nsecs_t timestamp) {
- std::vector<std::unique_ptr<MotionEvent>> predictions;
-
- for (const auto& [deviceId, buffer] : mDeviceBuffers) {
- if (!buffer.isReady()) {
- continue;
- }
-
- LOG_ALWAYS_FATAL_IF(!mModel);
- buffer.copyTo(*mModel);
- LOG_ALWAYS_FATAL_IF(!mModel->invoke());
-
- // Read out the predictions.
- const std::span<const float> predictedR = mModel->outputR();
- const std::span<const float> predictedPhi = mModel->outputPhi();
- const std::span<const float> predictedPressure = mModel->outputPressure();
-
- TfLiteMotionPredictorSample::Point axisFrom = buffer.axisFrom().position;
- TfLiteMotionPredictorSample::Point axisTo = buffer.axisTo().position;
-
- if (isDebug()) {
- ALOGD("deviceId: %d", deviceId);
- ALOGD("axisFrom: %f, %f", axisFrom.x, axisFrom.y);
- ALOGD("axisTo: %f, %f", axisTo.x, axisTo.y);
- ALOGD("mInputR: %s", base::Join(mModel->inputR(), ", ").c_str());
- ALOGD("mInputPhi: %s", base::Join(mModel->inputPhi(), ", ").c_str());
- ALOGD("mInputPressure: %s", base::Join(mModel->inputPressure(), ", ").c_str());
- ALOGD("mInputTilt: %s", base::Join(mModel->inputTilt(), ", ").c_str());
- ALOGD("mInputOrientation: %s", base::Join(mModel->inputOrientation(), ", ").c_str());
- ALOGD("predictedR: %s", base::Join(predictedR, ", ").c_str());
- ALOGD("predictedPhi: %s", base::Join(predictedPhi, ", ").c_str());
- ALOGD("predictedPressure: %s", base::Join(predictedPressure, ", ").c_str());
- }
-
- const MotionEvent& event = mLastEvents[deviceId];
- bool hasPredictions = false;
- std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>();
- int64_t predictionTime = buffer.lastTimestamp();
- 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]);
- // TODO(b/266747654): Stop predictions if confidence is < some threshold.
-
- ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.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_PRESSURE, predictedPressure[i]);
-
- predictionTime += PREDICTION_INTERVAL_NANOS;
- if (i == 0) {
- hasPredictions = true;
- prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
- event.getDisplayId(), INVALID_HMAC,
- AMOTION_EVENT_ACTION_MOVE, event.getActionButton(),
- event.getFlags(), event.getEdgeFlags(), event.getMetaState(),
- event.getButtonState(), event.getClassification(),
- event.getTransform(), event.getXPrecision(),
- event.getYPrecision(), event.getRawXCursorPosition(),
- event.getRawYCursorPosition(), event.getRawTransform(),
- event.getDownTime(), predictionTime, event.getPointerCount(),
- event.getPointerProperties(), &coords);
- } else {
- prediction->addSample(predictionTime, &coords);
- }
-
- axisFrom = axisTo;
- axisTo = point;
- }
- // TODO(b/266747511): Interpolate to futureTime?
- if (hasPredictions) {
- predictions.push_back(std::move(prediction));
- }
+std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) {
+ if (mBuffers == nullptr || !mBuffers->isReady()) {
+ return nullptr;
}
- return predictions;
+
+ LOG_ALWAYS_FATAL_IF(!mModel);
+ mBuffers->copyTo(*mModel);
+ LOG_ALWAYS_FATAL_IF(!mModel->invoke());
+
+ // Read out the predictions.
+ const std::span<const float> predictedR = mModel->outputR();
+ const std::span<const float> predictedPhi = mModel->outputPhi();
+ const std::span<const float> predictedPressure = mModel->outputPressure();
+
+ TfLiteMotionPredictorSample::Point axisFrom = mBuffers->axisFrom().position;
+ TfLiteMotionPredictorSample::Point axisTo = mBuffers->axisTo().position;
+
+ if (isDebug()) {
+ ALOGD("axisFrom: %f, %f", axisFrom.x, axisFrom.y);
+ ALOGD("axisTo: %f, %f", axisTo.x, axisTo.y);
+ ALOGD("mInputR: %s", base::Join(mModel->inputR(), ", ").c_str());
+ ALOGD("mInputPhi: %s", base::Join(mModel->inputPhi(), ", ").c_str());
+ ALOGD("mInputPressure: %s", base::Join(mModel->inputPressure(), ", ").c_str());
+ ALOGD("mInputTilt: %s", base::Join(mModel->inputTilt(), ", ").c_str());
+ ALOGD("mInputOrientation: %s", base::Join(mModel->inputOrientation(), ", ").c_str());
+ ALOGD("predictedR: %s", base::Join(predictedR, ", ").c_str());
+ ALOGD("predictedPhi: %s", base::Join(predictedPhi, ", ").c_str());
+ ALOGD("predictedPressure: %s", base::Join(predictedPressure, ", ").c_str());
+ }
+
+ LOG_ALWAYS_FATAL_IF(!mLastEvent);
+ const MotionEvent& event = *mLastEvent;
+ bool hasPredictions = false;
+ std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>();
+ int64_t predictionTime = mBuffers->lastTimestamp();
+ 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]);
+ // TODO(b/266747654): Stop predictions if confidence is < some threshold.
+
+ ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.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_PRESSURE, predictedPressure[i]);
+
+ predictionTime += PREDICTION_INTERVAL_NANOS;
+ if (i == 0) {
+ hasPredictions = true;
+ prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
+ event.getDisplayId(), INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE,
+ event.getActionButton(), event.getFlags(), event.getEdgeFlags(),
+ event.getMetaState(), event.getButtonState(),
+ event.getClassification(), event.getTransform(),
+ event.getXPrecision(), event.getYPrecision(),
+ event.getRawXCursorPosition(), event.getRawYCursorPosition(),
+ event.getRawTransform(), event.getDownTime(), predictionTime,
+ event.getPointerCount(), event.getPointerProperties(), &coords);
+ } else {
+ prediction->addSample(predictionTime, &coords);
+ }
+
+ axisFrom = axisTo;
+ axisTo = point;
+ }
+ // TODO(b/266747511): Interpolate to futureTime?
+ if (!hasPredictions) {
+ return nullptr;
+ }
+ return prediction;
}
bool MotionPredictor::isPredictionAvailable(int32_t /*deviceId*/, int32_t source) {
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index ed9ac9f..548f894 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "PropertyMap"
+#include <cstdlib>
+
#include <input/PropertyMap.h>
#include <log/log.h>
@@ -44,62 +46,76 @@
mProperties.emplace(key, value);
}
+std::unordered_set<std::string> PropertyMap::getKeysWithPrefix(const std::string& prefix) const {
+ std::unordered_set<std::string> keys;
+ for (const auto& [key, _] : mProperties) {
+ if (key.starts_with(prefix)) {
+ keys.insert(key);
+ }
+ }
+ return keys;
+}
+
bool PropertyMap::hasProperty(const std::string& key) const {
return mProperties.find(key) != mProperties.end();
}
-bool PropertyMap::tryGetProperty(const std::string& key, std::string& outValue) const {
+std::optional<std::string> PropertyMap::getString(const std::string& key) const {
auto it = mProperties.find(key);
- if (it == mProperties.end()) {
- return false;
- }
-
- outValue = it->second;
- return true;
+ return it != mProperties.end() ? std::make_optional(it->second) : std::nullopt;
}
-bool PropertyMap::tryGetProperty(const std::string& key, bool& outValue) const {
- int32_t intValue;
- if (!tryGetProperty(key, intValue)) {
- return false;
- }
-
- outValue = intValue;
- return true;
+std::optional<bool> PropertyMap::getBool(const std::string& key) const {
+ std::optional<int32_t> intValue = getInt(key);
+ return intValue.has_value() ? std::make_optional(*intValue != 0) : std::nullopt;
}
-bool PropertyMap::tryGetProperty(const std::string& key, int32_t& outValue) const {
- std::string stringValue;
- if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
- return false;
+std::optional<int32_t> PropertyMap::getInt(const std::string& key) const {
+ std::optional<std::string> stringValue = getString(key);
+ if (!stringValue.has_value() || stringValue->length() == 0) {
+ return std::nullopt;
}
char* end;
- int32_t value = static_cast<int32_t>(strtol(stringValue.c_str(), &end, 10));
+ int32_t value = static_cast<int32_t>(strtol(stringValue->c_str(), &end, 10));
if (*end != '\0') {
ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.c_str(),
- stringValue.c_str());
- return false;
+ stringValue->c_str());
+ return std::nullopt;
}
- outValue = value;
- return true;
+ return value;
}
-bool PropertyMap::tryGetProperty(const std::string& key, float& outValue) const {
- std::string stringValue;
- if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
- return false;
+std::optional<float> PropertyMap::getFloat(const std::string& key) const {
+ std::optional<std::string> stringValue = getString(key);
+ if (!stringValue.has_value() || stringValue->length() == 0) {
+ return std::nullopt;
}
char* end;
- float value = strtof(stringValue.c_str(), &end);
+ float value = strtof(stringValue->c_str(), &end);
if (*end != '\0') {
ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.c_str(),
- stringValue.c_str());
- return false;
+ stringValue->c_str());
+ return std::nullopt;
}
- outValue = value;
- return true;
+ return value;
+}
+
+std::optional<double> PropertyMap::getDouble(const std::string& key) const {
+ std::optional<std::string> stringValue = getString(key);
+ if (!stringValue.has_value() || stringValue->length() == 0) {
+ return std::nullopt;
+ }
+
+ char* end;
+ double value = strtod(stringValue->c_str(), &end);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected a double.", key.c_str(),
+ stringValue->c_str());
+ return std::nullopt;
+ }
+ return value;
}
void PropertyMap::addAll(const PropertyMap* map) {
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
old mode 100755
new mode 100644
index d985dc1..6299ca8
--- a/libs/input/PropertyMap_fuzz.cpp
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -29,8 +29,7 @@
},
[](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void {
std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- std::string out;
- propertyMap.tryGetProperty(key, out);
+ propertyMap.getString(key);
},
[](FuzzedDataProvider* dataProvider, android::PropertyMap& /*unused*/) -> void {
TemporaryFile tf;
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 10510d6..3b061d1 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -30,6 +30,7 @@
#include <type_traits>
#include <utility>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/mapped_file.h>
#define ATRACE_TAG ATRACE_TAG_INPUT
@@ -60,6 +61,27 @@
constexpr char OUTPUT_PHI[] = "phi";
constexpr char OUTPUT_PRESSURE[] = "pressure";
+// Ideally, we would just use std::filesystem::exists here, but it requires libc++fs, which causes
+// build issues in other parts of the system.
+#if defined(__ANDROID__)
+bool fileExists(const char* filename) {
+ struct stat buffer;
+ return stat(filename, &buffer) == 0;
+}
+#endif
+
+std::string getModelPath() {
+#if defined(__ANDROID__)
+ static const char* oemModel = "/vendor/etc/motion_predictor_model.fb";
+ if (fileExists(oemModel)) {
+ return oemModel;
+ }
+ return "/system/etc/motion_predictor_model.fb";
+#else
+ return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
+#endif
+}
+
// A TFLite ErrorReporter that logs to logcat.
class LoggingErrorReporter : public tflite::ErrorReporter {
public:
@@ -206,9 +228,9 @@
mInputOrientation.pushBack(orientation);
}
-std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create(
- const char* modelPath) {
- const int fd = open(modelPath, O_RDONLY);
+std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create() {
+ const std::string modelPath = getModelPath();
+ android::base::unique_fd fd(open(modelPath.c_str(), O_RDONLY));
if (fd == -1) {
PLOG(FATAL) << "Could not read model from " << modelPath;
}
@@ -223,9 +245,6 @@
if (!modelBuffer) {
PLOG(FATAL) << "Failed to mmap model";
}
- if (close(fd) == -1) {
- PLOG(FATAL) << "Failed to close model fd";
- }
return std::unique_ptr<TfLiteMotionPredictorModel>(
new TfLiteMotionPredictorModel(std::move(modelBuffer)));
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index c9393f4..6d7d561 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -43,13 +43,13 @@
void TouchVideoFrame::rotate(ui::Rotation orientation) {
switch (orientation) {
case ui::ROTATION_90:
- rotateQuarterTurn(false /*clockwise*/);
+ rotateQuarterTurn(/*clockwise=*/false);
break;
case ui::ROTATION_180:
rotate180();
break;
case ui::ROTATION_270:
- rotateQuarterTurn(true /*clockwise*/);
+ rotateQuarterTurn(/*clockwise=*/true);
break;
case ui::ROTATION_0:
// No need to rotate if there's no rotation.
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 3632914..8551e5f 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -157,10 +157,10 @@
std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) {
- createdStrategy = createStrategy(mOverrideStrategy, isDifferentialAxis /* deltaValues */);
+ createdStrategy = createStrategy(mOverrideStrategy, /*deltaValues=*/isDifferentialAxis);
} else {
createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
- isDifferentialAxis /* deltaValues */);
+ /*deltaValues=*/isDifferentialAxis);
}
LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
@@ -495,7 +495,7 @@
}
ALOGD_IF(DEBUG_STRATEGY, " - a=%s",
- matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
+ matrixToString(&a[0][0], m, n, /*rowMajor=*/false).c_str());
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
float q[n][m]; // orthonormal basis, column-major order
@@ -527,8 +527,8 @@
}
}
if (DEBUG_STRATEGY) {
- ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
- ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
+ ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, /*rowMajor=*/false).c_str());
+ ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, /*rowMajor=*/true).c_str());
// calculate QR, if we factored A correctly then QR should equal A
float qr[n][m];
@@ -540,7 +540,7 @@
}
}
}
- ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
+ ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, /*rowMajor=*/false).c_str());
}
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
new file mode 100644
index 0000000..3c1f2b6
--- /dev/null
+++ b/libs/input/VirtualInputDevice.cpp
@@ -0,0 +1,378 @@
+/*
+ * 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_TAG "VirtualInputDevice"
+
+#include <android/input.h>
+#include <android/keycodes.h>
+#include <fcntl.h>
+#include <input/Input.h>
+#include <input/VirtualInputDevice.h>
+#include <linux/uinput.h>
+#include <math.h>
+#include <utils/Log.h>
+
+#include <map>
+#include <string>
+
+using android::base::unique_fd;
+
+/**
+ * Log debug messages about native virtual input devices.
+ * Enable this via "adb shell setprop log.tag.VirtualInputDevice DEBUG"
+ */
+static bool isDebug() {
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
+}
+
+namespace android {
+VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {}
+VirtualInputDevice::~VirtualInputDevice() {
+ ioctl(mFd, UI_DEV_DESTROY);
+}
+
+bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t value) {
+ struct input_event ev = {.type = type, .code = code, .value = value};
+ return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev);
+}
+
+/** Utility method to write keyboard key events or mouse button events. */
+bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAction,
+ const std::map<int, int>& evKeyCodeMapping,
+ const std::map<int, UinputAction>& actionMapping) {
+ auto evKeyCodeIterator = evKeyCodeMapping.find(androidCode);
+ if (evKeyCodeIterator == evKeyCodeMapping.end()) {
+ ALOGE("Unsupported native EV keycode for android code %d", androidCode);
+ return false;
+ }
+ auto actionIterator = actionMapping.find(androidAction);
+ if (actionIterator == actionMapping.end()) {
+ return false;
+ }
+ if (!writeInputEvent(EV_KEY, static_cast<uint16_t>(evKeyCodeIterator->second),
+ static_cast<int32_t>(actionIterator->second))) {
+ return false;
+ }
+ if (!writeInputEvent(EV_SYN, SYN_REPORT, 0)) {
+ return false;
+ }
+ return true;
+}
+
+// --- VirtualKeyboard ---
+const std::map<int, UinputAction> VirtualKeyboard::KEY_ACTION_MAPPING = {
+ {AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS},
+ {AKEY_EVENT_ACTION_UP, UinputAction::RELEASE},
+};
+// Keycode mapping from https://source.android.com/devices/input/keyboard-devices
+const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = {
+ {AKEYCODE_0, KEY_0},
+ {AKEYCODE_1, KEY_1},
+ {AKEYCODE_2, KEY_2},
+ {AKEYCODE_3, KEY_3},
+ {AKEYCODE_4, KEY_4},
+ {AKEYCODE_5, KEY_5},
+ {AKEYCODE_6, KEY_6},
+ {AKEYCODE_7, KEY_7},
+ {AKEYCODE_8, KEY_8},
+ {AKEYCODE_9, KEY_9},
+ {AKEYCODE_A, KEY_A},
+ {AKEYCODE_B, KEY_B},
+ {AKEYCODE_C, KEY_C},
+ {AKEYCODE_D, KEY_D},
+ {AKEYCODE_E, KEY_E},
+ {AKEYCODE_F, KEY_F},
+ {AKEYCODE_G, KEY_G},
+ {AKEYCODE_H, KEY_H},
+ {AKEYCODE_I, KEY_I},
+ {AKEYCODE_J, KEY_J},
+ {AKEYCODE_K, KEY_K},
+ {AKEYCODE_L, KEY_L},
+ {AKEYCODE_M, KEY_M},
+ {AKEYCODE_N, KEY_N},
+ {AKEYCODE_O, KEY_O},
+ {AKEYCODE_P, KEY_P},
+ {AKEYCODE_Q, KEY_Q},
+ {AKEYCODE_R, KEY_R},
+ {AKEYCODE_S, KEY_S},
+ {AKEYCODE_T, KEY_T},
+ {AKEYCODE_U, KEY_U},
+ {AKEYCODE_V, KEY_V},
+ {AKEYCODE_W, KEY_W},
+ {AKEYCODE_X, KEY_X},
+ {AKEYCODE_Y, KEY_Y},
+ {AKEYCODE_Z, KEY_Z},
+ {AKEYCODE_GRAVE, KEY_GRAVE},
+ {AKEYCODE_MINUS, KEY_MINUS},
+ {AKEYCODE_EQUALS, KEY_EQUAL},
+ {AKEYCODE_LEFT_BRACKET, KEY_LEFTBRACE},
+ {AKEYCODE_RIGHT_BRACKET, KEY_RIGHTBRACE},
+ {AKEYCODE_BACKSLASH, KEY_BACKSLASH},
+ {AKEYCODE_SEMICOLON, KEY_SEMICOLON},
+ {AKEYCODE_APOSTROPHE, KEY_APOSTROPHE},
+ {AKEYCODE_COMMA, KEY_COMMA},
+ {AKEYCODE_PERIOD, KEY_DOT},
+ {AKEYCODE_SLASH, KEY_SLASH},
+ {AKEYCODE_ALT_LEFT, KEY_LEFTALT},
+ {AKEYCODE_ALT_RIGHT, KEY_RIGHTALT},
+ {AKEYCODE_CTRL_LEFT, KEY_LEFTCTRL},
+ {AKEYCODE_CTRL_RIGHT, KEY_RIGHTCTRL},
+ {AKEYCODE_SHIFT_LEFT, KEY_LEFTSHIFT},
+ {AKEYCODE_SHIFT_RIGHT, KEY_RIGHTSHIFT},
+ {AKEYCODE_META_LEFT, KEY_LEFTMETA},
+ {AKEYCODE_META_RIGHT, KEY_RIGHTMETA},
+ {AKEYCODE_CAPS_LOCK, KEY_CAPSLOCK},
+ {AKEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK},
+ {AKEYCODE_NUM_LOCK, KEY_NUMLOCK},
+ {AKEYCODE_ENTER, KEY_ENTER},
+ {AKEYCODE_TAB, KEY_TAB},
+ {AKEYCODE_SPACE, KEY_SPACE},
+ {AKEYCODE_DPAD_DOWN, KEY_DOWN},
+ {AKEYCODE_DPAD_UP, KEY_UP},
+ {AKEYCODE_DPAD_LEFT, KEY_LEFT},
+ {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
+ {AKEYCODE_MOVE_END, KEY_END},
+ {AKEYCODE_MOVE_HOME, KEY_HOME},
+ {AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN},
+ {AKEYCODE_PAGE_UP, KEY_PAGEUP},
+ {AKEYCODE_DEL, KEY_BACKSPACE},
+ {AKEYCODE_FORWARD_DEL, KEY_DELETE},
+ {AKEYCODE_INSERT, KEY_INSERT},
+ {AKEYCODE_ESCAPE, KEY_ESC},
+ {AKEYCODE_BREAK, KEY_PAUSE},
+ {AKEYCODE_F1, KEY_F1},
+ {AKEYCODE_F2, KEY_F2},
+ {AKEYCODE_F3, KEY_F3},
+ {AKEYCODE_F4, KEY_F4},
+ {AKEYCODE_F5, KEY_F5},
+ {AKEYCODE_F6, KEY_F6},
+ {AKEYCODE_F7, KEY_F7},
+ {AKEYCODE_F8, KEY_F8},
+ {AKEYCODE_F9, KEY_F9},
+ {AKEYCODE_F10, KEY_F10},
+ {AKEYCODE_F11, KEY_F11},
+ {AKEYCODE_F12, KEY_F12},
+ {AKEYCODE_BACK, KEY_BACK},
+ {AKEYCODE_FORWARD, KEY_FORWARD},
+ {AKEYCODE_NUMPAD_1, KEY_KP1},
+ {AKEYCODE_NUMPAD_2, KEY_KP2},
+ {AKEYCODE_NUMPAD_3, KEY_KP3},
+ {AKEYCODE_NUMPAD_4, KEY_KP4},
+ {AKEYCODE_NUMPAD_5, KEY_KP5},
+ {AKEYCODE_NUMPAD_6, KEY_KP6},
+ {AKEYCODE_NUMPAD_7, KEY_KP7},
+ {AKEYCODE_NUMPAD_8, KEY_KP8},
+ {AKEYCODE_NUMPAD_9, KEY_KP9},
+ {AKEYCODE_NUMPAD_0, KEY_KP0},
+ {AKEYCODE_NUMPAD_ADD, KEY_KPPLUS},
+ {AKEYCODE_NUMPAD_SUBTRACT, KEY_KPMINUS},
+ {AKEYCODE_NUMPAD_MULTIPLY, KEY_KPASTERISK},
+ {AKEYCODE_NUMPAD_DIVIDE, KEY_KPSLASH},
+ {AKEYCODE_NUMPAD_DOT, KEY_KPDOT},
+ {AKEYCODE_NUMPAD_ENTER, KEY_KPENTER},
+ {AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
+ {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
+};
+VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
+VirtualKeyboard::~VirtualKeyboard() {}
+
+bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction) {
+ return writeEvKeyEvent(androidKeyCode, androidAction, KEY_CODE_MAPPING, KEY_ACTION_MAPPING);
+}
+
+// --- VirtualDpad ---
+// Dpad keycode mapping from https://source.android.com/devices/input/keyboard-devices
+const std::map<int, int> VirtualDpad::DPAD_KEY_CODE_MAPPING = {
+ // clang-format off
+ {AKEYCODE_DPAD_DOWN, KEY_DOWN},
+ {AKEYCODE_DPAD_UP, KEY_UP},
+ {AKEYCODE_DPAD_LEFT, KEY_LEFT},
+ {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
+ {AKEYCODE_DPAD_CENTER, KEY_SELECT},
+ {AKEYCODE_BACK, KEY_BACK},
+ // clang-format on
+};
+
+VirtualDpad::VirtualDpad(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
+
+VirtualDpad::~VirtualDpad() {}
+
+bool VirtualDpad::writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction) {
+ return writeEvKeyEvent(androidKeyCode, androidAction, DPAD_KEY_CODE_MAPPING,
+ VirtualKeyboard::KEY_ACTION_MAPPING);
+}
+
+// --- VirtualMouse ---
+const std::map<int, UinputAction> VirtualMouse::BUTTON_ACTION_MAPPING = {
+ {AMOTION_EVENT_ACTION_BUTTON_PRESS, UinputAction::PRESS},
+ {AMOTION_EVENT_ACTION_BUTTON_RELEASE, UinputAction::RELEASE},
+};
+
+// Button code mapping from https://source.android.com/devices/input/touch-devices
+const std::map<int, int> VirtualMouse::BUTTON_CODE_MAPPING = {
+ // clang-format off
+ {AMOTION_EVENT_BUTTON_PRIMARY, BTN_LEFT},
+ {AMOTION_EVENT_BUTTON_SECONDARY, BTN_RIGHT},
+ {AMOTION_EVENT_BUTTON_TERTIARY, BTN_MIDDLE},
+ {AMOTION_EVENT_BUTTON_BACK, BTN_BACK},
+ {AMOTION_EVENT_BUTTON_FORWARD, BTN_FORWARD},
+ // clang-format on
+};
+
+VirtualMouse::VirtualMouse(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
+
+VirtualMouse::~VirtualMouse() {}
+
+bool VirtualMouse::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction) {
+ return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING,
+ BUTTON_ACTION_MAPPING);
+}
+
+bool VirtualMouse::writeRelativeEvent(float relativeX, float relativeY) {
+ return writeInputEvent(EV_REL, REL_X, relativeX) && writeInputEvent(EV_REL, REL_Y, relativeY) &&
+ writeInputEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement) {
+ return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement) &&
+ writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement) &&
+ writeInputEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+// --- VirtualTouchscreen ---
+const std::map<int, UinputAction> VirtualTouchscreen::TOUCH_ACTION_MAPPING = {
+ {AMOTION_EVENT_ACTION_DOWN, UinputAction::PRESS},
+ {AMOTION_EVENT_ACTION_UP, UinputAction::RELEASE},
+ {AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE},
+ {AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL},
+};
+// Tool type mapping from https://source.android.com/devices/input/touch-devices
+const std::map<int, int> VirtualTouchscreen::TOOL_TYPE_MAPPING = {
+ {AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER},
+ {AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM},
+};
+
+VirtualTouchscreen::VirtualTouchscreen(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
+
+VirtualTouchscreen::~VirtualTouchscreen() {}
+
+bool VirtualTouchscreen::isValidPointerId(int32_t pointerId, UinputAction uinputAction) {
+ if (pointerId < -1 || pointerId >= (int)MAX_POINTERS) {
+ ALOGE("Virtual touch event has invalid pointer id %d; value must be between -1 and %zu",
+ pointerId, MAX_POINTERS - 0);
+ return false;
+ }
+
+ if (uinputAction == UinputAction::PRESS && mActivePointers.test(pointerId)) {
+ ALOGE("Repetitive action DOWN event received on a pointer %d that is already down.",
+ pointerId);
+ return false;
+ }
+ if (uinputAction == UinputAction::RELEASE && !mActivePointers.test(pointerId)) {
+ ALOGE("PointerId %d action UP received with no prior action DOWN on touchscreen %d.",
+ pointerId, mFd.get());
+ return false;
+ }
+ return true;
+}
+
+bool VirtualTouchscreen::writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action,
+ float locationX, float locationY, float pressure,
+ float majorAxisSize) {
+ auto actionIterator = TOUCH_ACTION_MAPPING.find(action);
+ if (actionIterator == TOUCH_ACTION_MAPPING.end()) {
+ return false;
+ }
+ UinputAction uinputAction = actionIterator->second;
+ if (!isValidPointerId(pointerId, uinputAction)) {
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_MT_SLOT, pointerId)) {
+ return false;
+ }
+ auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
+ if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_MT_TOOL_TYPE,
+ static_cast<int32_t>(toolTypeIterator->second))) {
+ return false;
+ }
+ if (uinputAction == UinputAction::PRESS && !handleTouchDown(pointerId)) {
+ return false;
+ }
+ if (uinputAction == UinputAction::RELEASE && !handleTouchUp(pointerId)) {
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_X, locationX)) {
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_Y, locationY)) {
+ return false;
+ }
+ if (!isnan(pressure)) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_PRESSURE, pressure)) {
+ return false;
+ }
+ }
+ if (!isnan(majorAxisSize)) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize)) {
+ return false;
+ }
+ }
+ return writeInputEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+bool VirtualTouchscreen::handleTouchUp(int32_t pointerId) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1))) {
+ return false;
+ }
+ // When a pointer is no longer in touch, remove the pointer id from the corresponding
+ // entry in the unreleased touches map.
+ mActivePointers.reset(pointerId);
+ ALOGD_IF(isDebug(), "Pointer %d erased from the touchscreen %d", pointerId, mFd.get());
+
+ // Only sends the BTN UP event when there's no pointers on the touchscreen.
+ if (mActivePointers.none()) {
+ if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE))) {
+ return false;
+ }
+ ALOGD_IF(isDebug(), "No pointers on touchscreen %d, BTN UP event sent.", mFd.get());
+ }
+ return true;
+}
+
+bool VirtualTouchscreen::handleTouchDown(int32_t pointerId) {
+ // When a new pointer is down on the touchscreen, add the pointer id in the corresponding
+ // entry in the unreleased touches map.
+ if (mActivePointers.none()) {
+ // Only sends the BTN Down event when the first pointer on the touchscreen is down.
+ if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS))) {
+ return false;
+ }
+ ALOGD_IF(isDebug(), "First pointer %d down under touchscreen %d, BTN DOWN event sent",
+ pointerId, mFd.get());
+ }
+
+ mActivePointers.set(pointerId);
+ ALOGD_IF(isDebug(), "Added pointer %d under touchscreen %d in the map", pointerId, mFd.get());
+ if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId))) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace android
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 37faf91..42bdf57 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -43,6 +43,13 @@
"-Werror",
"-Wno-unused-parameter",
],
+ sanitize: {
+ undefined: true,
+ all_undefined: true,
+ diag: {
+ undefined: true,
+ },
+ },
shared_libs: [
"libbase",
"libbinder",
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 8a6e983..2132dc1 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -29,6 +29,8 @@
// Default display id.
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
class BaseTest : public testing::Test {
protected:
static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
@@ -235,102 +237,110 @@
static constexpr float RAW_X_OFFSET = 12;
static constexpr float RAW_Y_OFFSET = -41.1;
+ void SetUp() override;
+
int32_t mId;
ui::Transform mTransform;
ui::Transform mRawTransform;
+ PointerProperties mPointerProperties[2];
+ struct Sample {
+ PointerCoords pointerCoords[2];
+ };
+ std::array<Sample, 3> mSamples{};
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
+void MotionEventTest::SetUp() {
mId = InputEvent::nextId();
mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1});
- PointerProperties pointerProperties[2];
- pointerProperties[0].clear();
- pointerProperties[0].id = 1;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- pointerProperties[1].clear();
- pointerProperties[1].id = 2;
- pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mPointerProperties[0].clear();
+ mPointerProperties[0].id = 1;
+ mPointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerProperties[1].clear();
+ mPointerProperties[1].id = 2;
+ mPointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
- PointerCoords pointerCoords[2];
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
- pointerCoords[0].isResampled = true;
- pointerCoords[1].clear();
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
+ mSamples[0].pointerCoords[0].clear();
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
+ mSamples[0].pointerCoords[0].isResampled = true;
+ mSamples[0].pointerCoords[1].clear();
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
+
+ mSamples[1].pointerCoords[0].clear();
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
+ mSamples[1].pointerCoords[0].isResampled = true;
+ mSamples[1].pointerCoords[1].clear();
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
+ mSamples[1].pointerCoords[1].isResampled = true;
+
+ mSamples[2].pointerCoords[0].clear();
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
+ mSamples[2].pointerCoords[1].clear();
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
+}
+
+void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
- pointerProperties, pointerCoords);
-
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
- pointerCoords[0].isResampled = true;
- pointerCoords[1].clear();
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
- pointerCoords[1].isResampled = true;
- event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
-
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
- pointerCoords[1].clear();
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
- event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
+ mPointerProperties, mSamples[0].pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords);
}
void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
@@ -367,51 +377,65 @@
ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
- ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ // Ensure the underlying PointerCoords are identical.
+ for (int sampleIdx = 0; sampleIdx < 3; sampleIdx++) {
+ for (int pointerIdx = 0; pointerIdx < 2; pointerIdx++) {
+ ASSERT_EQ(mSamples[sampleIdx].pointerCoords[pointerIdx],
+ event->getSamplePointerCoords()[sampleIdx * 2 + pointerIdx]);
+ }
+ }
- ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+ ASSERT_NEAR(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON);
+ ASSERT_NEAR(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON);
- ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0));
- ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0));
- ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1));
- ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1));
- ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0));
- ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1));
+ ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0),
+ EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1),
+ EPSILON);
- ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0));
- ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1));
+ ASSERT_NEAR(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1), EPSILON);
- ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
- ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
+ ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1), EPSILON);
- ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
- ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
+ ASSERT_NEAR(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 210 * X_SCALE, event->getX(0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 220 * X_SCALE, event->getX(1), EPSILON);
+
+ ASSERT_NEAR(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 211 * Y_SCALE, event->getY(0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 221 * Y_SCALE, event->getY(1), EPSILON);
ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -507,7 +531,7 @@
initializeEventWithHistory(&event);
MotionEvent copy;
- copy.copyFrom(&event, true /*keepHistory*/);
+ copy.copyFrom(&event, /*keepHistory=*/true);
ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
}
@@ -517,7 +541,7 @@
initializeEventWithHistory(&event);
MotionEvent copy;
- copy.copyFrom(&event, false /*keepHistory*/);
+ copy.copyFrom(&event, /*keepHistory=*/false);
ASSERT_EQ(event.getPointerCount(), copy.getPointerCount());
ASSERT_EQ(0U, copy.getHistorySize());
@@ -550,10 +574,10 @@
ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
- ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0));
- ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0));
- ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
- ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
+ ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON);
+ ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON);
+ ASSERT_NEAR((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0), EPSILON);
+ ASSERT_NEAR((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0), EPSILON);
ASSERT_EQ(212, event.getPressure(0));
ASSERT_EQ(213, event.getSize(0));
ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -618,12 +642,12 @@
}
MotionEvent event;
ui::Transform identityTransform;
- event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
- INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
- AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
- 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
- identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
+ event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+ INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, /*flags=*/0,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
+ MotionClassification::NONE, identityTransform, /*xPrecision=*/0,
+ /*yPrecision=*/0, /*xCursorPosition=*/3 + RADIUS, /*yCursorPosition=*/2,
+ identityTransform, /*downTime=*/0, /*eventTime=*/0, pointerCount,
pointerProperties, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -791,18 +815,18 @@
// The x and y axes should have the window transform applied.
const auto newPoint = transform.transform(60, 100);
- ASSERT_EQ(newPoint.x, event.getX(0));
- ASSERT_EQ(newPoint.y, event.getY(0));
+ ASSERT_NEAR(newPoint.x, event.getX(0), EPSILON);
+ ASSERT_NEAR(newPoint.y, event.getY(0), EPSILON);
// The raw values should have the display transform applied.
const auto raw = rawTransform.transform(60, 100);
- ASSERT_EQ(raw.x, event.getRawX(0));
- ASSERT_EQ(raw.y, event.getRawY(0));
+ ASSERT_NEAR(raw.x, event.getRawX(0), EPSILON);
+ ASSERT_NEAR(raw.y, event.getRawY(0), EPSILON);
// Relative values should have the window transform applied without any translation.
const auto rel = transformWithoutTranslation(transform, 42, 96);
- ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
+ ASSERT_NEAR(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), EPSILON);
+ ASSERT_NEAR(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), EPSILON);
}
TEST_F(MotionEventTest, Initialize_SetsClassification) {
@@ -824,12 +848,12 @@
ui::Transform identityTransform;
for (MotionClassification classification : classifications) {
- event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
+ event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN,
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
- 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, /*downTime=*/0,
+ /*eventTime=*/0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
}
}
@@ -846,11 +870,11 @@
}
ui::Transform identityTransform;
- event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
+ event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identityTransform,
- 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ /*xCursorPosition=*/280, /*yCursorPosition=*/540, identityTransform,
+ /*downTime=*/0, /*eventTime=*/0, pointerCount, pointerProperties,
pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
@@ -869,4 +893,42 @@
ASSERT_EQ(4, event.getYCursorPosition());
}
+TEST_F(MotionEventTest, CoordinatesAreRoundedAppropriately) {
+ // These are specifically integral values, since we are testing for rounding.
+ const vec2 EXPECTED{400.f, 700.f};
+
+ // Pick a transform such that transforming the point with its inverse and bringing that
+ // back to the original coordinate space results in a non-zero error amount due to the
+ // nature of floating point arithmetics. This can happen when the display is scaled.
+ // For example, the 'adb shell wm size' command can be used to set an override for the
+ // logical display size, which could result in the display being scaled.
+ constexpr float scale = 720.f / 1080.f;
+ ui::Transform transform;
+ transform.set(scale, 0, 0, scale);
+ ASSERT_NE(EXPECTED, transform.transform(transform.inverse().transform(EXPECTED)));
+
+ // Store the inverse-transformed values in the motion event.
+ const vec2 rawCoords = transform.inverse().transform(EXPECTED);
+ PointerCoords pc{};
+ pc.setAxisValue(AMOTION_EVENT_AXIS_X, rawCoords.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_Y, rawCoords.y);
+ PointerProperties pp{};
+ MotionEvent event;
+ event.initialize(InputEvent::nextId(), 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
+ AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
+ AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
+ MotionClassification::NONE, transform, 2.0f, 2.1f, rawCoords.x, rawCoords.y,
+ transform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 1, &pp, &pc);
+
+ // When using the getters from the MotionEvent to obtain the coordinates, the transformed
+ // values should be rounded by an appropriate amount so that they now precisely equal the
+ // original coordinates.
+ ASSERT_EQ(EXPECTED.x, event.getX(0));
+ ASSERT_EQ(EXPECTED.y, event.getY(0));
+ ASSERT_EQ(EXPECTED.x, event.getRawX(0));
+ ASSERT_EQ(EXPECTED.y, event.getRawY(0));
+ ASSERT_EQ(EXPECTED.x, event.getXCursorPosition());
+ ASSERT_EQ(EXPECTED.y, event.getYCursorPosition());
+}
+
} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 70e4fda..5d8b970 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -25,6 +25,8 @@
namespace android {
+constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
class InputPublisherAndConsumerTest : public testing::Test {
protected:
std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
@@ -90,7 +92,7 @@
uint32_t consumeSeq;
InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
ASSERT_EQ(OK, status)
<< "consumer consume should return OK";
@@ -199,7 +201,7 @@
uint32_t consumeSeq;
InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
ASSERT_EQ(OK, status)
<< "consumer consume should return OK";
@@ -226,10 +228,10 @@
EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
- EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
- EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
- EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
- EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
+ EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON);
+ EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON);
+ EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON);
+ EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON);
EXPECT_EQ(rawTransform, motionEvent->getRawTransform());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
@@ -242,10 +244,12 @@
EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
const auto& pc = pointerCoords[i];
- EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i));
- EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i));
- EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i));
- EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i));
+ EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]);
+
+ EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON);
+ EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON);
+ EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON);
+ EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON);
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i));
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i));
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i));
@@ -290,7 +294,7 @@
uint32_t consumeSeq;
InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
ASSERT_EQ(OK, status) << "consumer consume should return OK";
ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
@@ -331,7 +335,7 @@
uint32_t consumeSeq;
InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
ASSERT_EQ(OK, status) << "consumer consume should return OK";
ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
@@ -373,7 +377,7 @@
uint32_t consumeSeq;
InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
ASSERT_EQ(OK, status) << "consumer consume should return OK";
ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
@@ -415,7 +419,7 @@
uint32_t consumeSeq;
InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event);
ASSERT_EQ(OK, status) << "consumer consume should return OK";
ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index ce87c86..c61efbf 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -30,13 +30,6 @@
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
-const char MODEL_PATH[] =
-#if defined(__ANDROID__)
- "/system/etc/motion_predictor_model.fb";
-#else
- "motion_predictor_model.fb";
-#endif
-
constexpr int32_t DOWN = AMOTION_EVENT_ACTION_DOWN;
constexpr int32_t MOVE = AMOTION_EVENT_ACTION_MOVE;
constexpr int32_t UP = AMOTION_EVENT_ACTION_UP;
@@ -73,83 +66,74 @@
}
TEST(MotionPredictorTest, IsPredictionAvailable) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
ASSERT_TRUE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS));
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
TEST(MotionPredictorTest, Offset) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1, MODEL_PATH,
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
[]() { return true /*enable prediction*/; });
predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
predictor.record(getMotionEvent(MOVE, 0, 2, 35ms));
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
- ASSERT_EQ(1u, predicted.size());
- ASSERT_GE(predicted[0]->getEventTime(), 41);
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+ ASSERT_NE(nullptr, predicted);
+ ASSERT_GE(predicted->getEventTime(), 41);
}
TEST(MotionPredictorTest, FollowsGesture) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
// MOVE without a DOWN is ignored.
predictor.record(getMotionEvent(MOVE, 1, 3, 10ms));
- EXPECT_THAT(predictor.predict(20 * NSEC_PER_MSEC), IsEmpty());
+ EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC));
predictor.record(getMotionEvent(DOWN, 2, 5, 20ms));
predictor.record(getMotionEvent(MOVE, 2, 7, 30ms));
predictor.record(getMotionEvent(MOVE, 3, 9, 40ms));
- EXPECT_THAT(predictor.predict(50 * NSEC_PER_MSEC), SizeIs(1));
+ EXPECT_NE(nullptr, predictor.predict(50 * NSEC_PER_MSEC));
predictor.record(getMotionEvent(UP, 4, 11, 50ms));
- EXPECT_THAT(predictor.predict(20 * NSEC_PER_MSEC), IsEmpty());
+ EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC));
}
-TEST(MotionPredictorTest, MultipleDevicesTracked) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+TEST(MotionPredictorTest, MultipleDevicesNotSupported) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
- predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0));
- predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0));
- predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0));
- predictor.record(getMotionEvent(MOVE, 3, 7, 30ms, /*deviceId=*/0));
+ ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 3, 7, 30ms, /*deviceId=*/0)).ok());
- predictor.record(getMotionEvent(DOWN, 100, 300, 0ms, /*deviceId=*/1));
- predictor.record(getMotionEvent(MOVE, 100, 300, 10ms, /*deviceId=*/1));
- predictor.record(getMotionEvent(MOVE, 200, 500, 20ms, /*deviceId=*/1));
- predictor.record(getMotionEvent(MOVE, 300, 700, 30ms, /*deviceId=*/1));
+ ASSERT_FALSE(predictor.record(getMotionEvent(DOWN, 100, 300, 40ms, /*deviceId=*/1)).ok());
+ ASSERT_FALSE(predictor.record(getMotionEvent(MOVE, 100, 300, 50ms, /*deviceId=*/1)).ok());
+}
- {
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
- ASSERT_EQ(2u, predicted.size());
+TEST(MotionPredictorTest, IndividualGesturesFromDifferentDevicesAreSupported) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
+ []() { return true /*enable prediction*/; });
- // Order of the returned vector is not guaranteed.
- std::vector<int32_t> seenDeviceIds;
- for (const auto& prediction : predicted) {
- seenDeviceIds.push_back(prediction->getDeviceId());
- }
- EXPECT_THAT(seenDeviceIds, UnorderedElementsAre(0, 1));
- }
+ ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(UP, 2, 5, 30ms, /*deviceId=*/0)).ok());
- // End the gesture for device 0.
- predictor.record(getMotionEvent(UP, 4, 9, 40ms, /*deviceId=*/0));
- predictor.record(getMotionEvent(MOVE, 400, 900, 40ms, /*deviceId=*/1));
-
- {
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
- ASSERT_EQ(1u, predicted.size());
- ASSERT_EQ(predicted[0]->getDeviceId(), 1);
- }
+ // Now, send a gesture from a different device. Since we have no active gesture, the new gesture
+ // should be processed correctly.
+ ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 100, 300, 40ms, /*deviceId=*/1)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 100, 300, 50ms, /*deviceId=*/1)).ok());
}
TEST(MotionPredictorTest, FlagDisablesPrediction) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return false /*disable prediction*/; });
predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
predictor.record(getMotionEvent(MOVE, 0, 1, 35ms));
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
- ASSERT_EQ(0u, predicted.size());
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+ ASSERT_EQ(nullptr, predicted);
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS));
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp
index 454f2aa..6e76ac1 100644
--- a/libs/input/tests/TfLiteMotionPredictor_test.cpp
+++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp
@@ -21,7 +21,6 @@
#include <iterator>
#include <string>
-#include <android-base/file.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/TfLiteMotionPredictor.h>
@@ -33,14 +32,6 @@
using ::testing::ElementsAre;
using ::testing::FloatNear;
-std::string getModelPath() {
-#if defined(__ANDROID__)
- return "/system/etc/motion_predictor_model.fb";
-#else
- return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
-#endif
-}
-
TEST(TfLiteMotionPredictorTest, BuffersReadiness) {
TfLiteMotionPredictorBuffers buffers(/*inputLength=*/5);
ASSERT_FALSE(buffers.isReady());
@@ -92,8 +83,7 @@
}
TEST(TfLiteMotionPredictorTest, BuffersCopyTo) {
- std::unique_ptr<TfLiteMotionPredictorModel> model =
- TfLiteMotionPredictorModel::create(getModelPath().c_str());
+ std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
TfLiteMotionPredictorBuffers buffers(model->inputLength());
buffers.pushSample(/*timestamp=*/1,
@@ -137,8 +127,7 @@
}
TEST(TfLiteMotionPredictorTest, ModelInputOutputLength) {
- std::unique_ptr<TfLiteMotionPredictorModel> model =
- TfLiteMotionPredictorModel::create(getModelPath().c_str());
+ std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
ASSERT_GT(model->inputLength(), 0u);
const int inputLength = model->inputLength();
@@ -155,8 +144,7 @@
}
TEST(TfLiteMotionPredictorTest, ModelOutput) {
- std::unique_ptr<TfLiteMotionPredictorModel> model =
- TfLiteMotionPredictorModel::create(getModelPath().c_str());
+ std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
TfLiteMotionPredictorBuffers buffers(model->inputLength());
buffers.pushSample(/*timestamp=*/1, {.position = {.x = 100, .y = 200}, .pressure = 0.2});
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index d01258c..7cb9526 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -56,7 +56,7 @@
mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel));
mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel),
- true /* enableTouchResampling */);
+ /*enableTouchResampling=*/true);
}
status_t publishSimpleMotionEventWithCoords(int32_t action, nsecs_t eventTime,
@@ -79,11 +79,11 @@
if (action == AMOTION_EVENT_ACTION_DOWN && eventTime != 0) {
ADD_FAILURE() << "Downtime should be equal to 0 (hardcoded for convenience)";
}
- return mPublisher->publishMotionEvent(mSeq++, InputEvent::nextId(), 1 /*deviceId*/,
- AINPUT_SOURCE_TOUCHSCREEN, 0 /*displayId*/, INVALID_HMAC,
- action, 0 /*actionButton*/, 0 /*flags*/, 0 /*edgeFlags*/,
- AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
- identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/,
+ return mPublisher->publishMotionEvent(mSeq++, InputEvent::nextId(), /*deviceId=*/1,
+ AINPUT_SOURCE_TOUCHSCREEN, /*displayId=*/0, INVALID_HMAC,
+ action, /*actionButton=*/0, /*flags=*/0, /*edgeFlags=*/0,
+ AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
+ identityTransform, /*xPrecision=*/0, /*yPrecision=*/0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
downTime, eventTime, properties.size(), properties.data(),
@@ -161,7 +161,7 @@
uint32_t consumeSeq;
InputEvent* event;
- status_t status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, frameTime.count(),
+ status_t status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, frameTime.count(),
&consumeSeq, &event);
ASSERT_EQ(OK, status);
MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index c6ad3a2..0277579 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -159,13 +159,13 @@
MotionEvent event;
ui::Transform identityTransform;
- event.initialize(InputEvent::nextId(), 5 /*deviceId*/, AINPUT_SOURCE_ROTARY_ENCODER,
+ event.initialize(InputEvent::nextId(), /*deviceId=*/5, AINPUT_SOURCE_ROTARY_ENCODER,
ADISPLAY_ID_NONE, INVALID_HMAC, AMOTION_EVENT_ACTION_SCROLL,
- 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE, identityTransform,
- 0 /*xPrecision*/, 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
- timeStamp.count(), 1 /*pointerCount*/, properties, coords);
+ /*actionButton=*/0, /*flags=*/0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
+ /*buttonState=*/0, MotionClassification::NONE, identityTransform,
+ /*xPrecision=*/0, /*yPrecision=*/0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, /*downTime=*/0,
+ timeStamp.count(), /*pointerCount=*/1, properties, coords);
events.emplace_back(event);
}
@@ -219,12 +219,12 @@
MotionEvent event;
ui::Transform identityTransform;
- event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
- DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/,
- AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
- 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
+ event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN,
+ DISPLAY_ID, INVALID_HMAC, action, /*actionButton=*/0, /*flags=*/0,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
+ MotionClassification::NONE, identityTransform, /*xPrecision=*/0,
+ /*yPrecision=*/0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, /*downTime=*/0,
entry.eventTime.count(), pointerCount, properties, coords);
events.emplace_back(event);
@@ -341,23 +341,23 @@
TEST_F(VelocityTrackerTest, TestComputedVelocity) {
VelocityTracker::ComputedVelocity computedVelocity;
- computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 0 /*id*/, 200 /*velocity*/);
- computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/, 400 /*velocity*/);
- computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/, 650 /*velocity*/);
- computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, 750 /*velocity*/);
- computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 0 /*id*/, 1000 /*velocity*/);
- computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/, 2000 /*velocity*/);
- computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/, 3000 /*velocity*/);
- computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, 4000 /*velocity*/);
+ computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, /*id=*/0, /*velocity=*/200);
+ computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, /*id=*/26U, /*velocity=*/400);
+ computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, /*id=*/27U, /*velocity=*/650);
+ computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, /*velocity=*/750);
+ computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/0, /*velocity=*/1000);
+ computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/26U, /*velocity=*/2000);
+ computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/27U, /*velocity=*/3000);
+ computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, /*velocity=*/4000);
// Check the axes/indices with velocity.
- EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 0U /*id*/)), 200);
- EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/)), 400);
- EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/)), 650);
+ EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, /*id=*/0U)), 200);
+ EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, /*id=*/26U)), 400);
+ EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, /*id=*/27U)), 650);
EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID)), 750);
- EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 0U /*id*/)), 1000);
- EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/)), 2000);
- EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/)), 3000);
+ EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/0U)), 1000);
+ EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/26U)), 2000);
+ EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, /*id=*/27U)), 3000);
EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID)), 4000);
for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
// Since no data was added for AXIS_SCROLL, expect empty value for the axis for any id.
@@ -436,17 +436,17 @@
float maxFloat = std::numeric_limits<float>::max();
VelocityTracker::ComputedVelocity computedVelocity;
- computedVelocity = vt.getComputedVelocity(1000 /* units */, maxFloat);
+ computedVelocity = vt.getComputedVelocity(/*units=*/1000, maxFloat);
checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
764.345703);
// Expect X velocity to be scaled with respective to provided units.
- computedVelocity = vt.getComputedVelocity(1000000 /* units */, maxFloat);
+ computedVelocity = vt.getComputedVelocity(/*units=*/1000000, maxFloat);
checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
764345.703);
// Expect X velocity to be clamped by provided max velocity.
- computedVelocity = vt.getComputedVelocity(1000000 /* units */, 1000);
+ computedVelocity = vt.getComputedVelocity(/*units=*/1000000, 1000);
checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), 1000);
// All 0 data for Y; expect 0 velocity.
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index f2b59ea..277d74d 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -23,10 +23,10 @@
static KeyEvent getKeyEventWithFlags(int32_t flags) {
KeyEvent event;
- event.initialize(InputEvent::nextId(), 2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD,
+ event.initialize(InputEvent::nextId(), /*deviceId=*/2, AINPUT_SOURCE_GAMEPAD,
ADISPLAY_ID_DEFAULT, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, flags,
- AKEYCODE_BUTTON_X, 121 /*scanCode*/, AMETA_ALT_ON, 1 /*repeatCount*/,
- 1000 /*downTime*/, 2000 /*eventTime*/);
+ AKEYCODE_BUTTON_X, /*scanCode=*/121, AMETA_ALT_ON, /*repeatCount=*/1,
+ /*downTime=*/1000, /*eventTime=*/2000);
return event;
}
@@ -44,12 +44,12 @@
ui::Transform transform;
transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
ui::Transform identity;
- event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
- INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
- AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identity, 100 /*downTime*/,
- 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
+ INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, /*actionButton=*/0, flags,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
+ MotionClassification::NONE, transform, /*xPrecision=*/0.1, /*yPrecision=*/0.2,
+ /*xCursorPosition=*/280, /*yCursorPosition=*/540, identity, /*downTime=*/100,
+ /*eventTime=*/200, pointerCount, pointerProperties, pointerCoords);
return event;
}
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
index 78d1912..b470f35 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/jpegrecoverymap/Android.bp
@@ -29,9 +29,11 @@
local_include_dirs: ["include"],
srcs: [
+ "icc.cpp",
"jpegr.cpp",
"recoverymapmath.cpp",
"jpegrutils.cpp",
+ "multipictureformat.cpp",
],
shared_libs: [
@@ -40,9 +42,8 @@
"libjpegencoder",
"libjpegdecoder",
"liblog",
+ "libutils",
],
-
- static_libs: ["libskia"],
}
cc_library {
diff --git a/libs/jpegrecoverymap/OWNERS b/libs/jpegrecoverymap/OWNERS
index 133af5b..6ace354 100644
--- a/libs/jpegrecoverymap/OWNERS
+++ b/libs/jpegrecoverymap/OWNERS
@@ -1,4 +1,3 @@
arifdikici@google.com
-deakin@google.com
dichenzhang@google.com
kyslov@google.com
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/icc.cpp b/libs/jpegrecoverymap/icc.cpp
new file mode 100644
index 0000000..5412cb1
--- /dev/null
+++ b/libs/jpegrecoverymap/icc.cpp
@@ -0,0 +1,584 @@
+/*
+ * 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 <jpegrecoverymap/icc.h>
+#include <jpegrecoverymap/recoverymapmath.h>
+#include <vector>
+#include <utils/Log.h>
+
+#ifndef FLT_MAX
+#define FLT_MAX 0x1.fffffep127f
+#endif
+
+namespace android::jpegrecoverymap {
+static void Matrix3x3_apply(const Matrix3x3* m, float* x) {
+ float y0 = x[0] * m->vals[0][0] + x[1] * m->vals[0][1] + x[2] * m->vals[0][2];
+ float y1 = x[0] * m->vals[1][0] + x[1] * m->vals[1][1] + x[2] * m->vals[1][2];
+ float y2 = x[0] * m->vals[2][0] + x[1] * m->vals[2][1] + x[2] * m->vals[2][2];
+ x[0] = y0;
+ x[1] = y1;
+ x[2] = y2;
+}
+
+bool Matrix3x3_invert(const Matrix3x3* src, Matrix3x3* dst) {
+ double a00 = src->vals[0][0],
+ a01 = src->vals[1][0],
+ a02 = src->vals[2][0],
+ a10 = src->vals[0][1],
+ a11 = src->vals[1][1],
+ a12 = src->vals[2][1],
+ a20 = src->vals[0][2],
+ a21 = src->vals[1][2],
+ a22 = src->vals[2][2];
+
+ double b0 = a00*a11 - a01*a10,
+ b1 = a00*a12 - a02*a10,
+ b2 = a01*a12 - a02*a11,
+ b3 = a20,
+ b4 = a21,
+ b5 = a22;
+
+ double determinant = b0*b5
+ - b1*b4
+ + b2*b3;
+
+ if (determinant == 0) {
+ return false;
+ }
+
+ double invdet = 1.0 / determinant;
+ if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) {
+ return false;
+ }
+
+ b0 *= invdet;
+ b1 *= invdet;
+ b2 *= invdet;
+ b3 *= invdet;
+ b4 *= invdet;
+ b5 *= invdet;
+
+ dst->vals[0][0] = (float)( a11*b5 - a12*b4 );
+ dst->vals[1][0] = (float)( a02*b4 - a01*b5 );
+ dst->vals[2][0] = (float)( + b2 );
+ dst->vals[0][1] = (float)( a12*b3 - a10*b5 );
+ dst->vals[1][1] = (float)( a00*b5 - a02*b3 );
+ dst->vals[2][1] = (float)( - b1 );
+ dst->vals[0][2] = (float)( a10*b4 - a11*b3 );
+ dst->vals[1][2] = (float)( a01*b3 - a00*b4 );
+ dst->vals[2][2] = (float)( + b0 );
+
+ for (int r = 0; r < 3; ++r)
+ for (int c = 0; c < 3; ++c) {
+ if (!isfinitef_(dst->vals[r][c])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static Matrix3x3 Matrix3x3_concat(const Matrix3x3* A, const Matrix3x3* B) {
+ Matrix3x3 m = { { { 0,0,0 },{ 0,0,0 },{ 0,0,0 } } };
+ for (int r = 0; r < 3; r++)
+ for (int c = 0; c < 3; c++) {
+ m.vals[r][c] = A->vals[r][0] * B->vals[0][c]
+ + A->vals[r][1] * B->vals[1][c]
+ + A->vals[r][2] * B->vals[2][c];
+ }
+ return m;
+}
+
+static void float_XYZD50_to_grid16_lab(const float* xyz_float, uint8_t* grid16_lab) {
+ float v[3] = {
+ xyz_float[0] / kD50_x,
+ xyz_float[1] / kD50_y,
+ xyz_float[2] / kD50_z,
+ };
+ for (size_t i = 0; i < 3; ++i) {
+ v[i] = v[i] > 0.008856f ? cbrtf(v[i]) : v[i] * 7.787f + (16 / 116.0f);
+ }
+ const float L = v[1] * 116.0f - 16.0f;
+ const float a = (v[0] - v[1]) * 500.0f;
+ const float b = (v[1] - v[2]) * 200.0f;
+ const float Lab_unorm[3] = {
+ L * (1 / 100.f),
+ (a + 128.0f) * (1 / 255.0f),
+ (b + 128.0f) * (1 / 255.0f),
+ };
+ // This will encode L=1 as 0xFFFF. This matches how skcms will interpret the
+ // table, but the spec appears to indicate that the value should be 0xFF00.
+ // https://crbug.com/skia/13807
+ for (size_t i = 0; i < 3; ++i) {
+ reinterpret_cast<uint16_t*>(grid16_lab)[i] =
+ Endian_SwapBE16(float_round_to_unorm16(Lab_unorm[i]));
+ }
+}
+
+std::string IccHelper::get_desc_string(const jpegr_transfer_function tf,
+ const jpegr_color_gamut gamut) {
+ std::string result;
+ switch (gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ result += "sRGB";
+ break;
+ case JPEGR_COLORGAMUT_P3:
+ result += "Display P3";
+ break;
+ case JPEGR_COLORGAMUT_BT2100:
+ result += "Rec2020";
+ break;
+ default:
+ result += "Unknown";
+ break;
+ }
+ result += " Gamut with ";
+ switch (tf) {
+ case JPEGR_TF_SRGB:
+ result += "sRGB";
+ break;
+ case JPEGR_TF_LINEAR:
+ result += "Linear";
+ break;
+ case JPEGR_TF_PQ:
+ result += "PQ";
+ break;
+ case JPEGR_TF_HLG:
+ result += "HLG";
+ break;
+ default:
+ result += "Unknown";
+ break;
+ }
+ result += " Transfer";
+ return result;
+}
+
+sp<DataStruct> IccHelper::write_text_tag(const char* text) {
+ uint32_t text_length = strlen(text);
+ uint32_t header[] = {
+ Endian_SwapBE32(kTAG_TextType), // Type signature
+ 0, // Reserved
+ Endian_SwapBE32(1), // Number of records
+ Endian_SwapBE32(12), // Record size (must be 12)
+ Endian_SwapBE32(SetFourByteTag('e', 'n', 'U', 'S')), // English USA
+ Endian_SwapBE32(2 * text_length), // Length of string in bytes
+ Endian_SwapBE32(28), // Offset of string
+ };
+
+ 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);
+
+ if (!dataStruct->write(header, sizeof(header))) {
+ ALOGE("write_text_tag(): error in writing data");
+ return dataStruct;
+ }
+
+ for (size_t i = 0; i < text_length; i++) {
+ // Convert ASCII to big-endian UTF-16.
+ dataStruct->write8(0);
+ dataStruct->write8(text[i]);
+ }
+
+ return dataStruct;
+}
+
+sp<DataStruct> IccHelper::write_xyz_tag(float x, float y, float z) {
+ uint32_t data[] = {
+ Endian_SwapBE32(kXYZ_PCSSpace),
+ 0,
+ static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(x))),
+ 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));
+ dataStruct->write(&data, sizeof(data));
+ return dataStruct;
+}
+
+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);
+ dataStruct->write32(Endian_SwapBE32(kTAG_CurveType)); // Type
+ 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);
+ }
+ return dataStruct;
+}
+
+sp<DataStruct> IccHelper::write_trc_tag_for_linear() {
+ 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(1.0)));
+
+ return dataStruct;
+}
+
+float IccHelper::compute_tone_map_gain(const jpegr_transfer_function tf, float L) {
+ if (L <= 0.f) {
+ return 1.f;
+ }
+ if (tf == JPEGR_TF_PQ) {
+ // The PQ transfer function will map to the range [0, 1]. Linearly scale
+ // it up to the range [0, 10,000/203]. We will then tone map that back
+ // down to [0, 1].
+ constexpr float kInputMaxLuminance = 10000 / 203.f;
+ constexpr float kOutputMaxLuminance = 1.0;
+ L *= kInputMaxLuminance;
+
+ // Compute the tone map gain which will tone map from 10,000/203 to 1.0.
+ constexpr float kToneMapA = kOutputMaxLuminance / (kInputMaxLuminance * kInputMaxLuminance);
+ constexpr float kToneMapB = 1.f / kOutputMaxLuminance;
+ return kInputMaxLuminance * (1.f + kToneMapA * L) / (1.f + kToneMapB * L);
+ }
+ if (tf == JPEGR_TF_HLG) {
+ // Let Lw be the brightness of the display in nits.
+ constexpr float Lw = 203.f;
+ const float gamma = 1.2f + 0.42f * std::log(Lw / 1000.f) / std::log(10.f);
+ return std::pow(L, gamma - 1.f);
+ }
+ return 1.f;
+}
+
+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);
+ dataStruct->write32(Endian_SwapBE32(kTAG_cicp)); // Type signature
+ dataStruct->write32(0); // Reserved
+ dataStruct->write8(color_primaries); // Color primaries
+ dataStruct->write8(transfer_characteristics); // Transfer characteristics
+ dataStruct->write8(0); // RGB matrix
+ dataStruct->write8(1); // Full range
+ return dataStruct;
+}
+
+void IccHelper::compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]) {
+ // Compute the matrices to convert from source to Rec2020, and from Rec2020 to XYZD50.
+ Matrix3x3 src_to_rec2020;
+ const Matrix3x3 rec2020_to_XYZD50 = kRec2020;
+ {
+ Matrix3x3 XYZD50_to_rec2020;
+ Matrix3x3_invert(&rec2020_to_XYZD50, &XYZD50_to_rec2020);
+ src_to_rec2020 = Matrix3x3_concat(&XYZD50_to_rec2020, &src_to_XYZD50);
+ }
+
+ // Convert the source signal to linear.
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ rgb[i] = pqOetf(rgb[i]);
+ }
+
+ // Convert source gamut to Rec2020.
+ Matrix3x3_apply(&src_to_rec2020, rgb);
+
+ // Compute the luminance of the signal.
+ float L = bt2100Luminance({{{rgb[0], rgb[1], rgb[2]}}});
+
+ // Compute the tone map gain based on the luminance.
+ float tone_map_gain = compute_tone_map_gain(JPEGR_TF_PQ, L);
+
+ // Apply the tone map gain.
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ rgb[i] *= tone_map_gain;
+ }
+
+ // Convert from Rec2020-linear to XYZD50.
+ Matrix3x3_apply(&rec2020_to_XYZD50, rgb);
+}
+
+sp<DataStruct> IccHelper::write_clut(const uint8_t* grid_points, const uint8_t* grid_16) {
+ uint32_t value_count = kNumChannels;
+ for (uint32_t i = 0; i < kNumChannels; ++i) {
+ value_count *= grid_points[i];
+ }
+
+ int total_length = 20 + 2 * value_count;
+ total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
+ sp<DataStruct> dataStruct = new DataStruct(total_length);
+
+ for (size_t i = 0; i < 16; ++i) {
+ dataStruct->write8(i < kNumChannels ? grid_points[i] : 0); // Grid size
+ }
+ dataStruct->write8(2); // Grid byte width (always 16-bit)
+ dataStruct->write8(0); // Reserved
+ dataStruct->write8(0); // Reserved
+ dataStruct->write8(0); // Reserved
+
+ for (uint32_t i = 0; i < value_count; ++i) {
+ uint16_t value = reinterpret_cast<const uint16_t*>(grid_16)[i];
+ dataStruct->write16(value);
+ }
+
+ return dataStruct;
+}
+
+sp<DataStruct> IccHelper::write_mAB_or_mBA_tag(uint32_t type,
+ bool has_a_curves,
+ const uint8_t* grid_points,
+ const uint8_t* grid_16) {
+ const size_t b_curves_offset = 32;
+ sp<DataStruct> b_curves_data[kNumChannels];
+ sp<DataStruct> a_curves_data[kNumChannels];
+ size_t clut_offset = 0;
+ sp<DataStruct> clut;
+ size_t a_curves_offset = 0;
+
+ // The "B" curve is required.
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ b_curves_data[i] = write_trc_tag_for_linear();
+ }
+
+ // The "A" curve and CLUT are optional.
+ if (has_a_curves) {
+ clut_offset = b_curves_offset;
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ clut_offset += b_curves_data[i]->getLength();
+ }
+ clut = write_clut(grid_points, grid_16);
+
+ a_curves_offset = clut_offset + clut->getLength();
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ a_curves_data[i] = write_trc_tag_for_linear();
+ }
+ }
+
+ int total_length = b_curves_offset;
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ total_length += b_curves_data[i]->getLength();
+ }
+ if (has_a_curves) {
+ total_length += clut->getLength();
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ total_length += a_curves_data[i]->getLength();
+ }
+ }
+ sp<DataStruct> dataStruct = new DataStruct(total_length);
+ dataStruct->write32(Endian_SwapBE32(type)); // Type signature
+ dataStruct->write32(0); // Reserved
+ dataStruct->write8(kNumChannels); // Input channels
+ dataStruct->write8(kNumChannels); // Output channels
+ dataStruct->write16(0); // Reserved
+ dataStruct->write32(Endian_SwapBE32(b_curves_offset)); // B curve offset
+ dataStruct->write32(Endian_SwapBE32(0)); // Matrix offset (ignored)
+ dataStruct->write32(Endian_SwapBE32(0)); // M curve offset (ignored)
+ dataStruct->write32(Endian_SwapBE32(clut_offset)); // CLUT offset
+ dataStruct->write32(Endian_SwapBE32(a_curves_offset)); // A curve offset
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ if (dataStruct->write(b_curves_data[i]->getData(), b_curves_data[i]->getLength())) {
+ return dataStruct;
+ }
+ }
+ if (has_a_curves) {
+ dataStruct->write(clut->getData(), clut->getLength());
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ dataStruct->write(a_curves_data[i]->getData(), a_curves_data[i]->getLength());
+ }
+ }
+ return dataStruct;
+}
+
+sp<DataStruct> IccHelper::writeIccProfile(jpegr_transfer_function tf, jpegr_color_gamut gamut) {
+ ICCHeader header;
+
+ std::vector<std::pair<uint32_t, sp<DataStruct>>> tags;
+
+ // Compute profile description tag
+ std::string desc = get_desc_string(tf, gamut);
+
+ tags.emplace_back(kTAG_desc, write_text_tag(desc.c_str()));
+
+ Matrix3x3 toXYZD50;
+ switch (gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ toXYZD50 = kSRGB;
+ break;
+ case JPEGR_COLORGAMUT_P3:
+ toXYZD50 = kDisplayP3;
+ break;
+ case JPEGR_COLORGAMUT_BT2100:
+ toXYZD50 = kRec2020;
+ break;
+ default:
+ // Should not fall here.
+ return new DataStruct(0);
+ }
+
+ // Compute primaries.
+ {
+ tags.emplace_back(kTAG_rXYZ,
+ write_xyz_tag(toXYZD50.vals[0][0], toXYZD50.vals[1][0], toXYZD50.vals[2][0]));
+ tags.emplace_back(kTAG_gXYZ,
+ write_xyz_tag(toXYZD50.vals[0][1], toXYZD50.vals[1][1], toXYZD50.vals[2][1]));
+ tags.emplace_back(kTAG_bXYZ,
+ write_xyz_tag(toXYZD50.vals[0][2], toXYZD50.vals[1][2], toXYZD50.vals[2][2]));
+ }
+
+ // Compute white point tag (must be D50)
+ tags.emplace_back(kTAG_wtpt, write_xyz_tag(kD50_x, kD50_y, kD50_z));
+
+ // Compute transfer curves.
+ if (tf != JPEGR_TF_PQ) {
+ if (tf == JPEGR_TF_HLG) {
+ std::vector<uint8_t> trc_table;
+ trc_table.resize(kTrcTableSize * 2);
+ for (uint32_t i = 0; i < kTrcTableSize; ++i) {
+ float x = i / (kTrcTableSize - 1.f);
+ float y = hlgOetf(x);
+ y *= compute_tone_map_gain(tf, y);
+ float_to_table16(y, &trc_table[2 * i]);
+ }
+
+ tags.emplace_back(kTAG_rTRC,
+ write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
+ tags.emplace_back(kTAG_gTRC,
+ write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
+ 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());
+ }
+ }
+
+ // Compute CICP.
+ if (tf == JPEGR_TF_HLG || tf == JPEGR_TF_PQ) {
+ // The CICP tag is present in ICC 4.4, so update the header's version.
+ header.version = Endian_SwapBE32(0x04400000);
+
+ uint32_t color_primaries = 0;
+ if (gamut == JPEGR_COLORGAMUT_BT709) {
+ color_primaries = kCICPPrimariesSRGB;
+ } else if (gamut == JPEGR_COLORGAMUT_P3) {
+ color_primaries = kCICPPrimariesP3;
+ }
+
+ uint32_t transfer_characteristics = 0;
+ if (tf == JPEGR_TF_SRGB) {
+ transfer_characteristics = kCICPTrfnSRGB;
+ } else if (tf == JPEGR_TF_LINEAR) {
+ transfer_characteristics = kCICPTrfnLinear;
+ } else if (tf == JPEGR_TF_PQ) {
+ transfer_characteristics = kCICPTrfnPQ;
+ } else if (tf == JPEGR_TF_HLG) {
+ transfer_characteristics = kCICPTrfnHLG;
+ }
+ tags.emplace_back(kTAG_cicp, write_cicp_tag(color_primaries, transfer_characteristics));
+ }
+
+ // Compute A2B0.
+ if (tf == JPEGR_TF_PQ) {
+ std::vector<uint8_t> a2b_grid;
+ a2b_grid.resize(kGridSize * kGridSize * kGridSize * kNumChannels * 2);
+ size_t a2b_grid_index = 0;
+ for (uint32_t r_index = 0; r_index < kGridSize; ++r_index) {
+ for (uint32_t g_index = 0; g_index < kGridSize; ++g_index) {
+ for (uint32_t b_index = 0; b_index < kGridSize; ++b_index) {
+ float rgb[3] = {
+ r_index / (kGridSize - 1.f),
+ g_index / (kGridSize - 1.f),
+ b_index / (kGridSize - 1.f),
+ };
+ compute_lut_entry(toXYZD50, rgb);
+ float_XYZD50_to_grid16_lab(rgb, &a2b_grid[a2b_grid_index]);
+ a2b_grid_index += 6;
+ }
+ }
+ }
+ const uint8_t* grid_16 = reinterpret_cast<const uint8_t*>(a2b_grid.data());
+
+ uint8_t grid_points[kNumChannels];
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ grid_points[i] = kGridSize;
+ }
+
+ auto a2b_data = write_mAB_or_mBA_tag(kTAG_mABType,
+ /* has_a_curves */ true,
+ grid_points,
+ grid_16);
+ tags.emplace_back(kTAG_A2B0, std::move(a2b_data));
+ }
+
+ // Compute B2A0.
+ if (tf == JPEGR_TF_PQ) {
+ auto b2a_data = write_mAB_or_mBA_tag(kTAG_mBAType,
+ /* has_a_curves */ false,
+ /* grid_points */ nullptr,
+ /* grid_16 */ nullptr);
+ tags.emplace_back(kTAG_B2A0, std::move(b2a_data));
+ }
+
+ // Compute copyright tag
+ tags.emplace_back(kTAG_cprt, write_text_tag("Google Inc. 2022"));
+
+ // Compute the size of the profile.
+ size_t tag_data_size = 0;
+ for (const auto& tag : tags) {
+ tag_data_size += tag.second->getLength();
+ }
+ size_t tag_table_size = kICCTagTableEntrySize * tags.size();
+ size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size;
+
+ // Write the header.
+ header.data_color_space = Endian_SwapBE32(Signature_RGB);
+ header.pcs = Endian_SwapBE32(tf == JPEGR_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;
+ }
+
+ // Write the tag table. Track the offset and size of the previous tag to
+ // compute each tag's offset. An empty SkData indicates that the previous
+ // tag is to be reused.
+ uint32_t last_tag_offset = sizeof(header) + tag_table_size;
+ uint32_t last_tag_size = 0;
+ for (const auto& tag : tags) {
+ last_tag_offset = last_tag_offset + last_tag_size;
+ last_tag_size = tag.second->getLength();
+ uint32_t tag_table_entry[3] = {
+ Endian_SwapBE32(tag.first),
+ Endian_SwapBE32(last_tag_offset),
+ Endian_SwapBE32(last_tag_size),
+ };
+ if (!dataStruct->write(tag_table_entry, sizeof(tag_table_entry))) {
+ ALOGE("writeIccProfile(): error in writing tag table");
+ return dataStruct;
+ }
+ }
+
+ // Write the tags.
+ for (const auto& tag : tags) {
+ if (!dataStruct->write(tag.second->getData(), tag.second->getLength())) {
+ ALOGE("writeIccProfile(): error in writing tags");
+ return dataStruct;
+ }
+ }
+
+ return dataStruct;
+}
+
+} // namespace android::jpegrecoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h b/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h
new file mode 100644
index 0000000..a81aa62
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_ICC_H
+#define ANDROID_JPEGRECOVERYMAP_ICC_H
+
+#include <jpegrecoverymap/jpegr.h>
+#include <jpegrecoverymap/jpegrutils.h>
+#include <utils/RefBase.h>
+#include <cmath>
+#include <string>
+
+#ifdef USE_BIG_ENDIAN
+#undef USE_BIG_ENDIAN
+#define USE_BIG_ENDIAN true
+#endif
+
+namespace android::jpegrecoverymap {
+
+typedef int32_t Fixed;
+#define Fixed1 (1 << 16)
+#define MaxS32FitsInFloat 2147483520
+#define MinS32FitsInFloat (-MaxS32FitsInFloat)
+#define FixedToFloat(x) ((x) * 1.52587890625e-5f)
+
+typedef struct Matrix3x3 {
+ float vals[3][3];
+} Matrix3x3;
+
+// The D50 illuminant.
+constexpr float kD50_x = 0.9642f;
+constexpr float kD50_y = 1.0000f;
+constexpr float kD50_z = 0.8249f;
+
+enum {
+ // data_color_space
+ Signature_CMYK = 0x434D594B,
+ Signature_Gray = 0x47524159,
+ Signature_RGB = 0x52474220,
+
+ // pcs
+ Signature_Lab = 0x4C616220,
+ 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);
+}
+
+// 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.
+static constexpr size_t kICCHeaderSize = 132;
+
+// Contains a signature (4), offset (4), and size (4).
+static constexpr size_t kICCTagTableEntrySize = 12;
+
+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', ' ');
+static constexpr uint32_t kACSP_Signature = SetFourByteTag('a', 'c', 's', 'p');
+
+static constexpr uint32_t kTAG_desc = SetFourByteTag('d', 'e', 's', 'c');
+static constexpr uint32_t kTAG_TextType = SetFourByteTag('m', 'l', 'u', 'c');
+static constexpr uint32_t kTAG_rXYZ = SetFourByteTag('r', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_gXYZ = SetFourByteTag('g', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_bXYZ = SetFourByteTag('b', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_wtpt = SetFourByteTag('w', 't', 'p', 't');
+static constexpr uint32_t kTAG_rTRC = SetFourByteTag('r', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_gTRC = SetFourByteTag('g', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_bTRC = SetFourByteTag('b', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_cicp = SetFourByteTag('c', 'i', 'c', 'p');
+static constexpr uint32_t kTAG_cprt = SetFourByteTag('c', 'p', 'r', 't');
+static constexpr uint32_t kTAG_A2B0 = SetFourByteTag('A', '2', 'B', '0');
+static constexpr uint32_t kTAG_B2A0 = SetFourByteTag('B', '2', 'A', '0');
+
+static constexpr uint32_t kTAG_CurveType = SetFourByteTag('c', 'u', 'r', 'v');
+static constexpr uint32_t kTAG_mABType = SetFourByteTag('m', 'A', 'B', ' ');
+static constexpr uint32_t kTAG_mBAType = SetFourByteTag('m', 'B', 'A', ' ');
+static constexpr uint32_t kTAG_ParaCurveType = SetFourByteTag('p', 'a', 'r', 'a');
+
+
+static constexpr Matrix3x3 kSRGB = {{
+ // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync.
+ // 0.436065674f, 0.385147095f, 0.143066406f,
+ // 0.222488403f, 0.716873169f, 0.060607910f,
+ // 0.013916016f, 0.097076416f, 0.714096069f,
+ { FixedToFloat(0x6FA2), FixedToFloat(0x6299), FixedToFloat(0x24A0) },
+ { FixedToFloat(0x38F5), FixedToFloat(0xB785), FixedToFloat(0x0F84) },
+ { FixedToFloat(0x0390), FixedToFloat(0x18DA), FixedToFloat(0xB6CF) },
+}};
+
+static constexpr Matrix3x3 kDisplayP3 = {{
+ { 0.515102f, 0.291965f, 0.157153f },
+ { 0.241182f, 0.692236f, 0.0665819f },
+ { -0.00104941f, 0.0418818f, 0.784378f },
+}};
+
+static constexpr Matrix3x3 kRec2020 = {{
+ { 0.673459f, 0.165661f, 0.125100f },
+ { 0.279033f, 0.675338f, 0.0456288f },
+ { -0.00193139f, 0.0299794f, 0.797162f },
+}};
+
+static constexpr uint32_t kCICPPrimariesSRGB = 1;
+static constexpr uint32_t kCICPPrimariesP3 = 12;
+static constexpr uint32_t kCICPPrimariesRec2020 = 9;
+
+static constexpr uint32_t kCICPTrfnSRGB = 1;
+static constexpr uint32_t kCICPTrfnLinear = 8;
+static constexpr uint32_t kCICPTrfnPQ = 16;
+static constexpr uint32_t kCICPTrfnHLG = 18;
+
+enum ParaCurveType {
+ kExponential_ParaCurveType = 0,
+ kGAB_ParaCurveType = 1,
+ kGABC_ParaCurveType = 2,
+ kGABDE_ParaCurveType = 3,
+ kGABCDEF_ParaCurveType = 4,
+};
+
+/**
+ * Return the closest int for the given float. Returns MaxS32FitsInFloat for NaN.
+ */
+static inline int float_saturate2int(float x) {
+ x = x < MaxS32FitsInFloat ? x : MaxS32FitsInFloat;
+ x = x > MinS32FitsInFloat ? x : MinS32FitsInFloat;
+ return (int)x;
+}
+
+static Fixed float_round_to_fixed(float x) {
+ return float_saturate2int((float)floor((double)x * Fixed1 + 0.5));
+}
+
+static uint16_t float_round_to_unorm16(float x) {
+ x = x * 65535.f + 0.5;
+ if (x > 65535) return 65535;
+ if (x < 0) return 0;
+ return static_cast<uint16_t>(x);
+}
+
+static void float_to_table16(const float f, uint8_t* table_16) {
+ *reinterpret_cast<uint16_t*>(table_16) = Endian_SwapBE16(float_round_to_unorm16(f));
+}
+
+static bool isfinitef_(float x) { return 0 == x*0; }
+
+struct ICCHeader {
+ // Size of the profile (computed)
+ uint32_t size;
+ // Preferred CMM type (ignored)
+ uint32_t cmm_type = 0;
+ // Version 4.3 or 4.4 if CICP is included.
+ uint32_t version = Endian_SwapBE32(0x04300000);
+ // Display device profile
+ uint32_t profile_class = Endian_SwapBE32(kDisplay_Profile);
+ // RGB input color space;
+ uint32_t data_color_space = Endian_SwapBE32(kRGB_ColorSpace);
+ // Profile connection space.
+ uint32_t pcs = Endian_SwapBE32(kXYZ_PCSSpace);
+ // Date and time (ignored)
+ uint8_t creation_date_time[12] = {0};
+ // Profile signature
+ uint32_t signature = Endian_SwapBE32(kACSP_Signature);
+ // Platform target (ignored)
+ uint32_t platform = 0;
+ // Flags: not embedded, can be used independently
+ uint32_t flags = 0x00000000;
+ // Device manufacturer (ignored)
+ uint32_t device_manufacturer = 0;
+ // Device model (ignored)
+ uint32_t device_model = 0;
+ // Device attributes (ignored)
+ uint8_t device_attributes[8] = {0};
+ // Relative colorimetric rendering intent
+ uint32_t rendering_intent = Endian_SwapBE32(1);
+ // D50 standard illuminant (X, Y, Z)
+ uint32_t illuminant_X = Endian_SwapBE32(float_round_to_fixed(kD50_x));
+ uint32_t illuminant_Y = Endian_SwapBE32(float_round_to_fixed(kD50_y));
+ uint32_t illuminant_Z = Endian_SwapBE32(float_round_to_fixed(kD50_z));
+ // Profile creator (ignored)
+ uint32_t creator = 0;
+ // Profile id checksum (ignored)
+ uint8_t profile_id[16] = {0};
+ // Reserved (ignored)
+ uint8_t reserved[28] = {0};
+ // Technically not part of header, but required
+ uint32_t tag_count = 0;
+};
+
+class IccHelper {
+private:
+ static constexpr uint32_t kTrcTableSize = 65;
+ static constexpr uint32_t kGridSize = 17;
+ static constexpr size_t kNumChannels = 3;
+
+ static sp<DataStruct> write_text_tag(const char* text);
+ static std::string get_desc_string(const jpegr_transfer_function tf,
+ const jpegr_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 float compute_tone_map_gain(const jpegr_transfer_function tf, float L);
+ static sp<DataStruct> write_cicp_tag(uint32_t color_primaries,
+ uint32_t transfer_characteristics);
+ static sp<DataStruct> write_mAB_or_mBA_tag(uint32_t type,
+ bool has_a_curves,
+ const uint8_t* grid_points,
+ const uint8_t* grid_16);
+ 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);
+
+public:
+ static sp<DataStruct> writeIccProfile(const jpegr_transfer_function tf,
+ const jpegr_color_gamut gamut);
+};
+} // namespace android::jpegrecoverymap
+
+#endif //ANDROID_JPEGRECOVERYMAP_ICC_H
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
index 5455ba6..e2023a6 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
@@ -38,6 +38,14 @@
JPEGR_TF_SRGB = 3,
} jpegr_transfer_function;
+// Target output formats for decoder
+typedef enum {
+ JPEGR_OUTPUT_SDR, // SDR in RGBA_8888 color format
+ JPEGR_OUTPUT_HDR_LINEAR, // HDR in F16 color format (linear)
+ JPEGR_OUTPUT_HDR_PQ, // HDR in RGBA_1010102 color format (PQ transfer function)
+ JPEGR_OUTPUT_HDR_HLG, // HDR in RGBA_1010102 color format (HLG transfer function)
+} jpegr_output_format;
+
struct jpegr_info_struct {
size_t width;
size_t height;
@@ -83,7 +91,10 @@
int length;
};
-struct jpegr_metadata {
+/*
+ * Holds information for recovery map related metadata.
+ */
+struct jpegr_metadata_struct {
// JPEG/R version
uint32_t version;
// Max Content Boost for the map
@@ -95,12 +106,14 @@
typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
typedef struct jpegr_compressed_struct* jr_compressed_ptr;
typedef struct jpegr_exif_struct* jr_exif_ptr;
-typedef struct jpegr_metadata* jr_metadata_ptr;
+typedef struct jpegr_metadata_struct* jr_metadata_ptr;
typedef struct jpegr_info_struct* jr_info_ptr;
class JpegR {
public:
/*
+ * Experimental only
+ *
* Encode API-0
* Compress JPEGR image from 10-bit HDR YUV.
*
@@ -191,24 +204,40 @@
* Decode API
* Decompress JPEGR image.
*
- * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
- * @param compressed_jpegr_image compressed JPEGR image
- * @param dest destination of the uncompressed JPEGR image
- * @param exif destination of the decoded EXIF metadata.
- * @param request_sdr flag that request SDR output. If set to true, decoder will only decode
- * the primary image which is SDR. Setting of request_sdr and input source
- * (HDR or SDR) can be found in the table below:
- * | input source | request_sdr | output of decoding |
- * | HDR | true | SDR |
- * | HDR | false | HDR |
- * | SDR | true | SDR |
- * | SDR | false | SDR |
+ * @param compressed_jpegr_image compressed JPEGR image.
+ * @param dest destination of the uncompressed JPEGR image.
+ * @param exif destination of the decoded EXIF metadata. The default value is NULL where the
+ decoder will do nothing about it. If configured not NULL the decoder will write
+ EXIF data into this structure. The format is defined in {@code jpegr_exif_struct}
+ * @param output_format flag for setting output color format. Its value configures the output
+ color format. The default value is {@code JPEGR_OUTPUT_HDR_LINEAR}.
+ ----------------------------------------------------------------------
+ | output_format | decoded color format to be written |
+ ----------------------------------------------------------------------
+ | JPEGR_OUTPUT_SDR | RGBA_8888 |
+ ----------------------------------------------------------------------
+ | JPEGR_OUTPUT_HDR_LINEAR | (default)RGBA_F16 linear |
+ ----------------------------------------------------------------------
+ | JPEGR_OUTPUT_HDR_PQ | RGBA_1010102 PQ |
+ ----------------------------------------------------------------------
+ | JPEGR_OUTPUT_HDR_HLG | RGBA_1010102 HLG |
+ ----------------------------------------------------------------------
+ * @param recovery_map destination of the decoded recovery map. The default value is NULL where
+ the decoder will do nothing about it. If configured not NULL the decoder
+ will write the decoded recovery_map data into this structure. The format
+ is defined in {@code jpegr_uncompressed_struct}.
+ * @param metadata destination of the decoded metadata. The default value is NULL where the
+ decoder will do nothing about it. If configured not NULL the decoder will
+ write metadata into this structure. the format of metadata is defined in
+ {@code jpegr_metadata}.
* @return NO_ERROR if decoding succeeds, error code if error occurs.
*/
status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
jr_uncompressed_ptr dest,
jr_exif_ptr exif = nullptr,
- bool request_sdr = false);
+ jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR,
+ jr_uncompressed_ptr recovery_map = nullptr,
+ jr_metadata_ptr metadata = nullptr);
/*
* Gets Info from JPEGR file without decoding it.
@@ -249,12 +278,16 @@
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_recovery_map uncompressed recovery map
* @param metadata JPEG/R metadata extracted from XMP.
+ * @param output_format flag for setting output color format. if set to
+ * {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
+ * which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
* @param dest reconstructed HDR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_recovery_map,
jr_metadata_ptr metadata,
+ jpegr_output_format output_format,
jr_uncompressed_ptr dest);
private:
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
index 581806c..dd06fa2 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
@@ -18,6 +18,7 @@
#define ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
#include <jpegrecoverymap/jpegr.h>
+#include <utils/RefBase.h>
#include <sstream>
#include <stdint.h>
@@ -26,7 +27,45 @@
namespace android::jpegrecoverymap {
-struct jpegr_metadata;
+static constexpr uint32_t EndianSwap32(uint32_t value) {
+ return ((value & 0xFF) << 24) |
+ ((value & 0xFF00) << 8) |
+ ((value & 0xFF0000) >> 8) |
+ (value >> 24);
+}
+static inline uint16_t EndianSwap16(uint16_t value) {
+ return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8));
+}
+
+#if USE_BIG_ENDIAN
+ #define Endian_SwapBE32(n) EndianSwap32(n)
+ #define Endian_SwapBE16(n) EndianSwap16(n)
+#else
+ #define Endian_SwapBE32(n) (n)
+ #define Endian_SwapBE16(n) (n)
+#endif
+
+struct jpegr_metadata_struct;
+/*
+ * Mutable data structure. Holds information for metadata.
+ */
+class DataStruct : public RefBase {
+private:
+ void* data;
+ int writePos;
+ int length;
+ ~DataStruct();
+
+public:
+ DataStruct(int s);
+ void* getData();
+ int getLength();
+ int getBytesWritten();
+ bool write8(uint8_t value);
+ bool write16(uint16_t value);
+ bool write32(uint32_t value);
+ bool write(const void* src, int size);
+};
/*
* Helper function used for writing data to destination.
@@ -48,15 +87,13 @@
* @param metadata place to store HDR metadata values
* @return true if metadata is successfully retrieved, false otherwise
*/
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata);
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata_struct* metadata);
/*
- * This method generates XMP metadata.
+ * This method generates XMP metadata for the primary image.
*
* below is an example of the XMP metadata that this function generates where
* secondary_image_length = 1000
- * max_content_boost = 8.0
- * min_content_boost = 0.5
*
* <x:xmpmeta
* xmlns:x="adobe:ns:meta/"
@@ -65,8 +102,7 @@
* xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
* <rdf:Description
* xmlns:Container="http://ns.google.com/photos/1.0/container/"
- * xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
- * xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/">
+ * xmlns:Item="http://ns.google.com/photos/1.0/container/item/">
* <Container:Directory>
* <rdf:Seq>
* <rdf:li>
@@ -78,10 +114,7 @@
* <Container:Item
* Item:Semantic="RecoveryMap"
* Item:Mime="image/jpeg"
- * Item:Length="1000"
- * RecoveryMap:Version="1"
- * RecoveryMap:MaxContentBoost="8.0"
- * RecoveryMap:MinContentBoost="0.5"/>
+ * Item:Length="1000"/>
* </rdf:li>
* </rdf:Seq>
* </Container:Directory>
@@ -90,10 +123,40 @@
* </x:xmpmeta>
*
* @param secondary_image_length length of secondary image
+ * @return XMP metadata in type of string
+ */
+std::string generateXmpForPrimaryImage(int secondary_image_length);
+
+/*
+ * This method generates XMP metadata for the recovery map image.
+ *
+ * below is an example of the XMP metadata that this function generates where
+ * max_content_boost = 8.0
+ * min_content_boost = 0.5
+ *
+ * <x:xmpmeta
+ * xmlns:x="adobe:ns:meta/"
+ * x:xmptk="Adobe XMP Core 5.1.2">
+ * <rdf:RDF
+ * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ * <rdf:Description
+ * xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/"
+ * hdrgm:Version="1"
+ * hdrgm:GainMapMin="0.5"
+ * hdrgm:GainMapMax="8.5"
+ * hdrgm:Gamma="1"
+ * hdrgm:OffsetSDR="0"
+ * hdrgm:OffsetHDR="0"
+ * hdrgm:HDRCapacityMin="0.5"
+ * hdrgm:HDRCapacityMax="8.5"
+ * hdrgm:BaseRendition="SDR"/>
+ * </rdf:RDF>
+ * </x:xmpmeta>
+ *
* @param metadata JPEG/R metadata to encode as XMP
* @return XMP metadata in type of string
*/
-std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
+ std::string generateXmpForSecondaryImage(jpegr_metadata_struct& metadata);
} // namespace android::jpegrecoverymap
#endif //ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h b/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
new file mode 100644
index 0000000..cf3387d
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
+#define ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
+
+#include <jpegrecoverymap/jpegrutils.h>
+
+#ifdef USE_BIG_ENDIAN
+#undef USE_BIG_ENDIAN
+#define USE_BIG_ENDIAN true
+#endif
+
+namespace android::jpegrecoverymap {
+
+constexpr size_t kNumPictures = 2;
+constexpr size_t kMpEndianSize = 4;
+constexpr uint16_t kTagSerializedCount = 3;
+constexpr uint32_t kTagSize = 12;
+
+constexpr uint16_t kTypeLong = 0x4;
+constexpr uint16_t kTypeUndefined = 0x7;
+
+static constexpr uint8_t kMpfSig[] = {'M', 'P', 'F', '\0'};
+constexpr uint8_t kMpLittleEndian[kMpEndianSize] = {0x49, 0x49, 0x2A, 0x00};
+constexpr uint8_t kMpBigEndian[kMpEndianSize] = {0x4D, 0x4D, 0x00, 0x2A};
+
+constexpr uint16_t kVersionTag = 0xB000;
+constexpr uint16_t kVersionType = kTypeUndefined;
+constexpr uint32_t kVersionCount = 4;
+constexpr size_t kVersionSize = 4;
+constexpr uint8_t kVersionExpected[kVersionSize] = {'0', '1', '0', '0'};
+
+constexpr uint16_t kNumberOfImagesTag = 0xB001;
+constexpr uint16_t kNumberOfImagesType = kTypeLong;
+constexpr uint32_t kNumberOfImagesCount = 1;
+
+constexpr uint16_t kMPEntryTag = 0xB002;
+constexpr uint16_t kMPEntryType = kTypeUndefined;
+constexpr uint32_t kMPEntrySize = 16;
+
+constexpr uint32_t kMPEntryAttributeFormatJpeg = 0x0000000;
+constexpr uint32_t kMPEntryAttributeTypePrimary = 0x030000;
+
+size_t calculateMpfSize();
+sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
+ int secondary_image_size, int secondary_image_offset);
+
+} // namespace android::jpegrecoverymap
+
+#endif //ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index c12cee9..8b5318f 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -115,6 +115,14 @@
return temp /= rhs;
}
+inline uint16_t floatToHalf(float f) {
+ uint32_t x = *((uint32_t*)&f);
+ uint16_t h = ((x >> 16) & 0x8000)
+ | ((((x & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
+ | ((x >> 13) & 0x03ff);
+ return h;
+}
+
constexpr size_t kRecoveryFactorPrecision = 10;
constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision;
struct RecoveryLUT {
@@ -392,6 +400,13 @@
*/
uint32_t colorToRgba1010102(Color e_gamma);
+/*
+ * Convert from Color to F16.
+ *
+ * Alpha always set to 1.0.
+ */
+uint64_t colorToRgbaF16(Color e_gamma);
+
} // namespace android::jpegrecoverymap
#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
diff --git a/libs/jpegrecoverymap/jpegr.cpp b/libs/jpegrecoverymap/jpegr.cpp
index 828af2d..e197bf0 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -19,6 +19,8 @@
#include <jpegrecoverymap/jpegdecoderhelper.h>
#include <jpegrecoverymap/recoverymapmath.h>
#include <jpegrecoverymap/jpegrutils.h>
+#include <jpegrecoverymap/multipictureformat.h>
+#include <jpegrecoverymap/icc.h>
#include <image_io/jpeg/jpeg_marker.h>
#include <image_io/jpeg/jpeg_info.h>
@@ -26,10 +28,6 @@
#include <image_io/jpeg/jpeg_info_builder.h>
#include <image_io/base/data_segment_data_source.h>
#include <utils/Log.h>
-#include "SkColorSpace.h"
-#include "SkData.h"
-#include "SkICC.h"
-#include "SkRefCnt.h"
#include <map>
#include <memory>
@@ -88,27 +86,12 @@
return cpuCoreCount;
}
-static const map<jpegrecoverymap::jpegr_color_gamut, skcms_Matrix3x3> jrGamut_to_skGamut {
- {JPEGR_COLORGAMUT_BT709, SkNamedGamut::kSRGB},
- {JPEGR_COLORGAMUT_P3, SkNamedGamut::kDisplayP3},
- {JPEGR_COLORGAMUT_BT2100, SkNamedGamut::kRec2020},
-};
-
-static const map<
- jpegrecoverymap::jpegr_transfer_function,
- skcms_TransferFunction> jrTransFunc_to_skTransFunc {
- {JPEGR_TF_SRGB, SkNamedTransferFn::kSRGB},
- {JPEGR_TF_LINEAR, SkNamedTransferFn::kLinear},
- {JPEGR_TF_HLG, SkNamedTransferFn::kHLG},
- {JPEGR_TF_PQ, SkNamedTransferFn::kPQ},
-};
-
/* Encode API-0 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jpegr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality,
- jr_exif_ptr exif) {
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif) {
if (uncompressed_p010_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -124,7 +107,7 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- jpegr_metadata metadata;
+ jpegr_metadata_struct metadata;
metadata.version = kJpegrVersion;
jpegr_uncompressed_struct uncompressed_yuv_420_image;
@@ -145,15 +128,14 @@
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
- sk_sp<SkData> icc = SkWriteICCProfile(
- jrTransFunc_to_skTransFunc.at(JPEGR_TF_SRGB),
- jrGamut_to_skGamut.at(uncompressed_yuv_420_image.colorGamut));
+ sp<DataStruct> icc = IccHelper::writeIccProfile(JPEGR_TF_SRGB,
+ uncompressed_yuv_420_image.colorGamut);
JpegEncoderHelper jpeg_encoder;
if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
uncompressed_yuv_420_image.width,
uncompressed_yuv_420_image.height, quality,
- icc.get()->data(), icc.get()->size())) {
+ icc->getData(), icc->getLength())) {
return ERROR_JPEGR_ENCODE_ERROR;
}
jpegr_compressed_struct jpeg;
@@ -167,11 +149,11 @@
/* Encode API-1 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- jpegr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality,
- jr_exif_ptr exif) {
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif) {
if (uncompressed_p010_image == nullptr
|| uncompressed_yuv_420_image == nullptr
|| dest == nullptr) {
@@ -194,7 +176,7 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- jpegr_metadata metadata;
+ jpegr_metadata_struct metadata;
metadata.version = kJpegrVersion;
jpegr_uncompressed_struct map;
@@ -209,15 +191,14 @@
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
- sk_sp<SkData> icc = SkWriteICCProfile(
- jrTransFunc_to_skTransFunc.at(JPEGR_TF_SRGB),
- jrGamut_to_skGamut.at(uncompressed_yuv_420_image->colorGamut));
+ sp<DataStruct> icc = IccHelper::writeIccProfile(JPEGR_TF_SRGB,
+ uncompressed_yuv_420_image->colorGamut);
JpegEncoderHelper jpeg_encoder;
if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
uncompressed_yuv_420_image->width,
uncompressed_yuv_420_image->height, quality,
- icc.get()->data(), icc.get()->size())) {
+ icc->getData(), icc->getLength())) {
return ERROR_JPEGR_ENCODE_ERROR;
}
jpegr_compressed_struct jpeg;
@@ -231,10 +212,10 @@
/* Encode API-2 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_compressed_ptr compressed_jpeg_image,
- jpegr_transfer_function hdr_tf,
- jr_compressed_ptr dest) {
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest) {
if (uncompressed_p010_image == nullptr
|| uncompressed_yuv_420_image == nullptr
|| compressed_jpeg_image == nullptr
@@ -254,7 +235,7 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- jpegr_metadata metadata;
+ jpegr_metadata_struct metadata;
metadata.version = kJpegrVersion;
jpegr_uncompressed_struct map;
@@ -276,9 +257,9 @@
/* Encode API-3 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_compressed_ptr compressed_jpeg_image,
- jpegr_transfer_function hdr_tf,
- jr_compressed_ptr dest) {
+ jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest) {
if (uncompressed_p010_image == nullptr
|| compressed_jpeg_image == nullptr
|| dest == nullptr) {
@@ -307,7 +288,7 @@
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
- jpegr_metadata metadata;
+ jpegr_metadata_struct metadata;
metadata.version = kJpegrVersion;
jpegr_uncompressed_struct map;
@@ -327,8 +308,7 @@
return NO_ERROR;
}
-status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
- jr_info_ptr jpegr_info) {
+status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_ptr jpegr_info) {
if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -349,16 +329,16 @@
/* Decode API */
status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
- jr_uncompressed_ptr dest,
- jr_exif_ptr exif,
- bool request_sdr) {
+ jr_uncompressed_ptr dest,
+ jr_exif_ptr exif,
+ jpegr_output_format output_format,
+ jr_uncompressed_ptr recovery_map,
+ jr_metadata_ptr metadata) {
if (compressed_jpegr_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- // TODO: fill EXIF data
- (void) exif;
- if (request_sdr) {
+ if (output_format == JPEGR_OUTPUT_SDR) {
JpegDecoderHelper jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
true)) {
@@ -372,21 +352,72 @@
uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
dest->width = uncompressed_rgba_image.width;
dest->height = uncompressed_rgba_image.height;
- return NO_ERROR;
+
+ if (recovery_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 (recovery_map == nullptr) {
+ return NO_ERROR;
+ }
}
jpegr_compressed_struct compressed_map;
- jpegr_metadata metadata;
JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
+ JpegDecoderHelper recovery_map_decoder;
+ if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ if (recovery_map != nullptr) {
+ recovery_map->width = recovery_map_decoder.getDecompressedImageWidth();
+ recovery_map->height = recovery_map_decoder.getDecompressedImageHeight();
+ int size = recovery_map->width * recovery_map->height;
+ recovery_map->data = malloc(size);
+ memcpy(recovery_map->data, recovery_map_decoder.getDecompressedImagePtr(), size);
+ }
+
+ jpegr_metadata_struct jr_metadata;
+ if (!getMetadataFromXMP(static_cast<uint8_t*>(recovery_map_decoder.getXMPPtr()),
+ recovery_map_decoder.getXMPSize(), &jr_metadata)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ if (metadata != nullptr) {
+ metadata->version = jr_metadata.version;
+ metadata->minContentBoost = jr_metadata.minContentBoost;
+ metadata->maxContentBoost = jr_metadata.maxContentBoost;
+ }
+
+ if (output_format == JPEGR_OUTPUT_SDR) {
+ return NO_ERROR;
+ }
+
JpegDecoderHelper jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
- JpegDecoderHelper recovery_map_decoder;
- if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
- return ERROR_JPEGR_DECODE_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();
}
jpegr_uncompressed_struct map;
@@ -399,17 +430,13 @@
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
- if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
- jpeg_decoder.getXMPSize(), &metadata)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
-
- JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+ JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &jr_metadata, output_format,
+ dest));
return NO_ERROR;
}
status_t JpegR::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
- jr_compressed_ptr dest) {
+ jr_compressed_ptr dest) {
if (uncompressed_recovery_map == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -493,10 +520,10 @@
}
status_t JpegR::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_uncompressed_ptr uncompressed_p010_image,
- jpegr_transfer_function hdr_tf,
- jr_metadata_ptr metadata,
- jr_uncompressed_ptr dest) {
+ jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_p010_image == nullptr
|| metadata == nullptr
@@ -637,9 +664,10 @@
}
status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_uncompressed_ptr uncompressed_recovery_map,
- jr_metadata_ptr metadata,
- jr_uncompressed_ptr dest) {
+ jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_metadata_ptr metadata,
+ jpegr_output_format output_format,
+ jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_recovery_map == nullptr
|| metadata == nullptr
@@ -654,18 +682,12 @@
JobQueue jobQueue;
std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
- metadata, dest, &jobQueue, &idwTable,
+ metadata, dest, &jobQueue, &idwTable, output_format,
&recoveryLUT]() -> void {
const float hdr_ratio = metadata->maxContentBoost;
size_t width = uncompressed_yuv_420_image->width;
size_t height = uncompressed_yuv_420_image->height;
-#if USE_HLG_OETF_LUT
- ColorTransformFn hdrOetf = hlgOetfLUT;
-#else
- ColorTransformFn hdrOetf = hlgOetf;
-#endif
-
size_t rowStart, rowEnd;
while (jobQueue.dequeueJob(rowStart, rowEnd)) {
for (size_t y = rowStart; y < rowEnd; ++y) {
@@ -693,11 +715,44 @@
#else
Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata);
#endif
- Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);
- uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
-
+ rgb_hdr = rgb_hdr / metadata->maxContentBoost;
size_t pixel_idx = x + y * width;
- reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
+
+ switch (output_format) {
+ case JPEGR_OUTPUT_HDR_LINEAR:
+ {
+ uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
+ reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16;
+ break;
+ }
+ case JPEGR_OUTPUT_HDR_HLG:
+ {
+#if USE_HLG_OETF_LUT
+ ColorTransformFn hdrOetf = hlgOetfLUT;
+#else
+ ColorTransformFn hdrOetf = hlgOetf;
+#endif
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+ uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
+ reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
+ break;
+ }
+ case JPEGR_OUTPUT_HDR_PQ:
+ {
+#if USE_HLG_OETF_LUT
+ ColorTransformFn hdrOetf = pqOetfLUT;
+#else
+ ColorTransformFn hdrOetf = pqOetf;
+#endif
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+ uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
+ reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
+ break;
+ }
+ default:
+ {}
+ // Should be impossible to hit after input validation.
+ }
}
}
}
@@ -721,8 +776,8 @@
}
status_t JpegR::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr primary_image,
- jr_compressed_ptr recovery_map) {
+ jr_compressed_ptr primary_image,
+ jr_compressed_ptr recovery_map) {
if (compressed_jpegr_image == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -771,7 +826,7 @@
status_t JpegR::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr dest) {
+ jr_compressed_ptr dest) {
if (compressed_jpegr_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -790,11 +845,22 @@
// (Required, XMP package) APP1 (ff e1)
// 2 bytes of length (2 + 29 + length of xmp package)
// name space ("http://ns.adobe.com/xap/1.0/\0")
-// xmp
+// XMP
+//
+// (Required, MPF package) APP2 (ff e2)
+// 2 bytes of length
+// MPF
//
// (Required) primary image (without the first two bytes (SOI), may have other packages)
//
-// (Required) secondary image (the recovery map)
+// SOI (ff d8)
+//
+// (Required, XMP package) APP1 (ff e1)
+// 2 bytes of length (2 + 29 + length of xmp package)
+// name space ("http://ns.adobe.com/xap/1.0/\0")
+// XMP
+//
+// (Required) secondary image (the recovery map, without the first two bytes (SOI))
//
// Metadata versions we are using:
// ECMA TR-98 for JFIF marker
@@ -802,10 +868,10 @@
// Adobe XMP spec part 3 for XMP marker
// ICC v4.3 spec for ICC
status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
- jr_compressed_ptr compressed_recovery_map,
- jr_exif_ptr exif,
- jr_metadata_ptr metadata,
- jr_compressed_ptr dest) {
+ jr_compressed_ptr compressed_recovery_map,
+ jr_exif_ptr exif,
+ jr_metadata_ptr metadata,
+ jr_compressed_ptr dest) {
if (compressed_jpeg_image == nullptr
|| compressed_recovery_map == nullptr
|| metadata == nullptr
@@ -813,8 +879,25 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- int pos = 0;
+ const string nameSpace = "http://ns.adobe.com/xap/1.0/";
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+ // calculate secondary image length first, because the length will be written into the primary
+ // image xmp
+ const string xmp_secondary = generateXmpForSecondaryImage(*metadata);
+ const int xmp_secondary_length = 2 /* 2 bytes representing the length of the package */
+ + nameSpaceLength /* 29 bytes length of name space including \0 */
+ + xmp_secondary.size(); /* length of xmp packet */
+ const int secondary_image_size = 2 /* 2 bytes length of APP1 sign */
+ + xmp_secondary_length
+ + compressed_recovery_map->length;
+ // primary image
+ const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size);
+ // same as primary
+ const int xmp_primary_length = 2 + nameSpaceLength + xmp_primary.size();
+
+ int pos = 0;
+ // Begin primary image
// Write SOI
JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
@@ -833,13 +916,7 @@
// Prepare and write XMP
{
- const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
- const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
- // 2 bytes: representing the length of the package
- // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
- // x bytes: length of xmp packet
- const int length = 2 + nameSpaceLength + xmp.size();
+ const int length = xmp_primary_length;
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));
@@ -847,15 +924,57 @@
JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
- JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
+ JPEGR_CHECK(Write(dest, (void*)xmp_primary.c_str(), xmp_primary.size(), pos));
+ }
+
+ // Prepare and write MPF
+ {
+ const int length = 2 + calculateMpfSize();
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+ int primary_image_size = pos + length + compressed_jpeg_image->length;
+ // between APP2 + package size + signature
+ // ff e2 00 58 4d 50 46 00
+ // 2 + 2 + 4 = 8 (bytes)
+ // and ff d8 sign of the secondary image
+ int secondary_image_offset = primary_image_size - pos - 8;
+ sp<DataStruct> mpf = generateMpf(primary_image_size,
+ 0, /* primary_image_offset */
+ secondary_image_size,
+ secondary_image_offset);
+ 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, (void*)mpf->getData(), mpf->getLength(), pos));
}
// Write primary image
JPEGR_CHECK(Write(dest,
(uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
+ // Finish primary image
+
+ // Begin secondary image (recovery map)
+ // Write SOI
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
+
+ // Prepare and write XMP
+ {
+ const int length = xmp_secondary_length;
+ 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::kAPP1, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
+ JPEGR_CHECK(Write(dest, (void*)xmp_secondary.c_str(), xmp_secondary.size(), pos));
+ }
// Write secondary image
- JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
+ JPEGR_CHECK(Write(dest,
+ (uint8_t*)compressed_recovery_map->data + 2, compressed_recovery_map->length - 2, pos));
// Set back length
dest->length = pos;
@@ -864,8 +983,7 @@
return NO_ERROR;
}
-status_t JpegR::toneMap(jr_uncompressed_ptr src,
- jr_uncompressed_ptr dest) {
+status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) {
if (src == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
diff --git a/libs/jpegrecoverymap/jpegrutils.cpp b/libs/jpegrecoverymap/jpegrutils.cpp
index 49526c8..ff96447 100644
--- a/libs/jpegrecoverymap/jpegrutils.cpp
+++ b/libs/jpegrecoverymap/jpegrutils.cpp
@@ -15,18 +15,19 @@
*/
#include <jpegrecoverymap/jpegrutils.h>
+#include <utils/Log.h>
#include <image_io/xml/xml_reader.h>
#include <image_io/xml/xml_writer.h>
#include <image_io/base/message_handler.h>
#include <image_io/xml/xml_element_rules.h>
#include <image_io/xml/xml_handler.h>
#include <image_io/xml/xml_rule.h>
+#include <cmath>
using namespace photos_editing_formats::image_io;
using namespace std;
namespace android::jpegrecoverymap {
-
/*
* Helper function used for generating XMP metadata.
*
@@ -34,12 +35,62 @@
* @param suffix The suffix part of the name.
* @return A name of the form "prefix:suffix".
*/
-string Name(const string &prefix, const string &suffix) {
+static inline string Name(const string &prefix, const string &suffix) {
std::stringstream ss;
ss << prefix << ":" << suffix;
return ss.str();
}
+DataStruct::DataStruct(int s) {
+ data = malloc(s);
+ length = s;
+ memset(data, 0, s);
+ writePos = 0;
+}
+
+DataStruct::~DataStruct() {
+ if (data != nullptr) {
+ free(data);
+ }
+}
+
+void* DataStruct::getData() {
+ return data;
+}
+
+int DataStruct::getLength() {
+ return length;
+}
+
+int DataStruct::getBytesWritten() {
+ return writePos;
+}
+
+bool DataStruct::write8(uint8_t value) {
+ uint8_t v = value;
+ return write(&v, 1);
+}
+
+bool DataStruct::write16(uint16_t value) {
+ uint16_t v = value;
+ return write(&v, 2);
+}
+bool DataStruct::write32(uint32_t value) {
+ uint32_t v = value;
+ return write(&v, 4);
+}
+
+bool DataStruct::write(const void* src, int size) {
+ if (writePos + size > length) {
+ ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d",
+ writePos, size, length);
+ return false;
+ }
+ memcpy((uint8_t*) data + writePos, src, size);
+ writePos += size;
+ return true;
+}
+
/*
* Helper function used for writing data to destination.
*/
@@ -58,7 +109,7 @@
public:
XMPXmlHandler() : XmlHandler() {
- gContainerItemState = NotStrarted;
+ state = NotStrarted;
}
enum ParseState {
@@ -70,11 +121,11 @@
virtual DataMatchResult StartElement(const XmlTokenContext& context) {
string val;
if (context.BuildTokenValue(&val)) {
- if (!val.compare(gContainerItemName)) {
- gContainerItemState = Started;
+ if (!val.compare(containerName)) {
+ state = Started;
} else {
- if (gContainerItemState != Done) {
- gContainerItemState = NotStrarted;
+ if (state != Done) {
+ state = NotStrarted;
}
}
}
@@ -82,8 +133,8 @@
}
virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
- if (gContainerItemState == Started) {
- gContainerItemState = Done;
+ if (state == Started) {
+ state = Done;
lastAttributeName = "";
}
return context.GetResult();
@@ -91,7 +142,7 @@
virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
string val;
- if (gContainerItemState == Started) {
+ if (state == Started) {
if (context.BuildTokenValue(&val)) {
if (!val.compare(maxContentBoostAttrName)) {
lastAttributeName = maxContentBoostAttrName;
@@ -107,7 +158,7 @@
virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
string val;
- if (gContainerItemState == Started) {
+ if (state == Started) {
if (context.BuildTokenValue(&val, true)) {
if (!lastAttributeName.compare(maxContentBoostAttrName)) {
maxContentBoostStr = val;
@@ -120,11 +171,11 @@
}
bool getMaxContentBoost(float* max_content_boost) {
- if (gContainerItemState == Done) {
+ if (state == Done) {
stringstream ss(maxContentBoostStr);
float val;
if (ss >> val) {
- *max_content_boost = val;
+ *max_content_boost = exp2(val);
return true;
} else {
return false;
@@ -135,11 +186,11 @@
}
bool getMinContentBoost(float* min_content_boost) {
- if (gContainerItemState == Done) {
+ if (state == Done) {
stringstream ss(minContentBoostStr);
float val;
if (ss >> val) {
- *min_content_boost = val;
+ *min_content_boost = exp2(val);
return true;
} else {
return false;
@@ -150,13 +201,13 @@
}
private:
- static const string gContainerItemName;
+ static const string containerName;
static const string maxContentBoostAttrName;
string maxContentBoostStr;
static const string minContentBoostAttrName;
string minContentBoostStr;
string lastAttributeName;
- ParseState gContainerItemState;
+ ParseState state;
};
// GContainer XMP constants - URI and namespace prefix
@@ -168,8 +219,7 @@
const string kConItem = Name(kContainerPrefix, "Item");
// GContainer XMP constants - names for XMP handlers
-const string XMPXmlHandler::gContainerItemName = kConItem;
-
+const string XMPXmlHandler::containerName = "rdf:Description";
// Item XMP constants - URI and namespace prefix
const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
const string kItemPrefix = "Item";
@@ -185,19 +235,25 @@
const string kMimeImageJpeg = "image/jpeg";
// RecoveryMap XMP constants - URI and namespace prefix
-const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/";
-const string kRecoveryMapPrefix = "RecoveryMap";
+const string kRecoveryMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/";
+const string kRecoveryMapPrefix = "hdrgm";
// RecoveryMap XMP constants - element and attribute names
-const string kMapMaxContentBoost = Name(kRecoveryMapPrefix, "MaxContentBoost");
-const string kMapMinContentBoost = Name(kRecoveryMapPrefix, "MinContentBoost");
const string kMapVersion = Name(kRecoveryMapPrefix, "Version");
+const string kMapGainMapMin = Name(kRecoveryMapPrefix, "GainMapMin");
+const string kMapGainMapMax = Name(kRecoveryMapPrefix, "GainMapMax");
+const string kMapGamma = Name(kRecoveryMapPrefix, "Gamma");
+const string kMapOffsetSdr = Name(kRecoveryMapPrefix, "OffsetSDR");
+const string kMapOffsetHdr = Name(kRecoveryMapPrefix, "OffsetHDR");
+const string kMapHDRCapacityMin = Name(kRecoveryMapPrefix, "HDRCapacityMin");
+const string kMapHDRCapacityMax = Name(kRecoveryMapPrefix, "HDRCapacityMax");
+const string kMapBaseRendition = Name(kRecoveryMapPrefix, "BaseRendition");
// RecoveryMap XMP constants - names for XMP handlers
-const string XMPXmlHandler::maxContentBoostAttrName = kMapMaxContentBoost;
-const string XMPXmlHandler::minContentBoostAttrName = kMapMinContentBoost;
+const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
+const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata_struct* metadata) {
string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
if (xmp_size < nameSpace.size()+2) {
@@ -243,7 +299,7 @@
return true;
}
-string generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
+string generateXmpForPrimaryImage(int secondary_image_length) {
const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
const vector<string> kLiItem({string("rdf:li"), kConItem});
@@ -257,7 +313,6 @@
writer.StartWritingElement("rdf:Description");
writer.WriteXmlns(kContainerPrefix, kContainerUri);
writer.WriteXmlns(kItemPrefix, kItemUri);
- writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
writer.StartWritingElements(kConDirSeq);
size_t item_depth = writer.StartWritingElements(kLiItem);
writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
@@ -267,9 +322,33 @@
writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticRecoveryMap);
writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+ writer.FinishWriting();
+
+ return ss.str();
+}
+
+string generateXmpForSecondaryImage(jpegr_metadata_struct& metadata) {
+ const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
+ const vector<string> kLiItem({string("rdf:li"), kConItem});
+
+ std::stringstream ss;
+ photos_editing_formats::image_io::XmlWriter writer(ss);
+ writer.StartWritingElement("x:xmpmeta");
+ writer.WriteXmlns("x", "adobe:ns:meta/");
+ writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+ writer.StartWritingElement("rdf:RDF");
+ writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ writer.StartWritingElement("rdf:Description");
+ writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
- writer.WriteAttributeNameAndValue(kMapMaxContentBoost, metadata.maxContentBoost);
- writer.WriteAttributeNameAndValue(kMapMinContentBoost, metadata.minContentBoost);
+ 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, "0");
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, "2.3");
+ writer.WriteAttributeNameAndValue(kMapBaseRendition, "SDR");
writer.FinishWriting();
return ss.str();
diff --git a/libs/jpegrecoverymap/multipictureformat.cpp b/libs/jpegrecoverymap/multipictureformat.cpp
new file mode 100644
index 0000000..a219aef
--- /dev/null
+++ b/libs/jpegrecoverymap/multipictureformat.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 <jpegrecoverymap/multipictureformat.h>
+#include <jpegrecoverymap/jpegrutils.h>
+
+namespace android::jpegrecoverymap {
+size_t calculateMpfSize() {
+ return sizeof(kMpfSig) + // Signature
+ kMpEndianSize + // Endianness
+ sizeof(uint32_t) + // Index IFD Offset
+ sizeof(uint16_t) + // Tag count
+ kTagSerializedCount * kTagSize + // 3 tags at 12 bytes each
+ sizeof(uint32_t) + // Attribute IFD offset
+ kNumPictures * kMPEntrySize; // MP Entries for each image
+}
+
+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);
+
+ dataStruct->write(static_cast<const void*>(kMpfSig), sizeof(kMpfSig));
+#if USE_BIG_ENDIAN
+ dataStruct->write(static_cast<const void*>(kMpBigEndian), kMpEndianSize);
+#else
+ dataStruct->write(static_cast<const void*>(kMpLittleEndian), kMpEndianSize);
+#endif
+
+ // Set the Index IFD offset be the position after the endianness value and this offset.
+ constexpr uint32_t indexIfdOffset =
+ static_cast<uint16_t>(kMpEndianSize + sizeof(kMpfSig));
+ dataStruct->write32(Endian_SwapBE32(indexIfdOffset));
+
+ // We will write 3 tags (version, number of images, MP entries).
+ dataStruct->write16(Endian_SwapBE16(kTagSerializedCount));
+
+ // Write the version tag.
+ dataStruct->write16(Endian_SwapBE16(kVersionTag));
+ dataStruct->write16(Endian_SwapBE16(kVersionType));
+ dataStruct->write32(Endian_SwapBE32(kVersionCount));
+ dataStruct->write(kVersionExpected, kVersionSize);
+
+ // Write the number of images.
+ dataStruct->write16(Endian_SwapBE16(kNumberOfImagesTag));
+ dataStruct->write16(Endian_SwapBE16(kNumberOfImagesType));
+ dataStruct->write32(Endian_SwapBE32(kNumberOfImagesCount));
+ dataStruct->write32(Endian_SwapBE32(kNumPictures));
+
+ // Write the MP entries.
+ dataStruct->write16(Endian_SwapBE16(kMPEntryTag));
+ dataStruct->write16(Endian_SwapBE16(kMPEntryType));
+ dataStruct->write32(Endian_SwapBE32(kMPEntrySize * kNumPictures));
+ const uint32_t mpEntryOffset =
+ static_cast<uint32_t>(dataStruct->getBytesWritten() - // The bytes written so far
+ sizeof(kMpfSig) + // Excluding the MPF signature
+ sizeof(uint32_t) + // The 4 bytes for this offset
+ sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset.
+ dataStruct->write32(Endian_SwapBE32(mpEntryOffset));
+
+ // Write the attribute IFD offset (zero because we don't write it).
+ dataStruct->write32(0);
+
+ // Write the MP entries for primary image
+ dataStruct->write32(
+ Endian_SwapBE32(kMPEntryAttributeFormatJpeg | kMPEntryAttributeTypePrimary));
+ dataStruct->write32(Endian_SwapBE32(primary_image_size));
+ dataStruct->write32(Endian_SwapBE32(primary_image_offset));
+ dataStruct->write16(0);
+ dataStruct->write16(0);
+
+ // Write the MP entries for secondary image
+ dataStruct->write32(Endian_SwapBE32(kMPEntryAttributeFormatJpeg));
+ dataStruct->write32(Endian_SwapBE32(secondary_image_size));
+ dataStruct->write32(Endian_SwapBE32(secondary_image_offset));
+ dataStruct->write16(0);
+ dataStruct->write16(0);
+
+ return dataStruct;
+}
+
+} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 7812e18..20c32ed 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -631,4 +631,11 @@
| (0x3 << 30); // Set alpha to 1.0
}
+uint64_t colorToRgbaF16(Color e_gamma) {
+ return (uint64_t) floatToHalf(e_gamma.r)
+ | (((uint64_t) floatToHalf(e_gamma.g)) << 16)
+ | (((uint64_t) floatToHalf(e_gamma.b)) << 32)
+ | (((uint64_t) floatToHalf(1.0f)) << 48);
+}
+
} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index 5a4edb2..d5da7fb 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -39,7 +39,7 @@
"libjpegdecoder",
"libjpegencoder",
"libjpegrecoverymap",
- "libskia",
+ "libutils",
],
}
diff --git a/libs/jpegrecoverymap/tests/jpegr_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp
index 7a3133d..be4b972 100644
--- a/libs/jpegrecoverymap/tests/jpegr_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegr_test.cpp
@@ -152,7 +152,7 @@
timerStart(&applyRecMapTime);
for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest));
+ ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, JPEGR_OUTPUT_HDR_HLG, dest));
}
timerStop(&applyRecMapTime);
@@ -170,18 +170,17 @@
jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
nullptr);
jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
- jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr, false);
+ jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr);
}
TEST_F(JpegRTest, writeXmpThenRead) {
- jpegr_metadata metadata_expected;
+ jpegr_metadata_struct metadata_expected;
metadata_expected.maxContentBoost = 1.25;
metadata_expected.minContentBoost = 0.75;
- int length_expected = 1000;
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 = generateXmp(1000, metadata_expected);
+ std::string xmp = generateXmpForSecondaryImage(metadata_expected);
std::vector<uint8_t> xmpData;
xmpData.reserve(nameSpaceLength + xmp.size());
@@ -190,7 +189,7 @@
xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
- jpegr_metadata metadata_read;
+ jpegr_metadata_struct metadata_read;
EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
ASSERT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
ASSERT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
@@ -220,7 +219,7 @@
}
if (SAVE_ENCODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ 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());
@@ -229,7 +228,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -237,7 +236,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ 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());
@@ -281,7 +280,7 @@
}
if (SAVE_ENCODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ 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());
@@ -290,7 +289,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -298,7 +297,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ 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());
@@ -346,7 +345,7 @@
}
if (SAVE_ENCODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ 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());
@@ -355,7 +354,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -363,7 +362,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ 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());
@@ -427,7 +426,7 @@
}
if (SAVE_ENCODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ 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());
@@ -436,7 +435,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -444,7 +443,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ 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());
@@ -477,7 +476,7 @@
JpegRBenchmark benchmark;
- jpegr_metadata metadata = { .version = 1,
+ jpegr_metadata_struct metadata = { .version = 1,
.maxContentBoost = 8.0f,
.minContentBoost = 1.0f / 8.0f };
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
index 6c61ff1..cf6a034 100644
--- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -554,8 +554,8 @@
TEST_F(RecoveryMapMathTest, applyRecoveryLUT) {
for (int boost = 1; boost <= 10; boost++) {
- jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f / static_cast<float>(boost) };
+ jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+ .minContentBoost = 1.0f / static_cast<float>(boost) };
RecoveryLUT recoveryLUT(&metadata);
for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
@@ -573,8 +573,8 @@
}
for (int boost = 1; boost <= 10; boost++) {
- jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f };
+ jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+ .minContentBoost = 1.0f };
RecoveryLUT recoveryLUT(&metadata);
for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
@@ -592,8 +592,8 @@
}
for (int boost = 1; boost <= 10; boost++) {
- jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f / pow(static_cast<float>(boost),
+ jpegr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
+ .minContentBoost = 1.0f / pow(static_cast<float>(boost),
1.0f / 3.0f) };
RecoveryLUT recoveryLUT(&metadata);
for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
@@ -659,8 +659,8 @@
}
TEST_F(RecoveryMapMathTest, EncodeRecovery) {
- jpegr_metadata metadata = { .maxContentBoost = 4.0f,
- .minContentBoost = 1.0f / 4.0f };
+ jpegr_metadata_struct metadata = { .maxContentBoost = 4.0f,
+ .minContentBoost = 1.0f / 4.0f };
EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 127);
EXPECT_EQ(encodeRecovery(0.0f, 1.0f, &metadata), 127);
@@ -717,8 +717,8 @@
}
TEST_F(RecoveryMapMathTest, ApplyRecovery) {
- jpegr_metadata metadata = { .maxContentBoost = 4.0f,
- .minContentBoost = 1.0f / 4.0f };
+ jpegr_metadata_struct metadata = { .maxContentBoost = 4.0f,
+ .minContentBoost = 1.0f / 4.0f };
EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, &metadata), RgbBlack());
EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.5f, &metadata), RgbBlack());
@@ -981,8 +981,8 @@
}
TEST_F(RecoveryMapMathTest, ApplyMap) {
- jpegr_metadata metadata = { .maxContentBoost = 8.0f,
- .minContentBoost = 1.0f / 8.0f };
+ jpegr_metadata_struct metadata = { .maxContentBoost = 8.0f,
+ .minContentBoost = 1.0f / 8.0f };
EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
RgbWhite() * 8.0f);
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index b7b2926..5306529 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -24,14 +24,49 @@
#include <private/android/AHardwareBufferHelpers.h>
+#include <android/binder_libbinder.h>
+#include <dlfcn.h>
#include <log/log.h>
#include <ui/GraphicBuffer.h>
-#include <gui/Surface.h>
-#include <gui/view/Surface.h>
-#include <android/binder_libbinder.h>
using namespace android;
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error libnativewindow can only be built for system
+#endif
+
+using android_view_Surface_writeToParcel = status_t (*)(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel);
+
+using android_view_Surface_readFromParcel =
+ status_t (*)(const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow);
+
+struct SurfaceParcelables {
+ android_view_Surface_writeToParcel write = nullptr;
+ android_view_Surface_readFromParcel read = nullptr;
+};
+
+const SurfaceParcelables* getSurfaceParcelFunctions() {
+ static SurfaceParcelables funcs = []() -> SurfaceParcelables {
+ SurfaceParcelables ret;
+ void* dl = dlopen("libgui.so", RTLD_NOW);
+ LOG_ALWAYS_FATAL_IF(!dl, "Failed to find libgui.so");
+ ret.write =
+ (android_view_Surface_writeToParcel)dlsym(dl, "android_view_Surface_writeToParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.write,
+ "libgui.so missing android_view_Surface_writeToParcel; "
+ "loaded wrong libgui?");
+ ret.read =
+ (android_view_Surface_readFromParcel)dlsym(dl,
+ "android_view_Surface_readFromParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.read,
+ "libgui.so missing android_view_Surface_readFromParcel; "
+ "loaded wrong libgui?");
+ return ret;
+ }();
+ return &funcs;
+}
+
static int32_t query(ANativeWindow* window, int what) {
int value;
int res = window->query(window, what, &value);
@@ -64,13 +99,6 @@
return false;
}
}
-static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) {
- return Surface::getIGraphicBufferProducer(window);
-}
-
-static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) {
- return Surface::getSurfaceControlHandle(window);
-}
/**************************************************************************************************
* NDK
@@ -355,38 +383,24 @@
binder_status_t ANativeWindow_readFromParcel(
const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
- const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
-
- // Use a android::view::Surface to unparcel the window
- std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
- status_t ret = shimSurface->readFromParcel(nativeParcel);
- if (ret != OK) {
- ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
- return STATUS_BAD_VALUE;
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->read == nullptr) {
+ ALOGE("Failed to load Surface_readFromParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
}
- sp<Surface> surface = sp<Surface>::make(
- shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle);
- ANativeWindow* anw = surface.get();
- ANativeWindow_acquire(anw);
- *outWindow = anw;
- return STATUS_OK;
+ const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+ return funcs->read(nativeParcel, outWindow);
}
binder_status_t ANativeWindow_writeToParcel(
ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) {
- int value;
- int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
- if (err != OK || value != NATIVE_WINDOW_SURFACE) {
- ALOGE("Error: ANativeWindow is not backed by Surface");
- return STATUS_BAD_VALUE;
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->write == nullptr) {
+ ALOGE("Failed to load Surface_writeToParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
}
- // Use a android::view::Surface to parcelize the window
- std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
- shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window);
- shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window);
-
Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
- return shimSurface->writeToParcel(nativeParcel);
+ return funcs->write(window, nativeParcel);
}
/**************************************************************************************************
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
index 84771c0..210dca5 100644
--- a/libs/renderengine/ExternalTexture.cpp
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -39,7 +39,7 @@
}
ExternalTexture::~ExternalTexture() {
- mRenderEngine.unmapExternalTextureBuffer(mBuffer);
+ mRenderEngine.unmapExternalTextureBuffer(std::move(mBuffer));
}
} // namespace android::renderengine::impl
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 13f766c..0d7df10 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -800,7 +800,7 @@
return NO_ERROR;
}
-void GLESRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void GLESRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
mImageManager->releaseAsync(buffer->getId(), nullptr);
}
@@ -1262,7 +1262,7 @@
// Do not cache protected EGLImage, protected memory is limited.
if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
- unmapExternalTextureBuffer(gBuf);
+ unmapExternalTextureBuffer(std::move(gBuf));
}
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 1b34921..402ff52 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -101,7 +101,7 @@
size_t getMaxViewportDims() const override;
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
EXCLUDES(mRenderingMutex);
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
+ void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) EXCLUDES(mRenderingMutex);
bool canSkipPostRenderCleanup() const override;
void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display,
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 39621cd..0d910c9 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -231,7 +231,7 @@
// asynchronously, but the caller can expect that map/unmap calls are performed in a manner
// that's conflict serializable, i.e. unmap a buffer should never occur before binding the
// buffer if the caller called mapExternalTextureBuffer before calling unmap.
- virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+ virtual void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) = 0;
// A thread safe query to determine if any post rendering cleanup is necessary. Returning true
// is a signal that calling the postRenderCleanup method would be a no-op and that callers can
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index e3ce85d..d3035e2 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -63,7 +63,7 @@
protected:
// mock renderengine still needs to implement these, but callers should never need to call them.
void mapExternalTextureBuffer(const sp<GraphicBuffer>&, bool) {}
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>&) {}
+ void unmapExternalTextureBuffer(sp<GraphicBuffer>&&) {}
};
} // namespace mock
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 413811e..5965d41 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -423,7 +423,7 @@
}
}
-void SkiaRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void SkiaRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mRenderingMutex);
if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 1973c7d..dd6646b 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -130,7 +130,7 @@
private:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) override final;
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override final;
+ void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) override final;
bool canSkipPostRenderCleanup() const override final;
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 8d99f3d..936e316 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -444,8 +444,11 @@
ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent);
VkQueue graphicsQueue;
- VK_GET_DEV_PROC(device, GetDeviceQueue);
- vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue);
+ VK_GET_DEV_PROC(device, GetDeviceQueue2);
+ const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr,
+ deviceQueueCreateFlags,
+ (uint32_t)graphicsQueueIndex, 0};
+ vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue);
VK_GET_DEV_PROC(device, DeviceWaitIdle);
VK_GET_DEV_PROC(device, DestroyDevice);
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 8aa41b3..6a1561a 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -230,16 +230,17 @@
mCondition.notify_one();
}
-void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void RenderEngineThreaded::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
- instance.unmapExternalTextureBuffer(buffer);
- });
+ mFunctionCalls.push(
+ [=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable {
+ ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+ instance.unmapExternalTextureBuffer(std::move(buffer));
+ });
}
mCondition.notify_one();
}
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 168e2d2..6eb108e 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -69,7 +69,7 @@
protected:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) override;
bool canSkipPostRenderCleanup() const override;
void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display,
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 2278d39..019d6cb 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -67,7 +67,11 @@
v.setCapacity(n);
while (n) {
n--;
- reply.read(s);
+ if(reply.read(s) != OK) {
+ ALOGE("Failed to read reply from getSensorList");
+ v.clear();
+ break;
+ }
v.add(s);
}
return v;
@@ -85,7 +89,11 @@
v.setCapacity(n);
while (n) {
n--;
- reply.read(s);
+ if(reply.read(s) != OK) {
+ ALOGE("Failed to read reply from getDynamicSensorList");
+ v.clear();
+ break;
+ }
v.add(s);
}
return v;
@@ -131,10 +139,12 @@
}
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
- uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) {
+ int deviceId, uint32_t size, int32_t type, int32_t format,
+ const native_handle_t *resource) {
Parcel data, reply;
data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
data.writeString16(opPackageName);
+ data.writeInt32(deviceId);
data.writeUint32(size);
data.writeInt32(type);
data.writeInt32(format);
@@ -229,6 +239,7 @@
case CREATE_SENSOR_DIRECT_CONNECTION: {
CHECK_INTERFACE(ISensorServer, data, reply);
const String16& opPackageName = data.readString16();
+ const int deviceId = data.readInt32();
uint32_t size = data.readUint32();
int32_t type = data.readInt32();
int32_t format = data.readInt32();
@@ -238,8 +249,8 @@
return BAD_VALUE;
}
native_handle_set_fdsan_tag(resource);
- sp<ISensorEventConnection> ch =
- createSensorDirectConnection(opPackageName, size, type, format, resource);
+ sp<ISensorEventConnection> ch = createSensorDirectConnection(
+ opPackageName, deviceId, size, type, format, resource);
native_handle_close_with_tag(resource);
native_handle_delete(resource);
reply->writeStrongBinder(IInterface::asBinder(ch));
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index fb895f5..b6ea77d 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -628,7 +628,13 @@
return false;
}
outputString8.setTo(static_cast<char const*>(buffer), len);
+
+ if (size < FlattenableUtils::align<4>(len)) {
+ ALOGE("Malformed Sensor String8 field. Should be in a 4-byte aligned buffer but is not.");
+ return false;
+ }
FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+
return true;
}
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 2748276..ba190e0 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -92,6 +92,16 @@
return *sensorManager;
}
+void SensorManager::removeInstanceForPackage(const String16& packageName) {
+ Mutex::Autolock _l(sLock);
+ auto iterator = sPackageInstances.find(packageName);
+ if (iterator != sPackageInstances.end()) {
+ SensorManager* sensorManager = iterator->second;
+ delete sensorManager;
+ sPackageInstances.erase(iterator);
+ }
+}
+
SensorManager::SensorManager(const String16& opPackageName)
: mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
Mutex::Autolock _l(mLock);
@@ -166,6 +176,11 @@
mSensors = mSensorServer->getSensorList(mOpPackageName);
size_t count = mSensors.size();
+ if (count == 0) {
+ ALOGE("Failed to get Sensor list");
+ mSensorServer.clear();
+ return UNKNOWN_ERROR;
+ }
mSensorList =
static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL");
@@ -300,6 +315,12 @@
int SensorManager::createDirectChannel(
size_t size, int channelType, const native_handle_t *resourceHandle) {
+ static constexpr int DEFAULT_DEVICE_ID = 0;
+ return createDirectChannel(DEFAULT_DEVICE_ID, size, channelType, resourceHandle);
+}
+
+int SensorManager::createDirectChannel(
+ int deviceId, size_t size, int channelType, const native_handle_t *resourceHandle) {
Mutex::Autolock _l(mLock);
if (assertStateLocked() != NO_ERROR) {
return NO_INIT;
@@ -312,7 +333,7 @@
}
sp<ISensorEventConnection> conn =
- mSensorServer->createSensorDirectConnection(mOpPackageName,
+ mSensorServer->createSensorDirectConnection(mOpPackageName, deviceId,
static_cast<uint32_t>(size),
static_cast<int32_t>(channelType),
SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle);
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
index 3295196..5815728 100644
--- a/libs/sensor/include/sensor/ISensorServer.h
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -50,7 +50,8 @@
virtual int32_t isDataInjectionEnabled() = 0;
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
- uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0;
+ int deviceId, uint32_t size, int32_t type, int32_t format,
+ const native_handle_t *resource) = 0;
virtual int setOperationParameter(
int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) = 0;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 0798da2..bb44cb8 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -54,6 +54,7 @@
{
public:
static SensorManager& getInstanceForPackage(const String16& packageName);
+ static void removeInstanceForPackage(const String16& packageName);
~SensorManager();
ssize_t getSensorList(Sensor const* const** list);
@@ -65,6 +66,8 @@
String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
bool isDataInjectionEnabled();
int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
+ int createDirectChannel(
+ int deviceId, size_t size, int channelType, const native_handle_t *channelData);
void destroyDirectChannel(int channelNativeHandle);
int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel);
int setOperationParameter(int handle, int type, const Vector<float> &floats, const Vector<int32_t> &ints);
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 7459466..c3af996 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -22,6 +22,8 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_enums.h>
#include <android/binder_manager.h>
+#include <cutils/android_filesystem_config.h>
+#include <cutils/multiuser.h>
#include <gralloctypes/Gralloc4.h>
#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
@@ -1195,8 +1197,15 @@
mAllocator = IAllocator::getService();
if (__builtin_available(android 31, *)) {
if (hasIAllocatorAidl()) {
- mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
- AServiceManager_waitForService(kAidlAllocatorServiceName.c_str())));
+ // TODO(b/269517338): Perform the isolated checking for this in service manager instead.
+ uid_t aid = multiuser_get_app_id(getuid());
+ if (aid >= AID_ISOLATED_START && aid <= AID_ISOLATED_END) {
+ mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
+ AServiceManager_getService(kAidlAllocatorServiceName.c_str())));
+ } else {
+ mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
+ AServiceManager_waitForService(kAidlAllocatorServiceName.c_str())));
+ }
ALOGE_IF(!mAidlAllocator, "AIDL IAllocator declared but failed to get service");
}
}
diff --git a/opengl/OWNERS b/opengl/OWNERS
index 379f763..3d60a1d 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,11 +1,6 @@
-abdolrashidi@google.com
-cclao@google.com
chrisforbes@google.com
cnorthrop@google.com
ianelliott@google.com
jessehall@google.com
-lfy@google.com
lpy@google.com
-romanl@google.com
vantablack@google.com
-yuxinhu@google.com
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index 86c788d..aecfc6b 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -231,7 +231,7 @@
int BlobCache::unflatten(void const* buffer, size_t size) {
// All errors should result in the BlobCache being in an empty state.
- mCacheEntries.clear();
+ clear();
// Read the cache header
if (size < sizeof(Header)) {
@@ -258,7 +258,7 @@
size_t numEntries = header->mNumEntries;
for (size_t i = 0; i < numEntries; i++) {
if (byteOffset + sizeof(EntryHeader) > size) {
- mCacheEntries.clear();
+ clear();
ALOGE("unflatten: not enough room for cache entry headers");
return -EINVAL;
}
@@ -270,7 +270,7 @@
size_t totalSize = align4(entrySize);
if (byteOffset + totalSize > size) {
- mCacheEntries.clear();
+ clear();
ALOGE("unflatten: not enough room for cache entry headers");
return -EINVAL;
}
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index ff03d30..52078ff 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -117,7 +117,10 @@
// clear flushes out all contents of the cache then the BlobCache, leaving
// it in an empty state.
- void clear() { mCacheEntries.clear(); }
+ void clear() {
+ mCacheEntries.clear();
+ mTotalSize = 0;
+ }
protected:
// mMaxTotalSize is the maximum size that all cache entries can occupy. This
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index ceea0fb..450c128 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -466,4 +466,31 @@
ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
}
+// Test for a divide by zero bug (b/239862516). Before the fix, unflatten() would not reset
+// mTotalSize when it encountered an error, which would trigger division by 0 in clean() in the
+// right conditions.
+TEST_F(BlobCacheFlattenTest, SetAfterFailedUnflatten) {
+ // isCleanable() must be true, so mTotalSize must be > mMaxTotalSize / 2 after unflattening
+ // after one entry is lost. To make this the case, MaxTotalSize is 30 and three 10 sized
+ // entries are used. One of those entries is lost, resulting in mTotalSize=20
+ const size_t kMaxKeySize = 10;
+ const size_t kMaxValueSize = 10;
+ const size_t kMaxTotalSize = 30;
+ mBC.reset(new BlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize));
+ mBC2.reset(new BlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize));
+ mBC->set("aaaaa", 5, "aaaaa", 5);
+ mBC->set("bbbbb", 5, "bbbbb", 5);
+ mBC->set("ccccc", 5, "ccccc", 5);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size));
+
+ ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size - 10));
+ delete[] flat;
+
+ // This line will trigger clean() which caused a crash.
+ mBC2->set("dddddddddd", 10, "dddddddddd", 10);
+}
+
} // namespace android
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index b00ee33..3dc93ee 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -110,38 +110,6 @@
}
}
- // Check the device config to decide whether multifile should be used
- if (base::GetBoolProperty("ro.egl.blobcache.multifile", false)) {
- mMultifileMode = true;
- ALOGV("Using multifile EGL blobcache");
- }
-
- // Allow forcing the mode for debug purposes
- std::string mode = base::GetProperty("debug.egl.blobcache.multifile", "");
- if (mode == "true") {
- ALOGV("Forcing multifile cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
- mMultifileMode = true;
- } else if (mode == "false") {
- ALOGV("Forcing monolithic cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
- mMultifileMode = false;
- }
-
- if (mMultifileMode) {
- mCacheByteLimit = static_cast<size_t>(
- base::GetUintProperty<uint32_t>("ro.egl.blobcache.multifile_limit",
- kMultifileCacheByteLimit));
-
- // Check for a debug value
- int debugCacheSize = base::GetIntProperty("debug.egl.blobcache.multifile_limit", -1);
- if (debugCacheSize >= 0) {
- ALOGV("Overriding cache limit %zu with %i from debug.egl.blobcache.multifile_limit",
- mCacheByteLimit, debugCacheSize);
- mCacheByteLimit = debugCacheSize;
- }
-
- ALOGV("Using multifile EGL blobcache limit of %zu bytes", mCacheByteLimit);
- }
-
mInitialized = true;
}
@@ -167,6 +135,8 @@
return;
}
+ updateMode();
+
if (mInitialized) {
if (mMultifileMode) {
MultifileBlobCache* mbc = getMultifileBlobCacheLocked();
@@ -200,6 +170,8 @@
return 0;
}
+ updateMode();
+
if (mInitialized) {
if (mMultifileMode) {
MultifileBlobCache* mbc = getMultifileBlobCacheLocked();
@@ -247,6 +219,51 @@
return 0;
}
+void egl_cache_t::updateMode() {
+ // We don't set the mode in the constructor because these checks have
+ // a non-trivial cost, and not all processes that instantiate egl_cache_t
+ // will use it.
+
+ // If we've already set the mode, skip these checks
+ static bool checked = false;
+ if (checked) {
+ return;
+ }
+ checked = true;
+
+ // Check the device config to decide whether multifile should be used
+ if (base::GetBoolProperty("ro.egl.blobcache.multifile", false)) {
+ mMultifileMode = true;
+ ALOGV("Using multifile EGL blobcache");
+ }
+
+ // Allow forcing the mode for debug purposes
+ std::string mode = base::GetProperty("debug.egl.blobcache.multifile", "");
+ if (mode == "true") {
+ ALOGV("Forcing multifile cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
+ mMultifileMode = true;
+ } else if (mode == "false") {
+ ALOGV("Forcing monolithic cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
+ mMultifileMode = false;
+ }
+
+ if (mMultifileMode) {
+ mCacheByteLimit = static_cast<size_t>(
+ base::GetUintProperty<uint32_t>("ro.egl.blobcache.multifile_limit",
+ kMultifileCacheByteLimit));
+
+ // Check for a debug value
+ int debugCacheSize = base::GetIntProperty("debug.egl.blobcache.multifile_limit", -1);
+ if (debugCacheSize >= 0) {
+ ALOGV("Overriding cache limit %zu with %i from debug.egl.blobcache.multifile_limit",
+ mCacheByteLimit, debugCacheSize);
+ mCacheByteLimit = debugCacheSize;
+ }
+
+ ALOGV("Using multifile EGL blobcache limit of %zu bytes", mCacheByteLimit);
+ }
+}
+
BlobCache* egl_cache_t::getBlobCacheLocked() {
if (mBlobCache == nullptr) {
mBlobCache.reset(new FileBlobCache(kMaxMonolithicKeySize, kMaxMonolithicValueSize,
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 1399368..ae6d381 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -88,6 +88,9 @@
egl_cache_t(const egl_cache_t&); // not implemented
void operator=(const egl_cache_t&); // not implemented
+ // Check system properties to determine which blobcache mode should be used
+ void updateMode();
+
// getBlobCacheLocked returns the BlobCache object being used to store the
// key/value blob pairs. If the BlobCache object has not yet been created,
// this will do so, loading the serialized cache contents from disk if
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index a2e4115..bf6189d 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -37,6 +37,7 @@
BATTERY_PROP_CHARGING_POLICY = 7, // equals BATTERY_PROPERTY_CHARGING_POLICY
BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE
BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE
+ BATTERY_PROP_STATE_OF_HEALTH = 10, // equals BATTERY_PROPERTY_STATE_OF_HEALTH
};
struct BatteryProperties {
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 86f6c7f..51642f9 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -24,9 +24,6 @@
cc_test {
name: "gpuservice_unittest",
test_suites: ["device-tests"],
- sanitize: {
- address: true,
- },
srcs: [
"GpuMemTest.cpp",
"GpuMemTracerTest.cpp",
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
index e789052..a63de1c 100644
--- a/services/gpuservice/vts/OWNERS
+++ b/services/gpuservice/vts/OWNERS
@@ -1,4 +1,5 @@
# Bug component: 653544
+kocdemir@google.com
paulthomson@google.com
pbaiget@google.com
lfy@google.com
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index ab5c5ef..da4e42f 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -34,6 +34,7 @@
srcs: [
"AnrTracker.cpp",
"Connection.cpp",
+ "DebugConfig.cpp",
"DragState.cpp",
"Entry.cpp",
"FocusResolver.cpp",
diff --git a/services/inputflinger/dispatcher/DebugConfig.cpp b/services/inputflinger/dispatcher/DebugConfig.cpp
new file mode 100644
index 0000000..764194d
--- /dev/null
+++ b/services/inputflinger/dispatcher/DebugConfig.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DebugConfig.h"
+
+#include <android-base/properties.h>
+
+namespace android::inputdispatcher {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+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);
+ return DEBUG_INBOUND_EVENT_DETAILS;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO);
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index d2ad407..0e260a7 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -22,12 +22,20 @@
#include <log/log_event_list.h>
namespace android::inputdispatcher {
+
+/**
+ * Signals whether this is a debuggable Android build.
+ * This is populated by reading the value of the "ro.debuggable" property.
+ */
+extern const bool IS_DEBUGGABLE_BUILD;
+
/**
* Log detailed debug messages about each inbound event notification to the dispatcher.
- * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG" (requires restart)
+ * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
*/
-const bool DEBUG_INBOUND_EVENT_DETAILS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO);
+bool debugInboundEventDetails();
/**
* Log detailed debug messages about each outbound event processed by the dispatcher.
@@ -90,4 +98,5 @@
*/
const bool DEBUG_HOVER =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO);
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index ce7c882..b625a1b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -14,16 +14,17 @@
* limitations under the License.
*/
+#define LOG_TAG "InputDispatcher"
+
#include "Entry.h"
#include "Connection.h"
+#include "DebugConfig.h"
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <cutils/atomic.h>
#include <inttypes.h>
-using android::base::GetBoolProperty;
using android::base::StringPrintf;
namespace android::inputdispatcher {
@@ -172,7 +173,7 @@
KeyEntry::~KeyEntry() {}
std::string KeyEntry::getDescription() const {
- if (!GetBoolProperty("ro.debuggable", false)) {
+ if (!IS_DEBUGGABLE_BUILD) {
return "KeyEvent";
}
return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32
@@ -242,7 +243,7 @@
MotionEntry::~MotionEntry() {}
std::string MotionEntry::getDescription() const {
- if (!GetBoolProperty("ro.debuggable", false)) {
+ if (!IS_DEBUGGABLE_BUILD) {
return "MotionEvent";
}
std::string msg;
@@ -292,7 +293,7 @@
deviceId, inputEventSourceToString(source).c_str(),
ftl::enum_string(sensorType).c_str(), accuracy, hwTimestamp);
- if (!GetBoolProperty("ro.debuggable", false)) {
+ if (IS_DEBUGGABLE_BUILD) {
for (size_t i = 0; i < values.size(); i++) {
if (i > 0) {
msg += ", ";
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9d5bbbd..cd427f0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -110,6 +110,8 @@
constexpr int LOGTAG_INPUT_FOCUS = 62001;
constexpr int LOGTAG_INPUT_CANCEL = 62003;
+const ui::Transform kIdentityTransform;
+
inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
@@ -475,8 +477,8 @@
}
// Returns true if the given window can accept pointer events at the given display location.
-bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y,
- bool isStylus) {
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y,
+ bool isStylus, const ui::Transform& displayTransform) {
const auto inputConfig = windowInfo.inputConfig;
if (windowInfo.displayId != displayId ||
inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
@@ -486,7 +488,17 @@
if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
return false;
}
- if (!windowInfo.touchableRegionContainsPoint(x, y)) {
+
+ // Window Manager works in the logical display coordinate space. When it specifies bounds for a
+ // window as (l, t, r, b), the range of x in [l, r) and y in [t, b) are considered to be inside
+ // the window. Points on the right and bottom edges should not be inside the window, so we need
+ // to be careful about performing a hit test when the display is rotated, since the "right" and
+ // "bottom" of the window will be different in the display (un-rotated) space compared to in the
+ // logical display in which WM determined the bounds. Perform the hit test in the logical
+ // display space to ensure these edges are considered correctly in all orientations.
+ const auto touchableRegion = displayTransform.transform(windowInfo.touchableRegion);
+ const auto p = displayTransform.transform(x, y);
+ if (!touchableRegion.contains(std::floor(p.x), std::floor(p.y))) {
return false;
}
return true;
@@ -540,19 +552,16 @@
return {};
}
-Point resolveTouchedPosition(const MotionEntry& entry) {
+std::pair<float, float> resolveTouchedPosition(const MotionEntry& entry) {
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
// Always dispatch mouse events to cursor position.
if (isFromMouse) {
- return Point(static_cast<int32_t>(entry.xCursorPosition),
- static_cast<int32_t>(entry.yCursorPosition));
+ return {entry.xCursorPosition, entry.yCursorPosition};
}
const int32_t pointerIndex = getMotionEventActionPointerIndex(entry.action);
- return Point(static_cast<int32_t>(
- entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)),
- static_cast<int32_t>(
- entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)));
+ return {entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X),
+ entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)};
}
std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
@@ -1159,7 +1168,7 @@
}
std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>>
-InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus,
+InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus,
bool ignoreDragWindow) const {
// Traverse windows from front to back to find touched window.
std::vector<InputTarget> outsideTargets;
@@ -1170,7 +1179,8 @@
}
const WindowInfo& info = *windowHandle->getInfo();
- if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+ if (!info.isSpy() &&
+ windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
return {windowHandle, outsideTargets};
}
@@ -1184,14 +1194,14 @@
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- int32_t displayId, int32_t x, int32_t y, bool isStylus) const {
+ int32_t displayId, float x, float y, bool isStylus) const {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
const WindowInfo& info = *windowHandle->getInfo();
- if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+ if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
continue;
}
if (!info.isSpy()) {
@@ -1207,7 +1217,7 @@
const char* reason;
switch (dropReason) {
case DropReason::POLICY:
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("Dropped event because policy consumed it.");
}
reason = "inbound event was dropped because the policy consumed it";
@@ -1596,7 +1606,7 @@
} else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
// The key on device 'deviceId' is still down, do not stop key repeat
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
}
} else if (!entry->syntheticRepeat) {
@@ -2184,18 +2194,20 @@
const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction;
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
+ // If pointers are already down, let's finish the current gesture and ignore the new events
+ // 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
+ << " is already down in display " << displayId << ": " << entry.getDescription();
+ // TODO(b/211379801): test multiple simultaneous input streams.
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {}; // wrong device
+ }
+
if (newGesture) {
- // If pointers are already down, let's finish the current gesture and ignore the new events
- // from another device.
- if (switchedDevice && wasDown) {
- ALOGI("Dropping event because a pointer for a different device is already down "
- "in display %" PRId32,
- displayId);
- // TODO: test multiple simultaneous input streams.
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {}; // wrong device
- }
- tempTouchState.clearWindowsWithoutPointers();
+ // If a new gesture is starting, clear the touch state completely.
+ tempTouchState.reset();
tempTouchState.deviceId = entry.deviceId;
tempTouchState.source = entry.source;
isSplit = false;
@@ -2203,7 +2215,7 @@
ALOGI("Dropping move event because a pointer for a different device is already active "
"in display %" PRId32,
displayId);
- // TODO: test multiple simultaneous input streams.
+ // TODO(b/211379801): test multiple simultaneous input streams.
outInjectionResult = InputEventInjectionResult::FAILED;
return {}; // wrong device
}
@@ -2229,8 +2241,7 @@
}
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
- ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
- displayId);
+ ALOGD("No new touched window at (%.1f, %.1f) in display %" PRId32, x, y, displayId);
// Try to assign the pointer to the first foreground window we find, if there is one.
newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
@@ -2268,7 +2279,8 @@
}
if (newTouchedWindows.empty()) {
- ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
+ ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display "
+ "%d.",
x, y, displayId);
outInjectionResult = InputEventInjectionResult::FAILED;
return {};
@@ -2317,6 +2329,10 @@
const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
+ // TODO(b/211379801): Currently, even if pointerIds are empty (hover case), we would
+ // 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,
isDownOrPointerDown
? std::make_optional(entry.eventTime)
@@ -2369,10 +2385,9 @@
// If the pointer is not currently down, then ignore the event.
if (!tempTouchState.isDown()) {
- ALOGD_IF(DEBUG_FOCUS,
- "Dropping event because the pointer is not down or we previously "
- "dropped the pointer down event in display %" PRId32 ": %s",
- displayId, entry.getDescription().c_str());
+ LOG(INFO) << "Dropping event because the pointer is not down or we previously "
+ "dropped the pointer down event in display "
+ << displayId << ": " << entry.getDescription();
outInjectionResult = InputEventInjectionResult::FAILED;
return {};
}
@@ -2530,7 +2545,6 @@
}
// Success! Output targets from the touch state.
- tempTouchState.clearWindowsWithoutPointers();
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
// Windows with hovering pointers are getting persisted inside TouchState.
@@ -2570,14 +2584,13 @@
} else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
// Pointer went up.
tempTouchState.removeTouchedPointer(entry.pointerProperties[0].id);
- tempTouchState.clearWindowsWithoutPointers();
} else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
tempTouchState.reset();
} else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// First pointer went down.
- if (oldState && oldState->isDown()) {
- ALOGD("Conflicting pointer actions: Down received while already down.");
+ if (oldState && (oldState->isDown() || oldState->hasHoveringPointers())) {
+ ALOGD("Conflicting pointer actions: Down received while already down or hovering.");
*outConflictingPointerActions = true;
}
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
@@ -2600,6 +2613,7 @@
// state was only valid for this one action.
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
if (displayId >= 0) {
+ tempTouchState.clearWindowsWithoutPointers();
mTouchStatesByDisplay[displayId] = tempTouchState;
} else {
mTouchStatesByDisplay.erase(displayId);
@@ -2741,7 +2755,8 @@
if (displayInfoIt != mDisplayInfos.end()) {
inputTarget.displayTransform = displayInfoIt->second.transform;
} else {
- ALOGE("DisplayInfo not found for window on display: %d", windowInfo->displayId);
+ // DisplayInfo not found for this window on display windowInfo->displayId.
+ // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
}
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
@@ -3048,9 +3063,13 @@
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
- LOG_ALWAYS_FATAL_IF(!inputTarget.firstDownTimeInTarget.has_value(),
- "Splitting motion events requires a down time to be set for the "
- "target");
+ if (!inputTarget.firstDownTimeInTarget.has_value()) {
+ logDispatchStateLocked();
+ LOG(FATAL) << "Splitting motion events requires a down time to be set for the "
+ "target on connection "
+ << connection->getInputChannelName() << " for "
+ << originalMotionEntry.getDescription();
+ }
std::unique_ptr<MotionEntry> splitMotionEntry =
splitMotionEvent(originalMotionEntry, inputTarget.pointerIds,
inputTarget.firstDownTimeInTarget.value());
@@ -3931,8 +3950,8 @@
// in this way.
ALOGW("Dropping split motion event because the pointer count is %d but "
"we expected there to be %zu pointers. This probably means we received "
- "a broken sequence of pointer ids from the input device.",
- splitPointerCount, pointerIds.count());
+ "a broken sequence of pointer ids from the input device: %s",
+ splitPointerCount, pointerIds.count(), originalMotionEntry.getDescription().c_str());
return nullptr;
}
@@ -4007,7 +4026,7 @@
}
void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
}
@@ -4027,18 +4046,20 @@
/**
* If one of the meta shortcuts is detected, process them here:
- * Meta + Backspace -> generate BACK
- * Meta + Enter -> generate HOME
- * This will potentially overwrite keyCode and metaState.
+ * Meta + Backspace; Meta + Grave; Meta + Left arrow -> generate BACK
+ * Most System shortcuts are handled in PhoneWindowManager.java except 'Back' shortcuts. Unlike
+ * Back, other shortcuts DO NOT need to be sent to applications and are fully handled by the system.
+ * But for Back key and Back shortcuts, we need to send KEYCODE_BACK to applications which can
+ * potentially handle the back key presses.
+ * Note: We don't send any Meta based KeyEvents to applications, so we need to convert to a KeyEvent
+ * where meta modifier is off before sending. Currently only use case is 'Back'.
*/
void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
int32_t& keyCode, int32_t& metaState) {
if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) {
int32_t newKeyCode = AKEYCODE_UNKNOWN;
- if (keyCode == AKEYCODE_DEL) {
+ if (keyCode == AKEYCODE_DEL || keyCode == AKEYCODE_GRAVE || keyCode == AKEYCODE_DPAD_LEFT) {
newKeyCode = AKEYCODE_BACK;
- } else if (keyCode == AKEYCODE_ENTER) {
- newKeyCode = AKEYCODE_HOME;
}
if (newKeyCode != AKEYCODE_UNKNOWN) {
std::scoped_lock _l(mLock);
@@ -4063,14 +4084,15 @@
}
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
- ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
- "policyFlags=0x%x, action=0x%x, "
- "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
- args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
- args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
- args->downTime);
- }
+ ALOGD_IF(debugInboundEventDetails(),
+ "notifyKey - id=%" PRIx32 ", eventTime=%" PRId64
+ ", deviceId=%d, source=%s, displayId=%" PRId32
+ "policyFlags=0x%x, action=%s, flags=0x%x, keyCode=%s, scanCode=0x%x, metaState=0x%x, "
+ "downTime=%" PRId64,
+ args->id, args->eventTime, args->deviceId,
+ inputEventSourceToString(args->source).c_str(), args->displayId, args->policyFlags,
+ KeyEvent::actionToString(args->action), args->flags, KeyEvent::getLabel(args->keyCode),
+ args->scanCode, args->metaState, args->downTime);
if (!validateKeyEvent(args->action)) {
return;
}
@@ -4141,23 +4163,22 @@
}
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
- ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+ if (debugInboundEventDetails()) {
+ ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, "
"displayId=%" PRId32 ", policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
"yCursorPosition=%f, downTime=%" PRId64,
- args->id, args->eventTime, args->deviceId, args->source, args->displayId,
- args->policyFlags, MotionEvent::actionToString(args->action).c_str(),
- args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags,
- args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition,
- args->downTime);
+ args->id, args->eventTime, args->deviceId,
+ inputEventSourceToString(args->source).c_str(), args->displayId, args->policyFlags,
+ MotionEvent::actionToString(args->action).c_str(), 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++) {
- ALOGD(" Pointer %d: id=%d, toolType=%d, "
- "x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
- i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
+ 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,
+ motionToolTypeToString(args->pointerProperties[i].toolType),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
@@ -4169,9 +4190,12 @@
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
}
- LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
- args->pointerProperties),
- "Invalid event: %s", args->dump().c_str());
+
+ if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+ args->pointerProperties)) {
+ LOG(ERROR) << "Invalid event: " << args->dump();
+ return;
+ }
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4252,7 +4276,7 @@
}
void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
" sensorType=%s",
args->id, args->eventTime, args->deviceId, args->source,
@@ -4280,7 +4304,7 @@
}
void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args->eventTime,
args->deviceId, args->isOn);
}
@@ -4292,7 +4316,7 @@
}
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
"switchMask=0x%08x",
args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
@@ -4304,7 +4328,7 @@
}
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
args->deviceId);
}
@@ -4324,7 +4348,7 @@
}
void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
args->request.enable ? "true" : "false");
}
@@ -4347,7 +4371,7 @@
InputEventInjectionSync syncMode,
std::chrono::milliseconds timeout,
uint32_t policyFlags) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("injectInputEvent - eventType=%d, targetUid=%s, syncMode=%d, timeout=%lld, "
"policyFlags=0x%08x",
event->getType(), targetUid ? std::to_string(*targetUid).c_str() : "none", syncMode,
@@ -4750,6 +4774,12 @@
return getWindowHandleLocked(focusedToken, displayId);
}
+ui::Transform InputDispatcher::getTransformLocked(int32_t displayId) const {
+ auto displayInfoIt = mDisplayInfos.find(displayId);
+ return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform
+ : kIdentityTransform;
+}
+
bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window,
const MotionEntry& motionEntry) const {
const WindowInfo& info = *window->getInfo();
@@ -4787,7 +4817,7 @@
TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(window, x, y);
if (!isTouchTrustedLocked(occlusionInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
- ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+ ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y);
for (const auto& log : occlusionInfo.debugInfo) {
ALOGD("%s", log.c_str());
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index b94858b..2246d47 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -239,11 +239,11 @@
std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
std::pair<sp<android::gui::WindowInfoHandle>, std::vector<InputTarget>>
- findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus = false,
+ findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus = false,
bool ignoreDragWindow = false) const REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
- int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
+ int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
REQUIRES(mLock);
@@ -374,6 +374,7 @@
int32_t displayId) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
const sp<IBinder>& windowHandleToken) const REQUIRES(mLock);
+ ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock);
// Same function as above, but faster. Since displayId is provided, this avoids the need
// to loop through all displays.
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index ad5a7fd..94f3813 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -28,10 +28,6 @@
InputState::~InputState() {}
-bool InputState::isNeutral() const {
- return mKeyMementos.empty() && mMotionMementos.empty();
-}
-
bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const {
for (const MotionMemento& memento : mMotionMementos) {
if (memento.deviceId == deviceId && memento.source == source &&
@@ -251,10 +247,19 @@
}
void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
- pointerCount = entry.pointerCount;
+ pointerCount = 0;
for (uint32_t i = 0; i < entry.pointerCount; i++) {
- pointerProperties[i].copyFrom(entry.pointerProperties[i]);
- pointerCoords[i].copyFrom(entry.pointerCoords[i]);
+ if (MotionEvent::getActionMasked(entry.action) == AMOTION_EVENT_ACTION_POINTER_UP) {
+ // In POINTER_UP events, the pointer is leaving. Since the action is not stored,
+ // this departing pointer should not be recorded.
+ const uint8_t actionIndex = MotionEvent::getActionIndex(entry.action);
+ if (i == actionIndex) {
+ continue;
+ }
+ }
+ pointerProperties[pointerCount].copyFrom(entry.pointerProperties[i]);
+ pointerCoords[pointerCount].copyFrom(entry.pointerCoords[i]);
+ pointerCount++;
}
}
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 42d8cc6..d788e47 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -34,9 +34,6 @@
explicit InputState(const IdGenerator& idGenerator);
~InputState();
- // Returns true if there is no state to be canceled.
- bool isNeutral() const;
-
// 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;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 4258471..9c443f1 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -35,6 +35,7 @@
for (TouchedWindow& touchedWindow : windows) {
touchedWindow.removeTouchingPointer(pointerId);
}
+ clearWindowsWithoutPointers();
}
void TouchState::removeTouchedPointerFromWindow(
@@ -42,6 +43,7 @@
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.removeTouchingPointer(pointerId);
+ clearWindowsWithoutPointers();
return;
}
}
@@ -51,6 +53,7 @@
for (TouchedWindow& touchedWindow : windows) {
touchedWindow.clearHoveringPointers();
}
+ clearWindowsWithoutPointers();
}
void TouchState::clearWindowsWithoutPointers() {
@@ -135,7 +138,7 @@
w.pointerIds &= ~pointerIds;
}
});
- std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.none(); });
+ clearWindowsWithoutPointers();
}
/**
@@ -164,7 +167,7 @@
w.pilferedPointerIds ^ allPilferedPointerIds;
w.pointerIds &= ~pilferedByOtherWindows;
});
- std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.none(); });
+ clearWindowsWithoutPointers();
}
sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
@@ -216,6 +219,11 @@
[](const TouchedWindow& window) { return window.pointerIds.any(); });
}
+bool TouchState::hasHoveringPointers() const {
+ return std::any_of(windows.begin(), windows.end(),
+ [](const TouchedWindow& window) { return window.hasHoveringPointers(); });
+}
+
std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId,
int32_t pointerId) const {
std::set<sp<WindowInfoHandle>> out;
@@ -231,9 +239,7 @@
for (TouchedWindow& window : windows) {
window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId);
}
- std::erase_if(windows, [](const TouchedWindow& w) {
- return w.pointerIds.none() && !w.hasHoveringPointers();
- });
+ clearWindowsWithoutPointers();
}
std::string TouchState::dump() const {
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 6e965d8..a20080f 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -71,6 +71,7 @@
const sp<android::gui::WindowInfoHandle>& windowHandle) const;
// Whether any of the windows are currently being touched
bool isDown() const;
+ bool hasHoveringPointers() const;
std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
int32_t deviceId, int32_t pointerId) const;
diff --git a/services/inputflinger/docs/input_coordinates.md b/services/inputflinger/docs/input_coordinates.md
new file mode 100644
index 0000000..7795710
--- /dev/null
+++ b/services/inputflinger/docs/input_coordinates.md
@@ -0,0 +1,113 @@
+# Input Coordinate Processing in InputFlinger
+
+This document aims to illustrate why we need to take care when converting
+between the discrete and continuous coordinate spaces, especially when
+performing rotations.
+
+The Linux evdev protocol works over **discrete integral** values. The same is
+true for displays, which output discrete pixels. WindowManager also tracks
+window bounds in pixels in the rotated logical display.
+
+However, our `MotionEvent` APIs
+report **floating point** axis values in a **continuous space**. This disparity
+is important to note when working in InputFlinger, which has to make sure the
+discrete raw coordinates are converted to the continuous space correctly in all
+scenarios.
+
+## Disparity between continuous and discrete coordinates during rotation
+
+Let's consider an example of device that has a 3 x 4 screen.
+
+### Natural orientation: No rotation
+
+If the user interacts with the highlighted pixel, the touchscreen would report
+the discreet coordinates (0, 2).
+
+```
+ ┌─────┬─────┬─────┐
+ │ 0,0 │ 1,0 │ 2,0 │
+ ├─────┼─────┼─────┤
+ │ 0,1 │ 1,1 │ 2,1 │
+ ├─────┼─────┼─────┤
+ │█0,2█│ 1,2 │ 2,2 │
+ ├─────┼─────┼─────┤
+ │ 0,3 │ 1,3 │ 2,3 │
+ └─────┴─────┴─────┘
+```
+
+When converted to the continuous space, the point (0, 2) corresponds to the
+location shown below.
+
+```
+ 0 1 2 3
+ 0 ┌─────┬─────┬─────┐
+ │ │ │ │
+ 1 ├─────┼─────┼─────┤
+ │ │ │ │
+ 2 █─────┼─────┼─────┤
+ │ │ │ │
+ 3 ├─────┼─────┼─────┤
+ │ │ │ │
+ 4 └─────┴─────┴─────┘
+```
+
+### Rotated orientation: 90-degree counter-clockwise rotation
+
+When the device is rotated and the same place on the touchscreen is touched, the
+input device will still report the same coordinates of (0, 2).
+
+In the rotated display, that now corresponds to the pixel (2, 2).
+
+```
+ ┌─────┬─────┬─────┬─────┐
+ │ 0,0 │ 1,0 │ 2,0 │ 3,0 │
+ ├─────┼─────┼─────┼─────┤
+ │ 0,1 │ 1,1 │ 2,1 │ 3,1 │
+ ├─────┼─────┼─────┼─────┤
+ │ 0,2 │ 1,2 │█2,2█│ 3,2 │
+ └─────┴─────┴─────┴─────┘
+```
+
+*It is important to note that rotating the device 90 degrees is NOT equivalent
+to rotating the continuous coordinate space by 90 degrees.*
+
+The point (2, 2) now corresponds to a different location in the continuous space
+than before, even though the user was interacting at the same place on the
+touchscreen.
+
+```
+ 0 1 2 3 4
+ 0 ┌─────┬─────┬─────┬─────┐
+ │ │ │ │ │
+ 1 ├─────┼─────┼─────┼─────┤
+ │ │ │ │ │
+ 2 ├─────┼─────█─────┼─────┤
+ │ │ │ │ │
+ 3 └─────┴─────┴─────┴─────┘
+```
+
+If we were to simply (incorrectly) rotate the continuous space from before by
+90 degrees, the touched point would correspond to the location (2, 3), shown
+below. This new point is outside the bounds of the display, since it does not
+correspond to any pixel at that location.
+
+It should be impossible for a touchscreen to generate points outside the bounds
+of the display, because we assume that the area of the touchscreen maps directly
+to the area of the display. Therefore, that point is an invalid coordinate that
+cannot be generated by an input device.
+
+```
+ 0 1 2 3 4
+ 0 ┌─────┬─────┬─────┬─────┐
+ │ │ │ │ ╏
+ 1 ├─────┼─────┼─────┼─────┤
+ │ │ │ │ ╏
+ 2 ├─────┼─────┼─────┼─────┤
+ │ │ │ │ ╏
+ 3 └-----┴-----█-----┴-----┘
+```
+
+The same logic applies to windows as well. When performing hit tests to
+determine if a point in the continuous space falls inside a window's bounds,
+hit test must be performed in the correct orientation, since points on the right
+and bottom edges of the window do not fall within the window bounds.
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 97d57e4..ec0388d 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -242,13 +242,13 @@
input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* key) {
if (map != nullptr) {
- std::string value;
- auto prop = std::make_unique<input_property_t>();
- if (!map->propertyMap->tryGetProperty(key, value)) {
+ std::optional<std::string> value = map->propertyMap->getString(key);
+ if (!value.has_value()) {
return nullptr;
}
+ auto prop = std::make_unique<input_property_t>();
prop->key = key;
- prop->value = value.c_str();
+ prop->value = value->c_str();
return prop.release();
}
return nullptr;
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 9dbdd5a..3f1ff30 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -22,6 +22,20 @@
namespace android {
+struct FloatPoint {
+ float x;
+ float y;
+
+ inline FloatPoint(float x, float y) : x(x), y(y) {}
+
+ inline explicit FloatPoint(vec2 p) : x(p.x), y(p.y) {}
+
+ template <typename T, typename U>
+ operator std::tuple<T, U>() {
+ return {x, y};
+ }
+};
+
/**
* Interface for tracking a mouse / touch pad pointer and touch pad spots.
*
@@ -40,8 +54,7 @@
public:
/* Gets the bounds of the region that the pointer can traverse.
* Returns true if the bounds are available. */
- virtual bool getBounds(float* outMinX, float* outMinY,
- float* outMaxX, float* outMaxY) const = 0;
+ virtual std::optional<FloatRect> getBounds() const = 0;
/* Move the pointer. */
virtual void move(float deltaX, float deltaY) = 0;
@@ -56,7 +69,7 @@
virtual void setPosition(float x, float y) = 0;
/* Gets the absolute location of the pointer. */
- virtual void getPosition(float* outX, float* outY) const = 0;
+ virtual FloatPoint getPosition() const = 0;
enum class Transition {
// Fade/unfade immediately.
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index d29692c..132c3a1 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -39,6 +39,7 @@
"EventHub.cpp",
"InputDevice.cpp",
"InputReader.cpp",
+ "Macros.cpp",
"TouchVideoDevice.cpp",
"controller/PeripheralController.cpp",
"mapper/CursorInputMapper.cpp",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index d3d8021..e65f3af 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -52,6 +52,7 @@
#include <utils/Timers.h>
#include <filesystem>
+#include <optional>
#include <regex>
#include <utility>
@@ -673,9 +674,9 @@
bool EventHub::Device::isExternalDeviceLocked() {
if (configuration) {
- bool value;
- if (configuration->tryGetProperty("device.internal", value)) {
- return !value;
+ std::optional<bool> isInternal = configuration->getBool("device.internal");
+ if (isInternal.has_value()) {
+ return !isInternal.value();
}
}
return identifier.bus == BUS_USB || identifier.bus == BUS_BLUETOOTH;
@@ -683,9 +684,9 @@
bool EventHub::Device::deviceHasMicLocked() {
if (configuration) {
- bool value;
- if (configuration->tryGetProperty("audio.mic", value)) {
- return value;
+ std::optional<bool> hasMic = configuration->getBool("audio.mic");
+ if (hasMic.has_value()) {
+ return hasMic.value();
}
}
return false;
@@ -882,14 +883,13 @@
return device != nullptr ? device->controllerNumber : 0;
}
-void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+std::optional<PropertyMap> EventHub::getConfiguration(int32_t deviceId) const {
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->configuration) {
- *outConfiguration = *device->configuration;
- } else {
- outConfiguration->clear();
+ if (device == nullptr || device->configuration == nullptr) {
+ return {};
}
+ return *device->configuration;
}
status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
@@ -2282,8 +2282,8 @@
}
// See if the device is specially configured to be of a certain type.
- std::string deviceType;
- if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
+ if (device->configuration) {
+ std::string deviceType = device->configuration->getString("device.type").value_or("");
if (deviceType == "rotaryEncoder") {
device->classes |= InputDeviceClass::ROTARY_ENCODER;
} else if (deviceType == "externalStylus") {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index c598c0a..69363b6 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -210,14 +210,13 @@
// Touchscreens and touchpad devices.
static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
- // TODO(b/246587538): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) and DualSense
- // (ce6) touchpads, or at least load this setting from the IDC file.
+ // TODO(b/272518665): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) touchpads, or
+ // at least load this setting from the IDC file.
const InputDeviceIdentifier identifier = contextPtr->getDeviceIdentifier();
- const bool isSonyGamepadTouchpad = identifier.vendor == 0x054c &&
- (identifier.product == 0x05c4 || identifier.product == 0x09cc ||
- identifier.product == 0x0ce6);
+ const bool isSonyDualShock4Touchpad = identifier.vendor == 0x054c &&
+ (identifier.product == 0x05c4 || identifier.product == 0x09cc);
if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
- classes.test(InputDeviceClass::TOUCH_MT) && !isSonyGamepadTouchpad) {
+ classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
} else if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
@@ -277,12 +276,18 @@
mHasMic = mClasses.test(InputDeviceClass::MIC);
if (!isIgnored()) {
- if (!changes) { // first time only
+ // Full configuration should happen the first time configure is called
+ // and when the device type is changed. Changing a device type can
+ // affect various other parameters so should result in a
+ // reconfiguration.
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
mConfiguration.clear();
for_each_subdevice([this](InputDeviceContext& context) {
- PropertyMap configuration;
- context.getConfiguration(&configuration);
- mConfiguration.addAll(&configuration);
+ std::optional<PropertyMap> configuration =
+ getEventHub()->getConfiguration(context.getEventHubId());
+ if (configuration) {
+ mConfiguration.addAll(&(*configuration));
+ }
});
mAssociatedDeviceType =
@@ -412,22 +417,21 @@
// in the order received.
std::list<NotifyArgs> out;
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
- if (DEBUG_RAW_EVENTS) {
- ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
- rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
- rawEvent->when);
+ if (debugRawEvents()) {
+ const auto [type, code, value] =
+ InputEventLookup::getLinuxEvdevLabel(rawEvent->type, rawEvent->code,
+ rawEvent->value);
+ ALOGD("Input event: eventHubDevice=%d type=%s code=%s value=%s when=%" PRId64,
+ rawEvent->deviceId, type.c_str(), code.c_str(), value.c_str(), rawEvent->when);
}
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
- if (DEBUG_RAW_EVENTS) {
- ALOGD("Recovered from input event buffer overrun.");
- }
+ ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun.");
} else {
- if (DEBUG_RAW_EVENTS) {
- ALOGD("Dropped input event while waiting for next input sync.");
- }
+ ALOGD_IF(debugRawEvents(),
+ "Dropped input event while waiting for next input sync.");
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 57f679c..9080cc1 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -146,7 +146,7 @@
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
- if (DEBUG_RAW_EVENTS) {
+ if (debugRawEvents()) {
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
}
mNextTimeout = LLONG_MAX;
@@ -199,7 +199,7 @@
}
batchSize += 1;
}
- if (DEBUG_RAW_EVENTS) {
+ if (debugRawEvents()) {
ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
}
out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
diff --git a/services/inputflinger/reader/Macros.cpp b/services/inputflinger/reader/Macros.cpp
new file mode 100644
index 0000000..8841d0f
--- /dev/null
+++ b/services/inputflinger/reader/Macros.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "Macros.h"
+
+#include <android-base/properties.h>
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+} // namespace
+
+namespace android {
+
+bool debugRawEvents() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_RAW_EVENTS =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO);
+ return DEBUG_RAW_EVENTS;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index d2a7ced..2bce215 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -25,12 +25,14 @@
#include <unordered_map>
namespace android {
+
/**
* Log debug messages for each raw event received from the EventHub.
- * Enable this via "adb shell setprop log.tag.InputReaderRawEvents DEBUG" (requires restart)
+ * Enable this via "adb shell setprop log.tag.InputReaderRawEvents DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
*/
-const bool DEBUG_RAW_EVENTS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO);
+bool debugRawEvents();
/**
* Log debug messages about virtual key processing.
@@ -52,6 +54,7 @@
*/
const bool DEBUG_POINTER_ASSIGNMENT =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "PointerAssignment", ANDROID_LOG_INFO);
+
/**
* Log debug messages about gesture detection.
* Enable this via "adb shell setprop log.tag.InputReaderGestures DEBUG" (requires restart)
@@ -79,6 +82,7 @@
*/
const bool DEBUG_LIGHT_DETAILS =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LightDetails", ANDROID_LOG_INFO);
+
} // namespace android
#define INDENT " "
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 86acadb..0b15efe 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -263,7 +263,13 @@
virtual int32_t getDeviceControllerNumber(int32_t deviceId) const = 0;
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
+ /**
+ * Get the PropertyMap for the provided EventHub device, if available.
+ * This acquires the device lock, so a copy is returned rather than the raw pointer
+ * to the device's PropertyMap. A std::nullopt may be returned if the device could
+ * not be found, or if it doesn't have any configuration.
+ */
+ virtual std::optional<PropertyMap> getConfiguration(int32_t deviceId) const = 0;
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const = 0;
@@ -464,7 +470,7 @@
int32_t getDeviceControllerNumber(int32_t deviceId) const override final;
- void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override final;
+ std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override final;
status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 7867029..4ae06fe 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -269,9 +269,6 @@
inline int32_t getDeviceControllerNumber() const {
return mEventHub->getDeviceControllerNumber(mId);
}
- inline void getConfiguration(PropertyMap* outConfiguration) const {
- return mEventHub->getConfiguration(mId, outConfiguration);
- }
inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
return mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo);
}
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 13e4d0c..b12a009 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -20,6 +20,8 @@
#include "CursorInputMapper.h"
+#include <optional>
+
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
#include "PointerControllerInterface.h"
@@ -83,10 +85,11 @@
InputMapper::populateDeviceInfo(info);
if (mParameters.mode == Parameters::Mode::POINTER) {
- float minX, minY, maxX, maxY;
- if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
+ if (const auto bounds = mPointerController->getBounds(); bounds) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, bounds->left, bounds->right, 0.0f,
+ 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, bounds->top, bounds->bottom, 0.0f,
+ 0.0f, 0.0f);
}
} else {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
@@ -250,18 +253,17 @@
void CursorInputMapper::configureParameters() {
mParameters.mode = Parameters::Mode::POINTER;
- std::string cursorModeString;
- if (getDeviceContext().getConfiguration().tryGetProperty("cursor.mode", cursorModeString)) {
- if (cursorModeString == "navigation") {
+ const PropertyMap& config = getDeviceContext().getConfiguration();
+ std::optional<std::string> cursorModeString = config.getString("cursor.mode");
+ if (cursorModeString.has_value()) {
+ if (*cursorModeString == "navigation") {
mParameters.mode = Parameters::Mode::NAVIGATION;
- } else if (cursorModeString != "pointer" && cursorModeString != "default") {
- ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.c_str());
+ } else if (*cursorModeString != "pointer" && *cursorModeString != "default") {
+ ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString->c_str());
}
}
- mParameters.orientationAware = false;
- getDeviceContext().getConfiguration().tryGetProperty("cursor.orientationAware",
- mParameters.orientationAware);
+ mParameters.orientationAware = config.getBool("cursor.orientationAware").value_or(false);
mParameters.hasAssociatedDisplay = false;
if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
@@ -379,7 +381,7 @@
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 0361bc6..f39e004 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -61,36 +61,6 @@
scanCode >= BTN_WHEEL;
}
-static bool isMediaKey(int32_t keyCode) {
- switch (keyCode) {
- case AKEYCODE_MEDIA_PLAY:
- case AKEYCODE_MEDIA_PAUSE:
- case AKEYCODE_MEDIA_PLAY_PAUSE:
- case AKEYCODE_MUTE:
- case AKEYCODE_HEADSETHOOK:
- case AKEYCODE_MEDIA_STOP:
- case AKEYCODE_MEDIA_NEXT:
- case AKEYCODE_MEDIA_PREVIOUS:
- case AKEYCODE_MEDIA_REWIND:
- case AKEYCODE_MEDIA_RECORD:
- case AKEYCODE_MEDIA_FAST_FORWARD:
- case AKEYCODE_MEDIA_SKIP_FORWARD:
- case AKEYCODE_MEDIA_SKIP_BACKWARD:
- case AKEYCODE_MEDIA_STEP_FORWARD:
- case AKEYCODE_MEDIA_STEP_BACKWARD:
- case AKEYCODE_MEDIA_AUDIO_TRACK:
- case AKEYCODE_VOLUME_UP:
- case AKEYCODE_VOLUME_DOWN:
- case AKEYCODE_VOLUME_MUTE:
- case AKEYCODE_TV_AUDIO_DESCRIPTION:
- case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
- case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
- return true;
- default:
- return false;
- }
-}
-
// --- KeyboardInputMapper ---
KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source,
@@ -184,15 +154,10 @@
}
void KeyboardInputMapper::configureParameters() {
- mParameters.orientationAware = false;
const PropertyMap& config = getDeviceContext().getConfiguration();
- config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);
-
- mParameters.handlesKeyRepeat = false;
- config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);
-
- mParameters.doNotWakeByDefault = false;
- config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
+ mParameters.orientationAware = config.getBool("keyboard.orientationAware").value_or(false);
+ mParameters.handlesKeyRepeat = config.getBool("keyboard.handlesKeyRepeat").value_or(false);
+ mParameters.doNotWakeByDefault = config.getBool("keyboard.doNotWakeByDefault").value_or(false);
}
void KeyboardInputMapper::dumpParameters(std::string& dump) const {
@@ -295,14 +260,12 @@
keyMetaState = mMetaState;
}
- // Key down on external an keyboard should wake the device.
+ // Any key down on an external keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
// For internal keyboards and devices for which the default wake behavior is explicitly
// prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
// wake key individually.
- // TODO: Use the input device configuration to control this behavior more finely.
- if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault &&
- !isMediaKey(keyCode)) {
+ if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault) {
policyFlags |= POLICY_FLAG_WAKE;
}
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 19a79d7..2ff26ed 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -20,6 +20,8 @@
#include "RotaryEncoderInputMapper.h"
+#include <optional>
+
#include "CursorScrollAccumulator.h"
namespace android {
@@ -39,18 +41,19 @@
InputMapper::populateDeviceInfo(info);
if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
- float res = 0.0f;
- if (!getDeviceContext().getConfiguration().tryGetProperty("device.res", res)) {
+ const PropertyMap& config = getDeviceContext().getConfiguration();
+ std::optional<float> res = config.getFloat("device.res");
+ if (!res.has_value()) {
ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
}
- if (!getDeviceContext().getConfiguration().tryGetProperty("device.scalingFactor",
- mScalingFactor)) {
+ std::optional<float> scalingFactor = config.getFloat("device.scalingFactor");
+ if (!scalingFactor.has_value()) {
ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
"default to 1.0!\n");
- mScalingFactor = 1.0f;
}
+ mScalingFactor = scalingFactor.value_or(1.0f);
info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- res * mScalingFactor);
+ res.value_or(0.0f) * mScalingFactor);
}
}
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index f797bc9..ba08823 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -61,12 +61,6 @@
return AINPUT_SOURCE_SENSOR;
}
-template <typename T>
-bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) {
- const auto& config = getDeviceContext().getConfiguration();
- return config.tryGetProperty(keyName, outValue);
-}
-
void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
int32_t sensorDataIndex, const Axis& axis) {
auto it = mSensors.find(sensorType);
@@ -201,6 +195,17 @@
SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType,
const Axis& axis) {
InputDeviceIdentifier identifier = getDeviceContext().getDeviceIdentifier();
+ const auto& config = getDeviceContext().getConfiguration();
+
+ std::string prefix = "sensor." + ftl::enum_string(sensorType);
+ transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
+
+ int32_t flags = 0;
+ std::optional<int32_t> reportingMode = config.getInt(prefix + ".reportingMode");
+ if (reportingMode.has_value()) {
+ flags |= (*reportingMode & REPORTING_MODE_MASK) << REPORTING_MODE_SHIFT;
+ }
+
// Sensor Id will be assigned to device Id to distinguish same sensor from multiple input
// devices, in such a way that the sensor Id will be same as input device Id.
// The sensorType is to distinguish different sensors within one device.
@@ -209,28 +214,15 @@
identifier.version, sensorType,
InputDeviceSensorAccuracy::ACCURACY_HIGH,
/*maxRange=*/axis.max, /*resolution=*/axis.scale,
- /*power=*/0.0f, /*minDelay=*/0,
- /*fifoReservedEventCount=*/0, /*fifoMaxEventCount=*/0,
- ftl::enum_string(sensorType), /*maxDelay=*/0, /*flags=*/0,
- getDeviceId());
-
- std::string prefix = "sensor." + ftl::enum_string(sensorType);
- transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
-
- int32_t reportingMode = 0;
- if (!tryGetProperty(prefix + ".reportingMode", reportingMode)) {
- sensorInfo.flags |= (reportingMode & REPORTING_MODE_MASK) << REPORTING_MODE_SHIFT;
- }
-
- tryGetProperty(prefix + ".maxDelay", sensorInfo.maxDelay);
-
- tryGetProperty(prefix + ".minDelay", sensorInfo.minDelay);
-
- tryGetProperty(prefix + ".power", sensorInfo.power);
-
- tryGetProperty(prefix + ".fifoReservedEventCount", sensorInfo.fifoReservedEventCount);
-
- tryGetProperty(prefix + ".fifoMaxEventCount", sensorInfo.fifoMaxEventCount);
+ /*power=*/config.getFloat(prefix + ".power").value_or(0.0f),
+ /*minDelay=*/config.getInt(prefix + ".minDelay").value_or(0),
+ /*fifoReservedEventCount=*/
+ config.getInt(prefix + ".fifoReservedEventCount").value_or(0),
+ /*fifoMaxEventCount=*/
+ config.getInt(prefix + ".fifoMaxEventCount").value_or(0),
+ ftl::enum_string(sensorType),
+ /*maxDelay=*/config.getInt(prefix + ".maxDelay").value_or(0),
+ /*flags=*/flags, getDeviceId());
return Sensor(sensorInfo);
}
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 457567b..043a895 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -16,6 +16,9 @@
#pragma once
+#include <optional>
+#include <string>
+
#include "InputMapper.h"
namespace android {
@@ -120,9 +123,6 @@
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, bool force);
- template <typename T>
- bool tryGetProperty(std::string keyName, T& outValue);
-
void parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
int32_t sensorDataIndex, const Axis& axis);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 31fdac9..a943a03 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -293,7 +293,10 @@
mConfig = *config;
- if (!changes) { // first time only
+ // Full configuration should happen the first time configure is called and
+ // when the device type is changed. Changing a device type can affect
+ // various other parameters so should result in a reconfiguration.
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
// Configure basic parameters.
configureParameters();
@@ -328,7 +331,8 @@
InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
- InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
+ InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE |
+ InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
// Configure device sources, display dimensions, orientation and
// scaling factors.
configureInputDevice(when, &resetNeeded);
@@ -362,15 +366,15 @@
? Parameters::GestureMode::SINGLE_TOUCH
: Parameters::GestureMode::MULTI_TOUCH;
- std::string gestureModeString;
- if (getDeviceContext().getConfiguration().tryGetProperty("touch.gestureMode",
- gestureModeString)) {
- if (gestureModeString == "single-touch") {
+ const PropertyMap& config = getDeviceContext().getConfiguration();
+ std::optional<std::string> gestureModeString = config.getString("touch.gestureMode");
+ if (gestureModeString.has_value()) {
+ if (*gestureModeString == "single-touch") {
mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
- } else if (gestureModeString == "multi-touch") {
+ } else if (*gestureModeString == "multi-touch") {
mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
- } else if (gestureModeString != "default") {
- ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.c_str());
+ } else if (*gestureModeString != "default") {
+ ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString->c_str());
}
}
@@ -378,24 +382,23 @@
mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
- mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
- getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
- mParameters.orientationAware);
+ mParameters.orientationAware =
+ config.getBool("touch.orientationAware")
+ .value_or(mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN);
mParameters.orientation = ui::ROTATION_0;
- std::string orientationString;
- if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
- orientationString)) {
+ std::optional<std::string> orientationString = config.getString("touch.orientation");
+ if (orientationString.has_value()) {
if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
- } else if (orientationString == "ORIENTATION_90") {
+ } else if (*orientationString == "ORIENTATION_90") {
mParameters.orientation = ui::ROTATION_90;
- } else if (orientationString == "ORIENTATION_180") {
+ } else if (*orientationString == "ORIENTATION_180") {
mParameters.orientation = ui::ROTATION_180;
- } else if (orientationString == "ORIENTATION_270") {
+ } else if (*orientationString == "ORIENTATION_270") {
mParameters.orientation = ui::ROTATION_270;
- } else if (orientationString != "ORIENTATION_0") {
- ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
+ } else if (*orientationString != "ORIENTATION_0") {
+ ALOGW("Invalid value for touch.orientation: '%s'", orientationString->c_str());
}
}
@@ -409,10 +412,7 @@
mParameters.hasAssociatedDisplay = true;
if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
- std::string uniqueDisplayId;
- getDeviceContext().getConfiguration().tryGetProperty("touch.displayId",
- uniqueDisplayId);
- mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
+ mParameters.uniqueDisplayId = config.getString("touch.displayId").value_or("").c_str();
}
}
if (getDeviceContext().getAssociatedDisplayPort()) {
@@ -422,20 +422,19 @@
// Initial downs on external touch devices should wake the device.
// Normally we don't do this for internal touch screens to prevent them from waking
// up in your pocket but you can enable it using the input device configuration.
- mParameters.wake = getDeviceContext().isExternal();
- getDeviceContext().getConfiguration().tryGetProperty("touch.wake", mParameters.wake);
+ mParameters.wake = config.getBool("touch.wake").value_or(getDeviceContext().isExternal());
- InputDeviceUsiVersion usiVersion;
- if (getDeviceContext().getConfiguration().tryGetProperty("touch.usiVersionMajor",
- usiVersion.majorVersion) &&
- getDeviceContext().getConfiguration().tryGetProperty("touch.usiVersionMinor",
- usiVersion.minorVersion)) {
- mParameters.usiVersion = usiVersion;
+ std::optional<int32_t> usiVersionMajor = config.getInt("touch.usiVersionMajor");
+ std::optional<int32_t> usiVersionMinor = config.getInt("touch.usiVersionMinor");
+ if (usiVersionMajor.has_value() && usiVersionMinor.has_value()) {
+ mParameters.usiVersion = {
+ .majorVersion = *usiVersionMajor,
+ .minorVersion = *usiVersionMinor,
+ };
}
- mParameters.enableForInactiveViewport = false;
- getDeviceContext().getConfiguration().tryGetProperty("touch.enableForInactiveViewport",
- mParameters.enableForInactiveViewport);
+ mParameters.enableForInactiveViewport =
+ config.getBool("touch.enableForInactiveViewport").value_or(false);
}
void TouchInputMapper::configureDeviceType() {
@@ -453,7 +452,8 @@
// Type association takes precedence over the device type found in the idc file.
std::string deviceTypeString = getDeviceContext().getDeviceTypeAssociation().value_or("");
if (deviceTypeString.empty()) {
- getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType", deviceTypeString);
+ deviceTypeString =
+ getDeviceContext().getConfiguration().getString("touch.deviceType").value_or("");
}
if (deviceTypeString == "touchScreen") {
mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
@@ -801,75 +801,96 @@
};
}
- // Compute oriented precision, scales and ranges.
- // Note that the maximum value reported is an inclusive maximum value so it is one
- // unit less than the total width or height of the display.
- // TODO(b/20508709): Calculate the oriented ranges using the input device's raw frame.
- switch (mInputDeviceOrientation) {
- case ui::ROTATION_90:
- case ui::ROTATION_270:
- mOrientedRanges.x.min = 0;
- mOrientedRanges.x.max = mDisplayBounds.height - 1;
- mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = 0;
- mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mRawToDisplay.getScaleY();
+ // Oriented X/Y range (in the rotated display's orientation)
+ const FloatRect rawFrame = Rect{mRawPointerAxes.x.minValue, mRawPointerAxes.y.minValue,
+ mRawPointerAxes.x.maxValue, mRawPointerAxes.y.maxValue}
+ .toFloatRect();
+ const auto orientedRangeRect = mRawToRotatedDisplay.transform(rawFrame);
+ mOrientedRanges.x.min = orientedRangeRect.left;
+ mOrientedRanges.y.min = orientedRangeRect.top;
+ mOrientedRanges.x.max = orientedRangeRect.right;
+ mOrientedRanges.y.max = orientedRangeRect.bottom;
- mOrientedRanges.y.min = 0;
- mOrientedRanges.y.max = mDisplayBounds.width - 1;
- mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = 0;
- mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mRawToDisplay.getScaleX();
- break;
+ // Oriented flat (in the rotated display's orientation)
+ const auto orientedFlat =
+ transformWithoutTranslation(mRawToRotatedDisplay,
+ {static_cast<float>(mRawPointerAxes.x.flat),
+ static_cast<float>(mRawPointerAxes.y.flat)});
+ mOrientedRanges.x.flat = std::abs(orientedFlat.x);
+ mOrientedRanges.y.flat = std::abs(orientedFlat.y);
- default:
- mOrientedRanges.x.min = 0;
- mOrientedRanges.x.max = mDisplayBounds.width - 1;
- mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = 0;
- mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mRawToDisplay.getScaleX();
+ // Oriented fuzz (in the rotated display's orientation)
+ const auto orientedFuzz =
+ transformWithoutTranslation(mRawToRotatedDisplay,
+ {static_cast<float>(mRawPointerAxes.x.fuzz),
+ static_cast<float>(mRawPointerAxes.y.fuzz)});
+ mOrientedRanges.x.fuzz = std::abs(orientedFuzz.x);
+ mOrientedRanges.y.fuzz = std::abs(orientedFuzz.y);
- mOrientedRanges.y.min = 0;
- mOrientedRanges.y.max = mDisplayBounds.height - 1;
- mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = 0;
- mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mRawToDisplay.getScaleY();
- break;
- }
+ // Oriented resolution (in the rotated display's orientation)
+ const auto orientedRes =
+ transformWithoutTranslation(mRawToRotatedDisplay,
+ {static_cast<float>(mRawPointerAxes.x.resolution),
+ static_cast<float>(mRawPointerAxes.y.resolution)});
+ mOrientedRanges.x.resolution = std::abs(orientedRes.x);
+ mOrientedRanges.y.resolution = std::abs(orientedRes.y);
}
void TouchInputMapper::computeInputTransforms() {
- const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
+ constexpr auto isRotated = [](const ui::Transform::RotationFlags& rotation) {
+ return rotation == ui::Transform::ROT_90 || rotation == ui::Transform::ROT_270;
+ };
- ui::Size rotatedRawSize = rawSize;
- if (mInputDeviceOrientation == ui::ROTATION_270 || mInputDeviceOrientation == ui::ROTATION_90) {
- std::swap(rotatedRawSize.width, rotatedRawSize.height);
- }
- const auto rotationFlags = ui::Transform::toRotationFlags(-mInputDeviceOrientation);
- mRawRotation = ui::Transform{rotationFlags};
+ // See notes about input coordinates in the inputflinger docs:
+ // //frameworks/native/services/inputflinger/docs/input_coordinates.md
// Step 1: Undo the raw offset so that the raw coordinate space now starts at (0, 0).
- ui::Transform undoRawOffset;
- undoRawOffset.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
+ ui::Transform undoOffsetInRaw;
+ undoOffsetInRaw.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
- // Step 2: Rotate the raw coordinates to the expected orientation.
- ui::Transform rotate;
- // When rotating raw coordinates, the raw size will be used as an offset.
- // Account for the extra unit added to the raw range when the raw size was calculated.
- rotate.set(rotationFlags, rotatedRawSize.width - 1, rotatedRawSize.height - 1);
+ // Step 2: Rotate the raw coordinates to account for input device orientation. The coordinates
+ // will now be in the same orientation as the display in ROTATION_0.
+ // Note: Negating an ui::Rotation value will give its inverse rotation.
+ const auto inputDeviceOrientation = ui::Transform::toRotationFlags(-mParameters.orientation);
+ const ui::Size orientedRawSize = isRotated(inputDeviceOrientation)
+ ? ui::Size{mRawPointerAxes.getRawHeight(), mRawPointerAxes.getRawWidth()}
+ : ui::Size{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
+ // When rotating raw values, account for the extra unit added when calculating the raw range.
+ const auto orientInRaw = ui::Transform(inputDeviceOrientation, orientedRawSize.width - 1,
+ orientedRawSize.height - 1);
- // Step 3: Scale the raw coordinates to the display space.
- ui::Transform scaleToDisplay;
- const float xScale = static_cast<float>(mDisplayBounds.width) / rotatedRawSize.width;
- const float yScale = static_cast<float>(mDisplayBounds.height) / rotatedRawSize.height;
- scaleToDisplay.set(xScale, 0, 0, yScale);
+ // Step 3: Rotate the raw coordinates to account for the display rotation. The coordinates will
+ // now be in the same orientation as the rotated display. There is no need to rotate the
+ // coordinates to the display rotation if the device is not orientation-aware.
+ const auto viewportRotation = ui::Transform::toRotationFlags(-mViewport.orientation);
+ const auto rotatedRawSize = mParameters.orientationAware && isRotated(viewportRotation)
+ ? ui::Size{orientedRawSize.height, orientedRawSize.width}
+ : orientedRawSize;
+ // When rotating raw values, account for the extra unit added when calculating the raw range.
+ const auto rotateInRaw = mParameters.orientationAware
+ ? ui::Transform(viewportRotation, rotatedRawSize.width - 1, rotatedRawSize.height - 1)
+ : ui::Transform();
- mRawToDisplay = (scaleToDisplay * (rotate * undoRawOffset));
+ // Step 4: Scale the raw coordinates to the display space.
+ // - Here, we assume that the raw surface of the touch device maps perfectly to the surface
+ // of the display panel. This is usually true for touchscreens.
+ // - From this point onward, we are no longer in the discrete space of the raw coordinates but
+ // are in the continuous space of the logical display.
+ ui::Transform scaleRawToDisplay;
+ const float xScale = static_cast<float>(mViewport.deviceWidth) / rotatedRawSize.width;
+ const float yScale = static_cast<float>(mViewport.deviceHeight) / rotatedRawSize.height;
+ scaleRawToDisplay.set(xScale, 0, 0, yScale);
- // Calculate the transform that takes raw coordinates to the rotated display space.
- ui::Transform displayToRotatedDisplay;
- displayToRotatedDisplay.set(ui::Transform::toRotationFlags(-mViewport.orientation),
- mViewport.deviceWidth, mViewport.deviceHeight);
- mRawToRotatedDisplay = displayToRotatedDisplay * mRawToDisplay;
+ // Step 5: Undo the display rotation to bring us back to the un-rotated display coordinate space
+ // that InputReader uses.
+ const auto undoRotateInDisplay =
+ ui::Transform(viewportRotation, mViewport.deviceWidth, mViewport.deviceHeight)
+ .inverse();
+
+ // Now put it all together!
+ mRawToRotatedDisplay = (scaleRawToDisplay * (rotateInRaw * (orientInRaw * undoOffsetInRaw)));
+ mRawToDisplay = (undoRotateInDisplay * mRawToRotatedDisplay);
+ mRawRotation = ui::Transform{mRawToDisplay.getOrientation()};
}
void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
@@ -945,6 +966,9 @@
mPhysicalFrameInRotatedDisplay = {mViewport.physicalLeft, mViewport.physicalTop,
mViewport.physicalRight, mViewport.physicalBottom};
+ // TODO(b/257118693): Remove the dependence on the old orientation/rotation logic that
+ // uses mInputDeviceOrientation. The new logic uses the transforms calculated in
+ // computeInputTransforms().
// InputReader works in the un-rotated display coordinate space, so we don't need to do
// anything if the device is already orientation-aware. If the device is not
// orientation-aware, then we need to apply the inverse rotation of the display so that
@@ -1132,92 +1156,79 @@
// Size
out.sizeCalibration = Calibration::SizeCalibration::DEFAULT;
- std::string sizeCalibrationString;
- if (in.tryGetProperty("touch.size.calibration", sizeCalibrationString)) {
- if (sizeCalibrationString == "none") {
+ std::optional<std::string> sizeCalibrationString = in.getString("touch.size.calibration");
+ if (sizeCalibrationString.has_value()) {
+ if (*sizeCalibrationString == "none") {
out.sizeCalibration = Calibration::SizeCalibration::NONE;
- } else if (sizeCalibrationString == "geometric") {
+ } else if (*sizeCalibrationString == "geometric") {
out.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
- } else if (sizeCalibrationString == "diameter") {
+ } else if (*sizeCalibrationString == "diameter") {
out.sizeCalibration = Calibration::SizeCalibration::DIAMETER;
- } else if (sizeCalibrationString == "box") {
+ } else if (*sizeCalibrationString == "box") {
out.sizeCalibration = Calibration::SizeCalibration::BOX;
- } else if (sizeCalibrationString == "area") {
+ } else if (*sizeCalibrationString == "area") {
out.sizeCalibration = Calibration::SizeCalibration::AREA;
- } else if (sizeCalibrationString != "default") {
- ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.c_str());
+ } else if (*sizeCalibrationString != "default") {
+ ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString->c_str());
}
}
- float sizeScale;
-
- if (in.tryGetProperty("touch.size.scale", sizeScale)) {
- out.sizeScale = sizeScale;
- }
- float sizeBias;
- if (in.tryGetProperty("touch.size.bias", sizeBias)) {
- out.sizeBias = sizeBias;
- }
- bool sizeIsSummed;
- if (in.tryGetProperty("touch.size.isSummed", sizeIsSummed)) {
- out.sizeIsSummed = sizeIsSummed;
- }
+ out.sizeScale = in.getFloat("touch.size.scale");
+ out.sizeBias = in.getFloat("touch.size.bias");
+ out.sizeIsSummed = in.getBool("touch.size.isSummed");
// Pressure
out.pressureCalibration = Calibration::PressureCalibration::DEFAULT;
- std::string pressureCalibrationString;
- if (in.tryGetProperty("touch.pressure.calibration", pressureCalibrationString)) {
- if (pressureCalibrationString == "none") {
+ std::optional<std::string> pressureCalibrationString =
+ in.getString("touch.pressure.calibration");
+ if (pressureCalibrationString.has_value()) {
+ if (*pressureCalibrationString == "none") {
out.pressureCalibration = Calibration::PressureCalibration::NONE;
- } else if (pressureCalibrationString == "physical") {
+ } else if (*pressureCalibrationString == "physical") {
out.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
- } else if (pressureCalibrationString == "amplitude") {
+ } else if (*pressureCalibrationString == "amplitude") {
out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE;
- } else if (pressureCalibrationString != "default") {
+ } else if (*pressureCalibrationString != "default") {
ALOGW("Invalid value for touch.pressure.calibration: '%s'",
- pressureCalibrationString.c_str());
+ pressureCalibrationString->c_str());
}
}
- float pressureScale;
- if (in.tryGetProperty("touch.pressure.scale", pressureScale)) {
- out.pressureScale = pressureScale;
- }
+ out.pressureScale = in.getFloat("touch.pressure.scale");
// Orientation
out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT;
- std::string orientationCalibrationString;
- if (in.tryGetProperty("touch.orientation.calibration", orientationCalibrationString)) {
- if (orientationCalibrationString == "none") {
+ std::optional<std::string> orientationCalibrationString =
+ in.getString("touch.orientation.calibration");
+ if (orientationCalibrationString.has_value()) {
+ if (*orientationCalibrationString == "none") {
out.orientationCalibration = Calibration::OrientationCalibration::NONE;
- } else if (orientationCalibrationString == "interpolated") {
+ } else if (*orientationCalibrationString == "interpolated") {
out.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
- } else if (orientationCalibrationString == "vector") {
+ } else if (*orientationCalibrationString == "vector") {
out.orientationCalibration = Calibration::OrientationCalibration::VECTOR;
- } else if (orientationCalibrationString != "default") {
+ } else if (*orientationCalibrationString != "default") {
ALOGW("Invalid value for touch.orientation.calibration: '%s'",
- orientationCalibrationString.c_str());
+ orientationCalibrationString->c_str());
}
}
// Distance
out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT;
- std::string distanceCalibrationString;
- if (in.tryGetProperty("touch.distance.calibration", distanceCalibrationString)) {
- if (distanceCalibrationString == "none") {
+ std::optional<std::string> distanceCalibrationString =
+ in.getString("touch.distance.calibration");
+ if (distanceCalibrationString.has_value()) {
+ if (*distanceCalibrationString == "none") {
out.distanceCalibration = Calibration::DistanceCalibration::NONE;
- } else if (distanceCalibrationString == "scaled") {
+ } else if (*distanceCalibrationString == "scaled") {
out.distanceCalibration = Calibration::DistanceCalibration::SCALED;
- } else if (distanceCalibrationString != "default") {
+ } else if (*distanceCalibrationString != "default") {
ALOGW("Invalid value for touch.distance.calibration: '%s'",
- distanceCalibrationString.c_str());
+ distanceCalibrationString->c_str());
}
}
- float distanceScale;
- if (in.tryGetProperty("touch.distance.scale", distanceScale)) {
- out.distanceScale = distanceScale;
- }
+ out.distanceScale = in.getFloat("touch.distance.scale");
}
void TouchInputMapper::resolveCalibration() {
@@ -1446,7 +1457,7 @@
assignPointerIds(last, next);
}
- ALOGD_IF(DEBUG_RAW_EVENTS,
+ ALOGD_IF(debugRawEvents(),
"syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
"hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
@@ -1650,7 +1661,8 @@
mPointerController->setButtonState(mCurrentRawState.buttonState);
mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(),
mCurrentCookedState.cookedPointerData.idToIndex.cbegin(),
- mCurrentCookedState.cookedPointerData.touchingIdBits,
+ mCurrentCookedState.cookedPointerData.touchingIdBits |
+ mCurrentCookedState.cookedPointerData.hoveringIdBits,
mViewport.displayId);
}
@@ -2662,8 +2674,7 @@
// the pointer is hovering again even if the user is not currently touching
// the touch pad. This ensures that a view will receive a fresh hover enter
// event after a tap.
- float x, y;
- mPointerController->getPosition(&x, &y);
+ const auto [x, y] = mPointerController->getPosition();
PointerProperties pointerProperties;
pointerProperties.clear();
@@ -2869,8 +2880,7 @@
mPointerVelocityControl.reset();
}
- float x, y;
- mPointerController->getPosition(&x, &y);
+ const auto [x, y] = mPointerController->getPosition();
mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
mPointerGesture.currentGestureIdBits.clear();
@@ -2896,8 +2906,7 @@
mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
lastFingerCount == 1) {
if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
- float x, y;
- mPointerController->getPosition(&x, &y);
+ const auto [x, y] = mPointerController->getPosition();
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP");
@@ -2959,8 +2968,7 @@
mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- float x, y;
- mPointerController->getPosition(&x, &y);
+ const auto [x, y] = mPointerController->getPosition();
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
@@ -2996,8 +3004,7 @@
down = false;
}
- float x, y;
- mPointerController->getPosition(&x, &y);
+ const auto [x, y] = mPointerController->getPosition();
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3164,8 +3171,8 @@
mCurrentRawState.rawPointerData
.getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
&mPointerGesture.referenceTouchY);
- mPointerController->getPosition(&mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
+ std::tie(mPointerGesture.referenceGestureX, mPointerGesture.referenceGestureY) =
+ mPointerController->getPosition();
}
// Clear the reference deltas for fingers not yet included in the reference calculation.
@@ -3480,8 +3487,7 @@
hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
down = !hovering;
- float x, y;
- mPointerController->getPosition(&x, &y);
+ const auto [x, y] = mPointerController->getPosition();
mPointerSimple.currentCoords.copyFrom(
mCurrentCookedState.cookedPointerData.pointerCoords[index]);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3519,9 +3525,8 @@
down = isPointerDown(mCurrentRawState.buttonState);
hovering = !down;
- float x, y;
- mPointerController->getPosition(&x, &y);
- uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
+ const auto [x, y] = mPointerController->getPosition();
+ const uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
mPointerSimple.currentCoords.copyFrom(
mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3568,8 +3573,7 @@
}
int32_t displayId = mPointerController->getDisplayId();
- float xCursorPosition, yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
if (mPointerSimple.down && !down) {
mPointerSimple.down = false;
@@ -3790,7 +3794,7 @@
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DeviceMode::POINTER) {
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
}
const int32_t deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 7b464ef..df66846 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -16,6 +16,9 @@
#pragma once
+#include <optional>
+#include <string>
+
#include <stdint.h>
#include <ui/Rotation.h>
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 9f32311..3309767 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -16,9 +16,11 @@
#include "../Macros.h"
+#include <limits>
#include <optional>
#include <android/input.h>
+#include <ftl/enum.h>
#include <input/PrintTools.h>
#include <linux/input-event-codes.h>
#include <log/log_main.h>
@@ -30,6 +32,85 @@
namespace {
+/**
+ * Log details of each gesture output by the gestures library.
+ * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
+ * restarting the shell)
+ */
+const bool DEBUG_TOUCHPAD_GESTURES =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+ ANDROID_LOG_INFO);
+
+// Describes a segment of the acceleration curve.
+struct CurveSegment {
+ // The maximum pointer speed which this segment should apply. The last segment in a curve should
+ // always set this to infinity.
+ double maxPointerSpeedMmPerS;
+ double slope;
+ double intercept;
+};
+
+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},
+};
+
+const std::vector<double> sensitivityFactors = {1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20};
+
+std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity,
+ size_t propertySize) {
+ LOG_ALWAYS_FATAL_IF(propertySize < 4 * segments.size());
+ std::vector<double> output(propertySize, 0);
+
+ // The Gestures library uses functions of the following form to define curve segments, where a,
+ // b, and c can be specified by us:
+ // output_speed(input_speed_mm) = a * input_speed_mm ^ 2 + b * input_speed_mm + c
+ //
+ // (a, b, and c are also called sqr_, mul_, and int_ in the Gestures library code.)
+ //
+ // We are trying to implement the following function, where slope and intercept are the
+ // parameters specified in the `segments` array above:
+ // gain(input_speed_mm) =
+ // 0.64 * (sensitivityFactor / 10) * (slope + intercept / input_speed_mm)
+ // Where "gain" is a multiplier applied to the input speed to produce the output speed:
+ // output_speed(input_speed_mm) = input_speed_mm * gain(input_speed_mm)
+ //
+ // To put our function in the library's form, we substitute it into the function above:
+ // output_speed(input_speed_mm) =
+ // input_speed_mm * (0.64 * (sensitivityFactor / 10) *
+ // (slope + 25.4 * intercept / input_speed_mm))
+ // then expand the brackets so that input_speed_mm cancels out for the intercept term:
+ // gain(input_speed_mm) =
+ // 0.64 * (sensitivityFactor / 10) * slope * input_speed_mm +
+ // 0.64 * (sensitivityFactor / 10) * intercept
+ //
+ // This gives us the following parameters for the Gestures library function form:
+ // a = 0
+ // b = 0.64 * (sensitivityFactor / 10) * slope
+ // c = 0.64 * (sensitivityFactor / 10) * intercept
+
+ double commonFactor = 0.64 * sensitivityFactors[sensitivity + 7] / 10;
+
+ size_t i = 0;
+ for (CurveSegment seg : segments) {
+ // The library's curve format consists of four doubles per segment:
+ // * maximum pointer speed for the segment (mm/s)
+ // * multiplier for the x² term (a.k.a. "a" or "sqr")
+ // * multiplier for the x term (a.k.a. "b" or "mul")
+ // * the intercept (a.k.a. "c" or "int")
+ // (see struct CurveSegment in the library's AccelFilterInterpreter)
+ output[i + 0] = seg.maxPointerSpeedMmPerS;
+ output[i + 1] = 0;
+ output[i + 2] = commonFactor * seg.slope;
+ output[i + 3] = commonFactor * seg.intercept;
+ i += 4;
+ }
+
+ return output;
+}
+
short getMaxTouchCount(const InputDeviceContext& context) {
if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
@@ -136,6 +217,11 @@
std::list<NotifyArgs> TouchpadInputMapper::configure(nsecs_t when,
const InputReaderConfiguration* config,
uint32_t changes) {
+ if (!changes) {
+ // First time configuration
+ mPropertyProvider.loadPropertiesFromIdcFile(getDeviceContext().getConfiguration());
+ }
+
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
std::optional<int32_t> displayId = mPointerController->getDisplayId();
ui::Rotation orientation = ui::ROTATION_0;
@@ -147,10 +233,12 @@
mGestureConverter.setOrientation(orientation);
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS)) {
- // TODO(b/265798483): load an Android-specific acceleration curve instead of mapping to one
- // of five ChromeOS curves.
- const int pointerSensitivity = (config->touchpadPointerSpeed + 7) / 3 + 1;
- mPropertyProvider.getProperty("Pointer Sensitivity").setIntValues({pointerSensitivity});
+ mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
+ .setBoolValues({true});
+ GesturesProp accelCurveProp = mPropertyProvider.getProperty("Pointer Accel Curve");
+ accelCurveProp.setRealValues(
+ createAccelerationCurveForSensitivity(config->touchpadPointerSpeed,
+ accelCurveProp.getCount()));
mPropertyProvider.getProperty("Invert Scrolling")
.setBoolValues({config->touchpadNaturalScrollingEnabled});
mPropertyProvider.getProperty("Tap Enable")
@@ -178,6 +266,7 @@
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());
mProcessing = true;
mGestureInterpreter->PushHardwareState(&schs.state);
mProcessing = false;
@@ -186,7 +275,7 @@
}
void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
- ALOGD("Gesture ready: %s", gesture->String().c_str());
+ ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
if (!mProcessing) {
ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
return;
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index d636d44..bc9f4c0 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -98,7 +98,6 @@
case kGestureTypePinch:
return handlePinch(when, readTime, gesture);
default:
- // TODO(b/251196347): handle more gesture types.
return {};
}
}
@@ -111,8 +110,8 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
mPointerController->move(deltaX, deltaY);
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- float xCursorPosition, yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
PointerCoords coords;
coords.clear();
@@ -136,8 +135,7 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- float xCursorPosition, yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
PointerCoords coords;
coords.clear();
@@ -202,18 +200,19 @@
const Gesture& gesture) {
std::list<NotifyArgs> out;
PointerCoords& coords = mFakeFingerCoords[0];
- float xCursorPosition, yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
mDownTime = when;
- out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
- /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
+ NotifyMotionArgs args =
+ makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
+ mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+ args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+ out.push_back(args);
}
float deltaX = gesture.details.scroll.dx;
float deltaY = gesture.details.scroll.dy;
@@ -224,9 +223,12 @@
// TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET.
coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, -gesture.details.scroll.dx);
coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
- out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ NotifyMotionArgs args =
+ makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
+ mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+ args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+ out.push_back(args);
return out;
}
@@ -239,14 +241,14 @@
return {};
}
- float xCursorPosition, yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
- NotifyArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
- /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition);
+ NotifyMotionArgs args =
+ makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
+ mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+ args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
mCurrentClassification = MotionClassification::NONE;
return args;
}
@@ -256,8 +258,8 @@
uint32_t fingerCount,
float dx, float dy) {
std::list<NotifyArgs> out = {};
- float xCursorPosition, yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
// If the user changes the number of fingers mid-way through a swipe (e.g. they start with
// three and then put a fourth finger down), the gesture library will treat it as two
@@ -292,8 +294,6 @@
yCursorPosition));
}
}
- // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y
- // values.
float rotatedDeltaX = dx, rotatedDeltaY = -dy;
rotateDelta(mOrientation, &rotatedDeltaX, &rotatedDeltaY);
for (size_t i = 0; i < mSwipeFingerCount; i++) {
@@ -304,8 +304,6 @@
coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + rotatedDeltaY);
}
float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue);
- // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y
- // values.
float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
@@ -322,8 +320,7 @@
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
return out;
}
- float xCursorPosition, yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
@@ -347,8 +344,8 @@
[[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
std::list<NotifyArgs> out;
- float xCursorPosition, yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
// Pinch gesture phases are reported a little differently from others, in that the same details
// struct is used for all phases of the gesture, just with different zoom_state values. When
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 2e175b8..c091a51 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -80,18 +80,32 @@
schs.fingers.clear();
for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
- if (slot.isInUse()) {
- FingerState& fingerState = schs.fingers.emplace_back();
- fingerState = {};
- fingerState.touch_major = slot.getTouchMajor();
- fingerState.touch_minor = slot.getTouchMinor();
- fingerState.width_major = slot.getToolMajor();
- fingerState.width_minor = slot.getToolMinor();
- fingerState.pressure = slot.getPressure();
- fingerState.orientation = slot.getOrientation();
- fingerState.position_x = slot.getX();
- fingerState.position_y = slot.getY();
- fingerState.tracking_id = slot.getTrackingId();
+ if (!slot.isInUse()) {
+ continue;
+ }
+ // Some touchpads continue to report contacts even after they've identified them as palms.
+ // We want to exclude these contacts from the HardwareStates, but still need to report a
+ // tracking ID of -1 if a finger turns into a palm.
+ const bool isPalm = slot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM;
+ if (isPalm && mFingerSlots.find(i) == mFingerSlots.end()) {
+ continue;
+ }
+
+ FingerState& fingerState = schs.fingers.emplace_back();
+ fingerState = {};
+ fingerState.touch_major = slot.getTouchMajor();
+ fingerState.touch_minor = slot.getTouchMinor();
+ fingerState.width_major = slot.getToolMajor();
+ fingerState.width_minor = slot.getToolMinor();
+ fingerState.pressure = slot.getPressure();
+ fingerState.orientation = slot.getOrientation();
+ fingerState.position_x = slot.getX();
+ fingerState.position_y = slot.getY();
+ fingerState.tracking_id = isPalm ? -1 : slot.getTrackingId();
+ if (fingerState.tracking_id == -1) {
+ mFingerSlots.erase(i);
+ } else {
+ mFingerSlots.insert(i);
}
}
schs.state.fingers = schs.fingers.data();
@@ -103,6 +117,7 @@
void HardwareStateConverter::reset() {
mCursorButtonAccumulator.reset(mDeviceContext);
mTouchButtonAccumulator.reset();
+ mFingerSlots.clear();
mMscTimestamp = 0;
}
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 8831299..d6787b7 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -17,6 +17,7 @@
#pragma once
#include <optional>
+#include <set>
#include <utils/Timers.h>
@@ -53,6 +54,7 @@
MultiTouchMotionAccumulator mMotionAccumulator;
TouchButtonAccumulator mTouchButtonAccumulator;
int32_t mMscTimestamp = 0;
+ std::set<size_t> mFingerSlots;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
index cd18cd3..be2bfed 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -19,6 +19,7 @@
#include "gestures/PropertyProvider.h"
#include <algorithm>
+#include <optional>
#include <utility>
#include <android-base/stringprintf.h>
@@ -68,11 +69,11 @@
.free_fn = freeProperty,
};
-bool PropertyProvider::hasProperty(const std::string name) const {
+bool PropertyProvider::hasProperty(const std::string& name) const {
return mProperties.find(name) != mProperties.end();
}
-GesturesProp& PropertyProvider::getProperty(const std::string name) {
+GesturesProp& PropertyProvider::getProperty(const std::string& name) {
return mProperties.at(name);
}
@@ -84,7 +85,30 @@
return dump;
}
-GesturesProp* PropertyProvider::createIntArrayProperty(const std::string name, int* loc,
+void PropertyProvider::loadPropertiesFromIdcFile(const PropertyMap& idcProperties) {
+ // For compatibility with the configuration file syntax, gesture property names in IDC files are
+ // prefixed with "gestureProp." and have spaces replaced by underscores. So, for example, the
+ // configuration key "gestureProp.Palm_Width" refers to the "Palm Width" property.
+ const std::string gesturePropPrefix = "gestureProp.";
+ for (const std::string key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) {
+ std::string propertyName = key.substr(gesturePropPrefix.length());
+ for (size_t i = 0; i < propertyName.length(); i++) {
+ if (propertyName[i] == '_') {
+ propertyName[i] = ' ';
+ }
+ }
+
+ auto it = mProperties.find(propertyName);
+ if (it != mProperties.end()) {
+ it->second.trySetFromIdcProperty(idcProperties, key);
+ } else {
+ ALOGE("Gesture property \"%s\" specified in IDC file does not exist for this device.",
+ propertyName.c_str());
+ }
+ }
+}
+
+GesturesProp* PropertyProvider::createIntArrayProperty(const std::string& name, int* loc,
size_t count, const int* init) {
const auto [it, inserted] =
mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
@@ -92,7 +116,7 @@
return &it->second;
}
-GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string name,
+GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string& name,
GesturesPropBool* loc, size_t count,
const GesturesPropBool* init) {
const auto [it, inserted] =
@@ -101,7 +125,7 @@
return &it->second;
}
-GesturesProp* PropertyProvider::createRealArrayProperty(const std::string name, double* loc,
+GesturesProp* PropertyProvider::createRealArrayProperty(const std::string& name, double* loc,
size_t count, const double* init) {
const auto [it, inserted] =
mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
@@ -109,7 +133,7 @@
return &it->second;
}
-GesturesProp* PropertyProvider::createStringProperty(const std::string name, const char** loc,
+GesturesProp* PropertyProvider::createStringProperty(const std::string& name, const char** loc,
const char* const init) {
const auto [it, inserted] = mProperties.insert(std::pair{name, GesturesProp(name, loc, init)});
LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
@@ -211,6 +235,57 @@
setValues(std::get<double*>(mDataPointer), values);
}
+namespace {
+
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {};
+// explicit deduction guide (not needed as of C++20)
+template <typename... V>
+Visitor(V...) -> Visitor<V...>;
+
+} // namespace
+
+void GesturesProp::trySetFromIdcProperty(const android::PropertyMap& idcProperties,
+ const std::string& propertyName) {
+ if (mCount != 1) {
+ ALOGE("Gesture property \"%s\" is an array, and so cannot be set in an IDC file.",
+ mName.c_str());
+ return;
+ }
+ bool parsedSuccessfully = false;
+ Visitor setVisitor{
+ [&](int*) {
+ if (std::optional<int32_t> value = idcProperties.getInt(propertyName); value) {
+ parsedSuccessfully = true;
+ setIntValues({*value});
+ }
+ },
+ [&](GesturesPropBool*) {
+ if (std::optional<bool> value = idcProperties.getBool(propertyName); value) {
+ parsedSuccessfully = true;
+ setBoolValues({*value});
+ }
+ },
+ [&](double*) {
+ if (std::optional<double> value = idcProperties.getDouble(propertyName); value) {
+ parsedSuccessfully = true;
+ setRealValues({*value});
+ }
+ },
+ [&](const char**) {
+ ALOGE("Gesture property \"%s\" is a string, and so cannot be set in an IDC file.",
+ mName.c_str());
+ // We've already reported the type mismatch, so set parsedSuccessfully.
+ parsedSuccessfully = true;
+ },
+ };
+ std::visit(setVisitor, mDataPointer);
+
+ ALOGE_IF(!parsedSuccessfully, "Gesture property \"%s\" couldn't be set due to a type mismatch.",
+ mName.c_str());
+}
+
template <typename T, typename U>
const std::vector<T> GesturesProp::getValues(U* dataPointer) const {
if (mGetter != nullptr) {
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
index c21260f..c7e0858 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
@@ -22,6 +22,7 @@
#include <vector>
#include "include/gestures.h"
+#include "input/PropertyMap.h"
namespace android {
@@ -31,18 +32,20 @@
// Implementation of a gestures library property provider, which provides configuration parameters.
class PropertyProvider {
public:
- bool hasProperty(const std::string name) const;
- GesturesProp& getProperty(const std::string name);
+ bool hasProperty(const std::string& name) const;
+ GesturesProp& getProperty(const std::string& name);
std::string dump() const;
+ void loadPropertiesFromIdcFile(const PropertyMap& idcProperties);
+
// Methods to be called by the gestures library:
- GesturesProp* createIntArrayProperty(const std::string name, int* loc, size_t count,
+ GesturesProp* createIntArrayProperty(const std::string& name, int* loc, size_t count,
const int* init);
- GesturesProp* createBoolArrayProperty(const std::string name, GesturesPropBool* loc,
+ GesturesProp* createBoolArrayProperty(const std::string& name, GesturesPropBool* loc,
size_t count, const GesturesPropBool* init);
- GesturesProp* createRealArrayProperty(const std::string name, double* loc, size_t count,
+ GesturesProp* createRealArrayProperty(const std::string& name, double* loc, size_t count,
const double* init);
- GesturesProp* createStringProperty(const std::string name, const char** loc,
+ GesturesProp* createStringProperty(const std::string& name, const char** loc,
const char* const init);
void freeProperty(GesturesProp* prop);
@@ -83,6 +86,9 @@
// Setting string values isn't supported since we don't have a use case yet and the memory
// management adds additional complexity.
+ void trySetFromIdcProperty(const android::PropertyMap& idcProperties,
+ const std::string& propertyName);
+
private:
// Two type parameters are required for these methods, rather than one, due to the gestures
// library using its own bool type.
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index af40fed..97138c7 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -86,6 +86,13 @@
],
},
},
+ sanitize: {
+ undefined: true,
+ all_undefined: true,
+ diag: {
+ undefined: true,
+ },
+ },
static_libs: [
"libc++fs",
"libgmock",
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 6ac0bfb..ff6d584 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -255,11 +255,12 @@
return 0;
}
-void FakeEventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+std::optional<PropertyMap> FakeEventHub::getConfiguration(int32_t deviceId) const {
Device* device = getDevice(deviceId);
- if (device) {
- *outConfiguration = device->configuration;
+ if (device == nullptr) {
+ return {};
}
+ return device->configuration;
}
status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 72f8ac0..e0a3f9e 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -159,7 +159,7 @@
ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override;
InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
int32_t getDeviceControllerNumber(int32_t) const override;
- void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
+ std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override;
status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const override;
bool hasRelativeAxis(int32_t deviceId, int axis) const override;
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 28dad95..ad311f9 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -45,9 +45,8 @@
return mButtonState;
}
-void FakePointerController::getPosition(float* outX, float* outY) const {
- *outX = mX;
- *outY = mY;
+FloatPoint FakePointerController::getPosition() const {
+ return {mX, mY};
}
int32_t FakePointerController::getDisplayId() const {
@@ -59,8 +58,7 @@
}
void FakePointerController::assertPosition(float x, float y) {
- float actualX, actualY;
- getPosition(&actualX, &actualY);
+ const auto [actualX, actualY] = getPosition();
ASSERT_NEAR(x, actualX, 1);
ASSERT_NEAR(y, actualY, 1);
}
@@ -69,13 +67,8 @@
return mIsPointerShown;
}
-bool FakePointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
- float* outMaxY) const {
- *outMinX = mMinX;
- *outMinY = mMinY;
- *outMaxX = mMaxX;
- *outMaxY = mMaxY;
- return mHaveBounds;
+std::optional<FloatRect> FakePointerController::getBounds() const {
+ return mHaveBounds ? std::make_optional<FloatRect>(mMinX, mMinY, mMaxX, mMaxY) : std::nullopt;
}
void FakePointerController::move(float deltaX, float deltaY) {
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index dd56e65..6d7bbeb 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -34,7 +34,7 @@
void setPosition(float x, float y) override;
void setButtonState(int32_t buttonState) override;
int32_t getButtonState() const override;
- void getPosition(float* outX, float* outY) const override;
+ FloatPoint getPosition() const override;
int32_t getDisplayId() const override;
void setDisplayViewport(const DisplayViewport& viewport) override;
@@ -42,7 +42,7 @@
bool isPointerShown();
private:
- bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override;
+ std::optional<FloatRect> getBounds() const override;
void move(float deltaX, float deltaY) override;
void fade(Transition) override;
void unfade(Transition) override;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 9c624ba..bbf7e8e 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -252,14 +252,16 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDownTime(downTime)));
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDownTime(downTime),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithCoords(POINTER_X, POINTER_Y - 10),
WithGestureScrollDistance(0, 10, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
@@ -269,7 +271,8 @@
WithCoords(POINTER_X, POINTER_Y - 15),
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -280,7 +283,8 @@
WithCoords(POINTER_X, POINTER_Y - 15),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
}
TEST_F(GestureConverterTest, Scroll_Rotated) {
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 7921881..36b9bab 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -191,6 +191,68 @@
EXPECT_EQ(0u, finger2.flags);
}
+TEST_F(HardwareStateConverterTest, OnePalm) {
+ const nsecs_t time = ARBITRARY_TIME;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(0, schs->state.finger_cnt);
+}
+
+TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalm) {
+ const nsecs_t time = ARBITRARY_TIME;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.finger_cnt);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 51);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 99);
+
+ schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ ASSERT_EQ(1, schs->state.finger_cnt);
+ EXPECT_EQ(-1, schs->state.fingers[0].tracking_id);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 53);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 97);
+
+ schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(0, schs->state.finger_cnt);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 55);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 95);
+ schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ ASSERT_EQ(1, schs->state.finger_cnt);
+ const FingerState& newFinger = schs->state.fingers[0];
+ EXPECT_EQ(123, newFinger.tracking_id);
+ EXPECT_NEAR(55, newFinger.position_x, EPSILON);
+ EXPECT_NEAR(95, newFinger.position_y, EPSILON);
+}
+
TEST_F(HardwareStateConverterTest, ButtonPressed) {
const nsecs_t time = ARBITRARY_TIME;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e71cdce..e299643 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -58,8 +58,23 @@
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr int32_t SECOND_DISPLAY_ID = 1;
+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;
+static constexpr int32_t ACTION_HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER;
+static constexpr int32_t ACTION_HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT;
static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE;
static constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL;
+/**
+ * The POINTER_DOWN(0) is an unusual, but valid, action. It just means that the new pointer in the
+ * MotionEvent is at the index 0 rather than 1 (or later). That is, the pointer id=0 (which is at
+ * index 0) is the new pointer going down. The same pointer could have been placed at a different
+ * index, and the action would become POINTER_1_DOWN, 2, etc..; these would all be valid. In
+ * general, we try to place pointer id = 0 at the index 0. Of course, this is not possible if
+ * pointer id=0 leaves but the pointer id=1 remains.
+ */
+static constexpr int32_t POINTER_0_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_1_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_2_DOWN =
@@ -90,6 +105,8 @@
static constexpr int expectedWallpaperFlags =
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+using ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID;
+
struct PointF {
float x;
float y;
@@ -145,6 +162,10 @@
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());
@@ -163,6 +184,10 @@
return arg.getX(/*pointerIndex=*/0) == x && arg.getY(/*pointerIndex=*/0) == y;
}
+MATCHER_P(WithPointerCount, pointerCount, "MotionEvent with specified number of pointers") {
+ return arg.getPointerCount() == pointerCount;
+}
+
MATCHER_P(WithPointers, pointers, "MotionEvent with specified pointers") {
// Build a map for the received pointers, by pointer id
std::map<int32_t /*pointerId*/, PointF> actualPointers;
@@ -205,10 +230,10 @@
const auto& motionEvent = static_cast<const MotionEvent&>(event);
EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
EXPECT_EQ(motionEvent.getAction(), args.action);
- EXPECT_EQ(motionEvent.getX(0), point.x);
- EXPECT_EQ(motionEvent.getY(0), point.y);
- EXPECT_EQ(motionEvent.getRawX(0), point.x);
- EXPECT_EQ(motionEvent.getRawY(0), point.y);
+ EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION);
});
}
@@ -1922,6 +1947,48 @@
}
/**
+ * Two fingers down on the window, and lift off the first finger.
+ * Next, cancel the gesture to the window by removing the window. Make sure that the CANCEL event
+ * contains a single pointer.
+ */
+TEST_F(InputDispatcherTest, CancelAfterPointer0Up) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ NotifyMotionArgs args;
+ // First touch pointer down on right window
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .build()));
+ // Second touch pointer down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(110).y(100))
+ .build()));
+ // First touch pointer lifts. The second one remains down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(110).y(100))
+ .build()));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));
+
+ // Remove the window. The gesture should be canceled
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ const std::map<int32_t, PointF> expectedPointers{{1, PointF{110, 100}}};
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedPointers)));
+}
+
+/**
* Same test as WhenForegroundWindowDisappears_WallpaperTouchIsCanceled above,
* with the following differences:
* After ACTION_DOWN, Wallpaper window hangs up its channel, which forces the dispatcher to
@@ -2029,8 +2096,17 @@
wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 100}))
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 1,
+ AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .build(),
+ INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
@@ -2365,6 +2441,205 @@
}
/**
+ * Two windows: a window on the left and a window on the right.
+ * Mouse is clicked on the left window and remains down. Touch is touched on the right and remains
+ * down. Then, on the left window, also place second touch pointer down.
+ * This test tries to reproduce a crash.
+ * In the buggy implementation, second pointer down on the left window would cause a crash.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceSplitTouch) {
+ 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, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}});
+
+ const int32_t touchDeviceId = 4;
+ const int32_t mouseDeviceId = 6;
+ NotifyMotionArgs args;
+
+ // Start hovering over the left window
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100))
+ .build()));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Mouse down on left window
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100))
+ .build()));
+
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100))
+ .build()));
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ // First touch pointer down on right window
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100))
+ .build()));
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Second touch pointer down on left window
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .build()));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ // This MOVE event is not necessary (doesn't carry any new information), but it's there in the
+ // current implementation.
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{100, 100}}};
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers)));
+
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * On a single window, use two different devices: mouse and touch.
+ * Touch happens first, with two pointers going down, and then the first pointer leaving.
+ * Mouse is clicked next, which causes the touch stream to be aborted with ACTION_CANCEL.
+ * Finally, a second touch pointer goes down again. Ensure the second touch pointer is ignored,
+ * because the mouse is currently down, and a POINTER_DOWN event from the touchscreen does not
+ * represent a new gesture.
+ */
+TEST_F(InputDispatcherTest, MixedTouchAndMouseWithPointerDown) {
+ 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->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ const int32_t touchDeviceId = 4;
+ const int32_t mouseDeviceId = 6;
+ NotifyMotionArgs args;
+
+ // First touch pointer down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100))
+ .build()));
+ // Second touch pointer down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(350).y(100))
+ .build()));
+ // First touch pointer lifts. The second one remains down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(350).y(100))
+ .build()));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));
+
+ // Mouse down. The touch should be canceled
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(320).y(100))
+ .build()));
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId),
+ WithPointerCount(1u)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(320).y(100))
+ .build()));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ // Second touch pointer down.
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_0_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(350).y(100))
+ .build()));
+ // The pointer_down event should be ignored
+ window->assertNoEvents();
+}
+
+/**
+ * Inject a touch down and then send a new event via 'notifyMotion'. Ensure the new event cancels
+ * the injected event.
+ */
+TEST_F(InputDispatcherTest, UnfinishedInjectedEvent) {
+ 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->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ 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,
+ MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(50)
+ .y(50))
+ .build()));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
+
+ // Now a real touch comes. Rather than crashing or dropping the real event, the injected pointer
+ // should be canceled and the new gesture should take over.
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100))
+ .build()));
+
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
* This test is similar to the test above, but the sequence of injected events is different.
*
* Two windows: a window on the left and a window on the right.
@@ -2541,6 +2816,182 @@
}
/**
+ * A spy window above a window with no input channel.
+ * Start hovering with a stylus device, and then tap with it.
+ * Ensure spy window receives the entire sequence.
+ */
+TEST_F(InputDispatcherTest, StylusHoverAndDownNoInputChannel) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setNoInputChannel(true);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+
+ NotifyMotionArgs args;
+
+ // Start hovering with stylus
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ // Stop hovering
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // Stylus touches down
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Stylus goes up
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
+ // Again hover
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ // Stop hovering
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // No more events
+ spyWindow->assertNoEvents();
+ window->assertNoEvents();
+}
+
+/**
+ * Start hovering with a mouse, and then tap with a touch device. Pilfer the touch stream.
+ * Next, click with the mouse device. Both windows (spy and regular) should receive the new mouse
+ * ACTION_DOWN event because that's a new gesture, and pilfering should no longer be active.
+ * While the mouse is down, new move events from the touch device should be ignored.
+ */
+TEST_F(InputDispatcherTest, TouchPilferAndMouseMove) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+
+ const int32_t mouseDeviceId = 7;
+ const int32_t touchDeviceId = 4;
+ NotifyMotionArgs args;
+
+ // Hover a bit with mouse first
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100))
+ .build()));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Start touching
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(55).y(55))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ // Pilfer the stream
+ EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindow->getToken()));
+ window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(60).y(60))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ // Mouse down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100))
+ .build()));
+
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ // Mouse move!
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(110).y(110))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ // Touch move!
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(65).y(65))
+ .build()));
+
+ // No more events
+ spyWindow->assertNoEvents();
+ window->assertNoEvents();
+}
+
+/**
* On the display, have a single window, and also an area where there's no window.
* First pointer touches the "no window" area of the screen. Second pointer touches the window.
* Make sure that the window receives the second pointer, and first pointer is simply ignored.
@@ -2649,8 +3100,8 @@
window1->assertNoEvents();
// Now move the pointer on the first window
- mDispatcher->notifyMotion(
- &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}})));
+ mDispatcher->notifyMotion(&(
+ args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}, {150, 50}})));
mDispatcher->waitForIdle();
window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
@@ -2705,7 +3156,8 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ windowLeft->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ windowLeft->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2750,7 +3202,6 @@
.x(900)
.y(400))
.build()));
- windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// No more events
@@ -2758,6 +3209,70 @@
windowRight->assertNoEvents();
}
+/**
+ * Put two fingers down (and don't release them) and click the mouse button.
+ * The clicking of mouse is a new ACTION_DOWN event. Since it's from a different device, the
+ * currently active gesture should be canceled, and the new one should proceed.
+ */
+TEST_F(InputDispatcherTest, TwoPointersDownMouseClick) {
+ 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, 600, 800));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ const int32_t touchDeviceId = 4;
+ const int32_t mouseDeviceId = 6;
+ NotifyMotionArgs args;
+
+ // Two pointers down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .build()));
+
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(120).y(120))
+ .build()));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+
+ // Inject a series of mouse events for a mouse click
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(300).y(400))
+ .build()));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId),
+ WithPointerCount(2u)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(300).y(400))
+ .build()));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ // Try to send more touch events while the mouse is down. Since it's a continuation of an
+ // already canceled gesture, it should be ignored.
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(101).y(101))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(121).y(121))
+ .build()));
+ window->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -2940,7 +3455,8 @@
.x(300)
.y(400))
.build()));
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2984,7 +3500,7 @@
.x(300)
.y(400))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ window->assertNoEvents();
}
/**
@@ -3017,6 +3533,42 @@
}
/**
+ * If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT.
+ */
+TEST_F(InputDispatcherTest, TouchDownAfterMouseHover) {
+ 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));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ const int32_t mouseDeviceId = 7;
+ const int32_t touchDeviceId = 4;
+ NotifyMotionArgs args;
+
+ // Start hovering with the mouse
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(10).y(10))
+ .build()));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Touch goes down
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .build()));
+
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
* Inject a mouse hover event followed by a tap from touchscreen.
* The tap causes a HOVER_EXIT event to be generated because the current event
* stream's source has been switched.
@@ -3386,8 +3938,7 @@
public:
void SetUp() override {
InputDispatcherTest::SetUp();
- mDisplayInfos.clear();
- mWindowInfos.clear();
+ removeAllWindowsAndDisplays();
}
void addDisplayInfo(int displayId, const ui::Transform& transform) {
@@ -3403,6 +3954,11 @@
mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
}
+ void removeAllWindowsAndDisplays() {
+ mDisplayInfos.clear();
+ mWindowInfos.clear();
+ }
+
// Set up a test scenario where the display has a scaled projection and there are two windows
// on the display.
std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() {
@@ -3435,11 +3991,11 @@
std::vector<gui::WindowInfo> mWindowInfos;
};
-TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) {
+TEST_F(InputDispatcherDisplayProjectionTest, HitTestCoordinateSpaceConsistency) {
auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
// Send down to the first window. The point is represented in the display space. The point is
- // selected so that if the hit test was done with the transform applied to it, then it would
- // end up in the incorrect window.
+ // selected so that if the hit test was performed with the point and the bounds being in
+ // different coordinate spaces, the event would end up in the incorrect window.
NotifyMotionArgs downMotionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {PointF{75, 55}});
@@ -3514,6 +4070,81 @@
EXPECT_EQ(80, event->getY(0));
}
+/** Ensure consistent behavior of InputDispatcher in all orientations. */
+class InputDispatcherDisplayOrientationFixture
+ : public InputDispatcherDisplayProjectionTest,
+ public ::testing::WithParamInterface<ui::Rotation> {};
+
+// This test verifies the touchable region of a window for all rotations of the display by tapping
+// in different locations on the display, specifically points close to the four corners of a
+// window.
+TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) {
+ constexpr static int32_t displayWidth = 400;
+ constexpr static int32_t displayHeight = 800;
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ const auto rotation = GetParam();
+
+ // Set up the display with the specified rotation.
+ const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270;
+ const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth;
+ const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight;
+ const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation),
+ logicalDisplayWidth, logicalDisplayHeight);
+ addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+
+ // Create a window with its bounds determined in the logical display.
+ const Rect frameInLogicalDisplay(100, 100, 200, 300);
+ const Rect frameInDisplay = displayTransform.inverse().transform(frameInLogicalDisplay);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(frameInDisplay, displayTransform);
+ addWindow(window);
+
+ // The following points in logical display space should be inside the window.
+ static const std::array<vec2, 4> insidePoints{
+ {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}};
+ for (const auto pointInsideWindow : insidePoints) {
+ const vec2 p = displayTransform.inverse().transform(pointInsideWindow);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&down);
+ window->consumeMotionDown();
+
+ const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&up);
+ window->consumeMotionUp();
+ }
+
+ // The following points in logical display space should be outside the window.
+ static const std::array<vec2, 5> outsidePoints{
+ {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}};
+ for (const auto pointOutsideWindow : outsidePoints) {
+ const vec2 p = displayTransform.inverse().transform(pointOutsideWindow);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&down);
+
+ const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&up);
+ }
+ window->assertNoEvents();
+}
+
+// Run the precision tests for all rotations.
+INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests,
+ InputDispatcherDisplayOrientationFixture,
+ ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
+ ui::ROTATION_270),
+ [](const testing::TestParamInfo<ui::Rotation>& testParamInfo) {
+ return ftl::enum_string(testParamInfo.param);
+ });
+
using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher,
sp<IBinder>, sp<IBinder>)>;
@@ -4903,6 +5534,7 @@
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) {
+ GTEST_SKIP() << "Flaky test (b/270393106)";
sendAndConsumeKeyDown(/*deviceId=*/1);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
InputEvent* repeatEvent = mWindow->consume();
@@ -4913,6 +5545,7 @@
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) {
+ GTEST_SKIP() << "Flaky test (b/270393106)";
sendAndConsumeKeyDown(/*deviceId=*/1);
std::unordered_set<int32_t> idSet;
@@ -5042,6 +5675,13 @@
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
+ // Lift up the touch from the second display
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ 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);
+
// Test inject a non-pointer motion event.
// 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.
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index a02ef05..ae30006 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -54,7 +54,8 @@
if (!changes ||
(changes &
(InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
+ InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
+ InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
mReader->requestRefreshConfiguration(changes);
mReader->loopOnce();
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index fe7af80..cdd2439 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -16,6 +16,7 @@
#include <cinttypes>
#include <memory>
+#include <optional>
#include <CursorInputMapper.h>
#include <InputDevice.h>
@@ -1830,7 +1831,7 @@
::testing::Types<UinputTouchScreen, UinputExternalStylus, UinputExternalStylusWithPressure>;
TYPED_TEST_SUITE(StylusButtonIntegrationTest, StylusButtonIntegrationTestTypes);
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsGenerateKeyEvents) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsGenerateKeyEvents) {
const auto stylusId = TestFixture::mStylusInfo.getId();
TestFixture::mStylus->pressKey(BTN_STYLUS);
@@ -1844,7 +1845,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsSurroundingTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -1890,7 +1891,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingHoveringTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsSurroundingHoveringTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -1966,7 +1967,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsWithinTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -2020,7 +2021,7 @@
WithDeviceId(touchscreenId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) {
+TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonMotionEventsDisabled) {
TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false);
TestFixture::mReader->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
@@ -2077,7 +2078,7 @@
// ongoing stylus gesture that is being emitted by the touchscreen.
using ExternalStylusIntegrationTest = TouchIntegrationTest;
-TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
+TEST_F(ExternalStylusIntegrationTest, DISABLED_FusedExternalStylusPressureReported) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus capable of reporting pressure data that
@@ -2123,7 +2124,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
}
-TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) {
+TEST_F(ExternalStylusIntegrationTest, DISABLED_FusedExternalStylusPressureNotReported) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus capable of reporting pressure data that
@@ -2191,7 +2192,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
}
-TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) {
+TEST_F(ExternalStylusIntegrationTest, DISABLED_UnfusedExternalStylus) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus device that does not support pressure. It should not affect any
@@ -2355,10 +2356,10 @@
InputReaderConfiguration config;
std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
- std::string propertyValue;
- ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue))
+ std::optional<std::string> propertyValue = mDevice->getConfiguration().getString("key");
+ ASSERT_TRUE(propertyValue.has_value())
<< "Device should have read configuration during configuration phase.";
- ASSERT_EQ("value", propertyValue);
+ ASSERT_EQ("value", *propertyValue);
ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled());
ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
@@ -3658,8 +3659,8 @@
};
TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
- // For external devices, non-media keys will trigger wake on key down. Media keys need to be
- // marked as WAKE in the keylayout file to trigger wake.
+ // For external devices, keys will trigger wake on key down. Media keys should also trigger
+ // wake if triggered from external devices.
mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
@@ -3681,7 +3682,7 @@
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
@@ -6724,6 +6725,29 @@
ASSERT_FALSE(fakePointerController->isPointerShown());
}
+TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_updatesDeviceType) {
+ // Initialize the device without setting device source to touch navigation.
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // Ensure that the device is created as a touchscreen, not touch navigation.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+
+ // Add device type association after the device was created.
+ mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
+
+ // Send update to the mapper.
+ std::list<NotifyArgs> unused2 =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DEVICE_TYPE /*changes*/);
+
+ // Check whether device type update was successful.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -6731,28 +6755,30 @@
// The values inside DisplayViewport are expected to be pre-rotated. This updates the current
// DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
// rotated equivalent of the given un-rotated physical display bounds.
- void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay) {
+ void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay,
+ int32_t naturalDisplayWidth = DISPLAY_WIDTH,
+ int32_t naturalDisplayHeight = DISPLAY_HEIGHT) {
uint32_t inverseRotationFlags;
- auto width = DISPLAY_WIDTH;
- auto height = DISPLAY_HEIGHT;
+ auto rotatedWidth = naturalDisplayWidth;
+ auto rotatedHeight = naturalDisplayHeight;
switch (orientation) {
case ui::ROTATION_90:
inverseRotationFlags = ui::Transform::ROT_270;
- std::swap(width, height);
+ std::swap(rotatedWidth, rotatedHeight);
break;
case ui::ROTATION_180:
inverseRotationFlags = ui::Transform::ROT_180;
break;
case ui::ROTATION_270:
inverseRotationFlags = ui::Transform::ROT_90;
- std::swap(width, height);
+ std::swap(rotatedWidth, rotatedHeight);
break;
case ui::ROTATION_0:
inverseRotationFlags = ui::Transform::ROT_0;
break;
}
- const ui::Transform rotation(inverseRotationFlags, width, height);
+ const ui::Transform rotation(inverseRotationFlags, rotatedWidth, rotatedHeight);
const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay);
std::optional<DisplayViewport> internalViewport =
@@ -6771,8 +6797,8 @@
v.physicalRight = rotatedPhysicalDisplay.right;
v.physicalBottom = rotatedPhysicalDisplay.bottom;
- v.deviceWidth = width;
- v.deviceHeight = height;
+ v.deviceWidth = rotatedWidth;
+ v.deviceHeight = rotatedHeight;
v.isActive = true;
v.uniqueId = UNIQUE_ID;
@@ -6886,6 +6912,243 @@
}
}
+// --- TouchscreenPrecisionTests ---
+
+// This test suite is used to ensure that touchscreen devices are scaled and configured correctly
+// in various orientations and with different display rotations. We configure the touchscreen to
+// have a higher resolution than that of the display by an integer scale factor in each axis so that
+// we can enforce that coordinates match precisely as expected.
+class TouchscreenPrecisionTestsFixture : public TouchDisplayProjectionTest,
+ public ::testing::WithParamInterface<ui::Rotation> {
+public:
+ void SetUp() override {
+ SingleTouchInputMapperTest::SetUp();
+
+ // Prepare the raw axes to have twice the resolution of the display in the X axis and
+ // four times the resolution of the display in the Y axis.
+ prepareButtons();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, PRECISION_RAW_X_MIN, PRECISION_RAW_X_MAX,
+ PRECISION_RAW_X_FLAT, PRECISION_RAW_X_FUZZ,
+ PRECISION_RAW_X_RES);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, PRECISION_RAW_Y_MIN, PRECISION_RAW_Y_MAX,
+ PRECISION_RAW_Y_FLAT, PRECISION_RAW_Y_FUZZ,
+ PRECISION_RAW_Y_RES);
+ }
+
+ static const int32_t PRECISION_RAW_X_MIN = TouchInputMapperTest::RAW_X_MIN;
+ static const int32_t PRECISION_RAW_X_MAX = PRECISION_RAW_X_MIN + DISPLAY_WIDTH * 2 - 1;
+ static const int32_t PRECISION_RAW_Y_MIN = TouchInputMapperTest::RAW_Y_MIN;
+ static const int32_t PRECISION_RAW_Y_MAX = PRECISION_RAW_Y_MIN + DISPLAY_HEIGHT * 4 - 1;
+
+ static const int32_t PRECISION_RAW_X_RES = 50; // units per millimeter
+ static const int32_t PRECISION_RAW_Y_RES = 100; // units per millimeter
+
+ static const int32_t PRECISION_RAW_X_FLAT = 16;
+ static const int32_t PRECISION_RAW_Y_FLAT = 32;
+
+ static const int32_t PRECISION_RAW_X_FUZZ = 4;
+ static const int32_t PRECISION_RAW_Y_FUZZ = 8;
+
+ static const std::array<Point, 4> kRawCorners;
+};
+
+const std::array<Point, 4> TouchscreenPrecisionTestsFixture::kRawCorners = {{
+ {PRECISION_RAW_X_MIN, PRECISION_RAW_Y_MIN}, // left-top
+ {PRECISION_RAW_X_MAX, PRECISION_RAW_Y_MIN}, // right-top
+ {PRECISION_RAW_X_MAX, PRECISION_RAW_Y_MAX}, // right-bottom
+ {PRECISION_RAW_X_MIN, PRECISION_RAW_Y_MAX}, // left-bottom
+}};
+
+// Tests for how the touchscreen is oriented relative to the natural orientation of the display.
+// For example, if a touchscreen is configured with an orientation of 90 degrees, it is a portrait
+// touchscreen panel that is used on a device whose natural display orientation is in landscape.
+TEST_P(TouchscreenPrecisionTestsFixture, OrientationPrecision) {
+ enum class Orientation {
+ ORIENTATION_0 = ui::toRotationInt(ui::ROTATION_0),
+ ORIENTATION_90 = ui::toRotationInt(ui::ROTATION_90),
+ ORIENTATION_180 = ui::toRotationInt(ui::ROTATION_180),
+ ORIENTATION_270 = ui::toRotationInt(ui::ROTATION_270),
+ ftl_last = ORIENTATION_270,
+ };
+ using Orientation::ORIENTATION_0, Orientation::ORIENTATION_90, Orientation::ORIENTATION_180,
+ Orientation::ORIENTATION_270;
+ static const std::map<Orientation, std::array<vec2, 4> /*mappedCorners*/> kMappedCorners = {
+ {ORIENTATION_0, {{{0, 0}, {479.5, 0}, {479.5, 799.75}, {0, 799.75}}}},
+ {ORIENTATION_90, {{{0, 479.5}, {0, 0}, {799.75, 0}, {799.75, 479.5}}}},
+ {ORIENTATION_180, {{{479.5, 799.75}, {0, 799.75}, {0, 0}, {479.5, 0}}}},
+ {ORIENTATION_270, {{{799.75, 0}, {799.75, 479.5}, {0, 479.5}, {0, 0}}}},
+ };
+
+ const auto touchscreenOrientation = static_cast<Orientation>(ui::toRotationInt(GetParam()));
+
+ // Configure the touchscreen as being installed in the one of the four different orientations
+ // relative to the display.
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addConfigurationProperty("touch.orientation", ftl::enum_string(touchscreenOrientation).c_str());
+ prepareDisplay(ui::ROTATION_0);
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // If the touchscreen is installed in a rotated orientation relative to the display (i.e. in
+ // orientations of either 90 or 270) this means the display's natural resolution will be
+ // flipped.
+ const bool displayRotated =
+ touchscreenOrientation == ORIENTATION_90 || touchscreenOrientation == ORIENTATION_270;
+ const int32_t width = displayRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ const int32_t height = displayRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ const Rect physicalFrame{0, 0, width, height};
+ configurePhysicalDisplay(ui::ROTATION_0, physicalFrame, width, height);
+
+ const auto& expectedPoints = kMappedCorners.at(touchscreenOrientation);
+ const float expectedPrecisionX = displayRotated ? 4 : 2;
+ const float expectedPrecisionY = displayRotated ? 2 : 4;
+
+ // Test all four corners.
+ for (int i = 0; i < 4; i++) {
+ const auto& raw = kRawCorners[i];
+ processDown(mapper, raw.x, raw.y);
+ processSync(mapper);
+ const auto& expected = expectedPoints[i];
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(expected.x, expected.y),
+ WithPrecision(expectedPrecisionX, expectedPrecisionY))))
+ << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+ << "with touchscreen orientation "
+ << ftl::enum_string(touchscreenOrientation).c_str() << ", expected point ("
+ << expected.x << ", " << expected.y << ").";
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(expected.x, expected.y))));
+ }
+}
+
+TEST_P(TouchscreenPrecisionTestsFixture, RotationPrecisionWhenOrientationAware) {
+ static const std::map<ui::Rotation /*rotation*/, std::array<vec2, 4> /*mappedCorners*/>
+ kMappedCorners = {
+ {ui::ROTATION_0, {{{0, 0}, {479.5, 0}, {479.5, 799.75}, {0, 799.75}}}},
+ {ui::ROTATION_90, {{{0.5, 0}, {480, 0}, {480, 799.75}, {0.5, 799.75}}}},
+ {ui::ROTATION_180, {{{0.5, 0.25}, {480, 0.25}, {480, 800}, {0.5, 800}}}},
+ {ui::ROTATION_270, {{{0, 0.25}, {479.5, 0.25}, {479.5, 800}, {0, 800}}}},
+ };
+
+ const ui::Rotation displayRotation = GetParam();
+
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(displayRotation);
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ const auto& expectedPoints = kMappedCorners.at(displayRotation);
+
+ // Test all four corners.
+ for (int i = 0; i < 4; i++) {
+ const auto& expected = expectedPoints[i];
+ const auto& raw = kRawCorners[i];
+ processDown(mapper, raw.x, raw.y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(expected.x, expected.y), WithPrecision(2, 4))))
+ << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+ << "with display rotation " << ui::toCString(displayRotation)
+ << ", expected point (" << expected.x << ", " << expected.y << ").";
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(expected.x, expected.y))));
+ }
+}
+
+TEST_P(TouchscreenPrecisionTestsFixture, RotationPrecisionOrientationAwareInOri270) {
+ static const std::map<ui::Rotation /*orientation*/, std::array<vec2, 4> /*mappedCorners*/>
+ kMappedCorners = {
+ {ui::ROTATION_0, {{{799.75, 0}, {799.75, 479.5}, {0, 479.5}, {0, 0}}}},
+ {ui::ROTATION_90, {{{800, 0}, {800, 479.5}, {0.25, 479.5}, {0.25, 0}}}},
+ {ui::ROTATION_180, {{{800, 0.5}, {800, 480}, {0.25, 480}, {0.25, 0.5}}}},
+ {ui::ROTATION_270, {{{799.75, 0.5}, {799.75, 480}, {0, 480}, {0, 0.5}}}},
+ };
+
+ const ui::Rotation displayRotation = GetParam();
+
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addConfigurationProperty("touch.orientation", "ORIENTATION_270");
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // Ori 270, so width and height swapped
+ const Rect physicalFrame{0, 0, DISPLAY_HEIGHT, DISPLAY_WIDTH};
+ prepareDisplay(displayRotation);
+ configurePhysicalDisplay(displayRotation, physicalFrame, DISPLAY_HEIGHT, DISPLAY_WIDTH);
+
+ const auto& expectedPoints = kMappedCorners.at(displayRotation);
+
+ // Test all four corners.
+ for (int i = 0; i < 4; i++) {
+ const auto& expected = expectedPoints[i];
+ const auto& raw = kRawCorners[i];
+ processDown(mapper, raw.x, raw.y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(expected.x, expected.y), WithPrecision(4, 2))))
+ << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+ << "with display rotation " << ui::toCString(displayRotation)
+ << ", expected point (" << expected.x << ", " << expected.y << ").";
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(expected.x, expected.y))));
+ }
+}
+
+TEST_P(TouchscreenPrecisionTestsFixture, MotionRangesAreOrientedInRotatedDisplay) {
+ const ui::Rotation displayRotation = GetParam();
+
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(displayRotation);
+
+ __attribute__((unused)) SingleTouchInputMapper& mapper =
+ addMapperAndConfigure<SingleTouchInputMapper>();
+
+ const InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
+ // MotionRanges use display pixels as their units
+ const auto* xRange = deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN);
+ const auto* yRange = deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN);
+
+ // The MotionRanges should be oriented in the rotated display's coordinate space
+ const bool displayRotated =
+ displayRotation == ui::ROTATION_90 || displayRotation == ui::ROTATION_270;
+
+ constexpr float MAX_X = 479.5;
+ constexpr float MAX_Y = 799.75;
+ EXPECT_EQ(xRange->min, 0.f);
+ EXPECT_EQ(yRange->min, 0.f);
+ EXPECT_EQ(xRange->max, displayRotated ? MAX_Y : MAX_X);
+ EXPECT_EQ(yRange->max, displayRotated ? MAX_X : MAX_Y);
+
+ EXPECT_EQ(xRange->flat, 8.f);
+ EXPECT_EQ(yRange->flat, 8.f);
+
+ EXPECT_EQ(xRange->fuzz, 2.f);
+ EXPECT_EQ(yRange->fuzz, 2.f);
+
+ EXPECT_EQ(xRange->resolution, 25.f); // pixels per millimeter
+ EXPECT_EQ(yRange->resolution, 25.f); // pixels per millimeter
+}
+
+// Run the precision tests for all rotations.
+INSTANTIATE_TEST_SUITE_P(TouchscreenPrecisionTests, TouchscreenPrecisionTestsFixture,
+ ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
+ ui::ROTATION_270),
+ [](const testing::TestParamInfo<ui::Rotation>& testParamInfo) {
+ return ftl::enum_string(testParamInfo.param);
+ });
+
// --- ExternalStylusFusionTest ---
class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
diff --git a/services/inputflinger/tests/PropertyProvider_test.cpp b/services/inputflinger/tests/PropertyProvider_test.cpp
index 42a6a9f..8a40e78 100644
--- a/services/inputflinger/tests/PropertyProvider_test.cpp
+++ b/services/inputflinger/tests/PropertyProvider_test.cpp
@@ -18,6 +18,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include "TestConstants.h"
#include "include/gestures.h"
namespace android {
@@ -283,4 +284,68 @@
EXPECT_FALSE(mProvider.hasProperty("Foo"));
}
+class PropertyProviderIdcLoadingTest : public testing::Test {
+protected:
+ void SetUp() override {
+ int initialInt = 0;
+ GesturesPropBool initialBool = false;
+ double initialReal = 0.0;
+ gesturePropProvider.create_int_fn(&mProvider, "An Integer", &mIntData, 1, &initialInt);
+ gesturePropProvider.create_bool_fn(&mProvider, "A Boolean", &mBoolData, 1, &initialBool);
+ gesturePropProvider.create_real_fn(&mProvider, "A Real", &mRealData, 1, &initialReal);
+ }
+
+ PropertyProvider mProvider;
+
+ int mIntData;
+ GesturesPropBool mBoolData;
+ double mRealData;
+};
+
+TEST_F(PropertyProviderIdcLoadingTest, AllCorrect) {
+ PropertyMap idcProps;
+ idcProps.addProperty("gestureProp.An_Integer", "42");
+ idcProps.addProperty("gestureProp.A_Boolean", "1");
+ idcProps.addProperty("gestureProp.A_Real", "3.14159");
+
+ mProvider.loadPropertiesFromIdcFile(idcProps);
+ EXPECT_THAT(mProvider.getProperty("An Integer").getIntValues(), ElementsAre(42));
+ EXPECT_THAT(mProvider.getProperty("A Boolean").getBoolValues(), ElementsAre(true));
+ EXPECT_NEAR(mProvider.getProperty("A Real").getRealValues()[0], 3.14159, EPSILON);
+}
+
+TEST_F(PropertyProviderIdcLoadingTest, InvalidPropsIgnored) {
+ int intArrayData[2];
+ int initialInts[2] = {0, 1};
+ gesturePropProvider.create_int_fn(&mProvider, "Two Integers", intArrayData, 2, initialInts);
+
+ PropertyMap idcProps;
+ // Wrong type
+ idcProps.addProperty("gestureProp.An_Integer", "37.25");
+ // Wrong size
+ idcProps.addProperty("gestureProp.Two_Integers", "42");
+ // Doesn't exist
+ idcProps.addProperty("gestureProp.Some_Nonexistent_Property", "1");
+ // A valid assignment that should still be applied despite the others being invalid
+ idcProps.addProperty("gestureProp.A_Real", "3.14159");
+
+ mProvider.loadPropertiesFromIdcFile(idcProps);
+ EXPECT_THAT(mProvider.getProperty("An Integer").getIntValues(), ElementsAre(0));
+ EXPECT_THAT(mProvider.getProperty("Two Integers").getIntValues(), ElementsAre(0, 1));
+ EXPECT_FALSE(mProvider.hasProperty("Some Nonexistent Property"));
+ EXPECT_NEAR(mProvider.getProperty("A Real").getRealValues()[0], 3.14159, EPSILON);
+}
+
+TEST_F(PropertyProviderIdcLoadingTest, FunkyName) {
+ int data;
+ int initialData = 0;
+ gesturePropProvider.create_int_fn(&mProvider, " I lOvE sNAKes ", &data, 1, &initialData);
+
+ PropertyMap idcProps;
+ idcProps.addProperty("gestureProp.__I_lOvE_sNAKes_", "42");
+
+ mProvider.loadPropertiesFromIdcFile(idcProps);
+ EXPECT_THAT(mProvider.getProperty(" I lOvE sNAKes ").getIntValues(), ElementsAre(42));
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h
index 27881f6..ad48b0f 100644
--- a/services/inputflinger/tests/TestConstants.h
+++ b/services/inputflinger/tests/TestConstants.h
@@ -16,6 +16,10 @@
#pragma once
+#include <chrono>
+
+#include <utils/Timers.h>
+
namespace android {
using std::chrono_literals::operator""ms;
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index b9d9607..09f7ae8 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -145,7 +145,7 @@
MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
*result_listener << "expected flags " << flags << ", but got " << arg.flags;
- return arg.flags == flags;
+ return arg.flags == static_cast<int32_t>(flags);
}
MATCHER_P(WithMotionClassification, classification,
@@ -176,4 +176,10 @@
return arg.downTime == downTime;
}
+MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified precision") {
+ *result_listener << "expected x-precision " << xPrecision << " and y-precision " << yPrecision
+ << ", but got " << arg.xPrecision << " and " << arg.yPrecision;
+ return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision;
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 7c9be5c..5d7bf4b 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -86,8 +86,8 @@
int32_t getDeviceControllerNumber(int32_t deviceId) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
- void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
- *outConfiguration = mFuzzConfig;
+ std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override {
+ return mFuzzConfig;
}
status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const override {
@@ -225,14 +225,23 @@
public:
FuzzPointerController(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {}
~FuzzPointerController() {}
- bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
- return mFdp->ConsumeBool();
+ std::optional<FloatRect> getBounds() const override {
+ if (mFdp->ConsumeBool()) {
+ return {};
+ } else {
+ return FloatRect{mFdp->ConsumeFloatingPoint<float>(),
+ mFdp->ConsumeFloatingPoint<float>(),
+ mFdp->ConsumeFloatingPoint<float>(),
+ mFdp->ConsumeFloatingPoint<float>()};
+ }
}
void move(float deltaX, float deltaY) override {}
void setButtonState(int32_t buttonState) override {}
int32_t getButtonState() const override { return mFdp->ConsumeIntegral<int32_t>(); }
void setPosition(float x, float y) override {}
- void getPosition(float* outX, float* outY) const override {}
+ FloatPoint getPosition() const override {
+ return {mFdp->ConsumeFloatingPoint<float>(), mFdp->ConsumeFloatingPoint<float>()};
+ }
void fade(Transition transition) override {}
void unfade(Transition transition) override {}
void setPresentation(Presentation presentation) override {}
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
index 4676167..9e41a93 100644
--- a/services/memtrackproxy/MemtrackProxy.cpp
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -97,9 +97,14 @@
return calling_pid == request_pid;
}
-MemtrackProxy::MemtrackProxy()
- : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
- memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+MemtrackProxy::MemtrackProxy() {
+ memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance();
+
+ // Only check for a HIDL implementation if we failed to get the AIDL service
+ if (!memtrack_aidl_instance_) {
+ memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance();
+ }
+}
ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
std::vector<MemtrackRecord>* _aidl_return) {
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 4ac9651..4fff8bb 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -28,10 +28,10 @@
SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName)
+ const String16& opPackageName, int deviceId)
: mService(service), mUid(uid), mMem(*mem),
mHalChannelHandle(halChannelHandle),
- mOpPackageName(opPackageName), mDestroyed(false) {
+ mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) {
mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -180,8 +180,7 @@
};
Mutex::Autolock _l(mConnectionLock);
- SensorDevice& dev(SensorDevice::getInstance());
- int ret = dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+ int ret = configure(handle, &config);
if (rateLevel == SENSOR_DIRECT_RATE_STOP) {
if (ret == NO_ERROR) {
@@ -224,7 +223,6 @@
std::unordered_map<int, int>& existingConnections =
(!temporarilyStopped) ? mActivated : mActivatedBackup;
- SensorDevice& dev(SensorDevice::getInstance());
for (auto &i : existingConnections) {
int handle = i.first;
int rateLevel = i.second;
@@ -239,8 +237,8 @@
// Only reconfigure the channel if it's ongoing
if (!temporarilyStopped) {
// Stopping before reconfiguring is the well-tested path in CTS
- dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig);
- dev.configureDirectChannel(handle, getHalChannelHandle(), &capConfig);
+ configure(handle, &stopConfig);
+ configure(handle, &capConfig);
}
}
}
@@ -258,7 +256,6 @@
const struct sensors_direct_cfg_t stopConfig = {
.rate_level = SENSOR_DIRECT_RATE_STOP
};
- SensorDevice& dev(SensorDevice::getInstance());
for (auto &i : mMicRateBackup) {
int handle = i.first;
int rateLevel = i.second;
@@ -273,13 +270,23 @@
// Only reconfigure the channel if it's ongoing
if (!temporarilyStopped) {
// Stopping before reconfiguring is the well-tested path in CTS
- dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig);
- dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+ configure(handle, &stopConfig);
+ configure(handle, &config);
}
}
mMicRateBackup.clear();
}
+int SensorService::SensorDirectConnection::configure(
+ int handle, const sensors_direct_cfg_t* config) {
+ if (mDeviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+ SensorDevice& dev(SensorDevice::getInstance());
+ return dev.configureDirectChannel(handle, getHalChannelHandle(), config);
+ } else {
+ return mService->configureRuntimeSensorDirectChannel(handle, this, config);
+ }
+}
+
void SensorService::SensorDirectConnection::stopAll(bool backupRecord) {
Mutex::Autolock _l(mConnectionLock);
stopAllLocked(backupRecord);
@@ -290,9 +297,8 @@
.rate_level = SENSOR_DIRECT_RATE_STOP
};
- SensorDevice& dev(SensorDevice::getInstance());
for (auto &i : mActivated) {
- dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+ configure(i.first, &config);
}
if (backupRecord && mActivatedBackup.empty()) {
@@ -306,8 +312,6 @@
if (!mActivatedBackup.empty()) {
stopAllLocked(false);
- SensorDevice& dev(SensorDevice::getInstance());
-
// recover list of report from backup
ALOG_ASSERT(mActivated.empty(),
"mActivated must be empty if mActivatedBackup was non-empty");
@@ -319,7 +323,7 @@
struct sensors_direct_cfg_t config = {
.rate_level = i.second
};
- dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+ configure(i.first, &config);
}
}
}
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index d39a073..bfaf811 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -39,7 +39,7 @@
public:
SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName);
+ const String16& opPackageName, int deviceId);
void dump(String8& result) const;
void dump(util::ProtoOutputStream* proto) const;
uid_t getUid() const { return mUid; }
@@ -53,6 +53,7 @@
void onSensorAccessChanged(bool hasAccess);
void onMicSensorAccessChanged(bool isMicToggleOn);
userid_t getUserId() const { return mUserId; }
+ int getDeviceId() const { return mDeviceId; }
protected:
virtual ~SensorDirectConnection();
@@ -68,6 +69,9 @@
private:
bool hasSensorAccess() const;
+ // Sends the configuration to the relevant sensor device.
+ int configure(int handle, const sensors_direct_cfg_t* config);
+
// Stops all active sensor direct report requests.
//
// If backupRecord is true, stopped requests can be recovered
@@ -95,6 +99,7 @@
const sensors_direct_mem_t mMem;
const int32_t mHalChannelHandle;
const String16 mOpPackageName;
+ const int mDeviceId;
mutable Mutex mConnectionLock;
std::unordered_map<int, int> mActivated;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3a0329c..0fb3cad 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -181,7 +181,7 @@
handle, sensor.type, sensor.name);
sp<RuntimeSensor::SensorCallback> runtimeSensorCallback(
- new RuntimeSensorCallbackProxy(std::move(callback)));
+ new RuntimeSensorCallbackProxy(callback));
sensor_t runtimeSensor = sensor;
// force the handle to be consistent
runtimeSensor.handle = handle;
@@ -193,11 +193,15 @@
return mSensors.getNonSensor().getHandle();
}
+ if (mRuntimeSensorCallbacks.find(deviceId) == mRuntimeSensorCallbacks.end()) {
+ mRuntimeSensorCallbacks.emplace(deviceId, callback);
+ }
return handle;
}
status_t SensorService::unregisterRuntimeSensor(int handle) {
ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle);
+ int deviceId = getDeviceIdFromHandle(handle);
{
Mutex::Autolock _l(mLock);
if (!unregisterDynamicSensorLocked(handle)) {
@@ -210,6 +214,20 @@
for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
connection->removeSensor(handle);
}
+
+ // If this was the last sensor for this device, remove its callback.
+ bool deviceHasSensors = false;
+ mSensors.forEachEntry(
+ [&deviceId, &deviceHasSensors] (const SensorServiceUtil::SensorList::Entry& e) -> bool {
+ if (e.deviceId == deviceId) {
+ deviceHasSensors = true;
+ return false; // stop iterating
+ }
+ return true;
+ });
+ if (!deviceHasSensors) {
+ mRuntimeSensorCallbacks.erase(deviceId);
+ }
return OK;
}
@@ -1517,7 +1535,7 @@
}
sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
- const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
+ const String16& opPackageName, int deviceId, uint32_t size, int32_t type, int32_t format,
const native_handle *resource) {
resetTargetSdkVersionCache(opPackageName);
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
@@ -1593,14 +1611,25 @@
native_handle_set_fdsan_tag(clone);
sp<SensorDirectConnection> conn;
- SensorDevice& dev(SensorDevice::getInstance());
- int channelHandle = dev.registerDirectChannel(&mem);
+ int channelHandle = 0;
+ if (deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+ SensorDevice& dev(SensorDevice::getInstance());
+ channelHandle = dev.registerDirectChannel(&mem);
+ } else {
+ auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+ if (runtimeSensorCallback == mRuntimeSensorCallbacks.end()) {
+ ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+ } else {
+ int fd = dup(clone->data[0]);
+ channelHandle = runtimeSensorCallback->second->onDirectChannelCreated(fd);
+ }
+ }
if (channelHandle <= 0) {
ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
} else {
mem.handle = clone;
- conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName);
+ conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId);
}
if (conn == nullptr) {
@@ -1614,6 +1643,24 @@
return conn;
}
+int SensorService::configureRuntimeSensorDirectChannel(
+ int sensorHandle, const SensorDirectConnection* c, const sensors_direct_cfg_t* config) {
+ int deviceId = c->getDeviceId();
+ int sensorDeviceId = getDeviceIdFromHandle(sensorHandle);
+ if (sensorDeviceId != c->getDeviceId()) {
+ ALOGE("Cannot configure direct channel created for device %d with a sensor that belongs"
+ "to device %d", c->getDeviceId(), sensorDeviceId);
+ return BAD_VALUE;
+ }
+ auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+ if (runtimeSensorCallback == mRuntimeSensorCallbacks.end()) {
+ ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+ return BAD_VALUE;
+ }
+ return runtimeSensorCallback->second->onDirectChannelConfigured(
+ c->getHalChannelHandle(), sensorHandle, config->rate_level);
+}
+
int SensorService::setOperationParameter(
int32_t handle, int32_t type,
const Vector<float> &floats, const Vector<int32_t> &ints) {
@@ -1769,8 +1816,18 @@
void SensorService::cleanupConnection(SensorDirectConnection* c) {
Mutex::Autolock _l(mLock);
- SensorDevice& dev(SensorDevice::getInstance());
- dev.unregisterDirectChannel(c->getHalChannelHandle());
+ int deviceId = c->getDeviceId();
+ if (deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+ SensorDevice& dev(SensorDevice::getInstance());
+ dev.unregisterDirectChannel(c->getHalChannelHandle());
+ } else {
+ auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+ if (runtimeSensorCallback != mRuntimeSensorCallbacks.end()) {
+ runtimeSensorCallback->second->onDirectChannelDestroyed(c->getHalChannelHandle());
+ } else {
+ ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+ }
+ }
mConnectionHolder.removeDirectConnection(c);
}
@@ -1848,6 +1905,19 @@
return mSensors.getInterface(handle);
}
+int SensorService::getDeviceIdFromHandle(int handle) const {
+ int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID;
+ mSensors.forEachEntry(
+ [&deviceId, handle] (const SensorServiceUtil::SensorList::Entry& e) -> bool {
+ if (e.si->getSensor().getHandle() == handle) {
+ deviceId = e.deviceId;
+ return false; // stop iterating
+ }
+ return true;
+ });
+ return deviceId;
+}
+
status_t SensorService::enable(const sp<SensorEventConnection>& connection,
int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,
const String16& opPackageName) {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 3f6a895..fe72a69 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -154,6 +154,10 @@
virtual status_t onConfigurationChanged(int handle, bool enabled,
int64_t samplingPeriodNanos,
int64_t batchReportLatencyNanos) = 0;
+ virtual int onDirectChannelCreated(int fd) = 0;
+ virtual void onDirectChannelDestroyed(int channelHandle) = 0;
+ virtual int onDirectChannelConfigured(int channelHandle, int sensorHandle,
+ int rateLevel) = 0;
};
static char const* getServiceName() ANDROID_API { return "sensorservice"; }
@@ -187,6 +191,9 @@
status_t unregisterRuntimeSensor(int handle) ANDROID_API;
status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API;
+ int configureRuntimeSensorDirectChannel(int sensorHandle, const SensorDirectConnection* c,
+ const sensors_direct_cfg_t* config);
+
// Returns true if a sensor should be throttled according to our rate-throttling rules.
static bool isSensorInCappedSet(int sensorType);
@@ -370,7 +377,8 @@
int requestedMode, const String16& opPackageName, const String16& attributionTag);
virtual int isDataInjectionEnabled();
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
- uint32_t size, int32_t type, int32_t format, const native_handle *resource);
+ int deviceId, uint32_t size, int32_t type, int32_t format,
+ const native_handle *resource);
virtual int setOperationParameter(
int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -380,6 +388,7 @@
String8 getSensorStringType(int handle) const;
bool isVirtualSensor(int handle) const;
std::shared_ptr<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
+ int getDeviceIdFromHandle(int handle) const;
bool isWakeUpSensor(int type) const;
void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
static void sortEventBuffer(sensors_event_t* buffer, size_t count);
@@ -517,6 +526,7 @@
std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
Mode mCurrentOperatingMode;
std::queue<sensors_event_t> mRuntimeSensorEventQueue;
+ std::unordered_map</*deviceId*/int, sp<RuntimeSensorCallback>> mRuntimeSensorCallbacks;
// true if the head tracker sensor type is currently restricted to system usage only
// (can only be unrestricted for testing, via shell cmd)
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index b7aecdf..08e00b4 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -59,6 +59,9 @@
if (mPollThread.joinable()) {
mPollThread.join();
}
+
+ ::android::SensorManager::removeInstanceForPackage(
+ String16(ISensorManager::descriptor));
}
ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type,
diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
index 1b63d76..ee8ceb3 100644
--- a/services/sensorservice/aidl/fuzzer/fuzzer.cpp
+++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
@@ -16,7 +16,7 @@
#include <fuzzbinder/libbinder_ndk_driver.h>
#include <fuzzer/FuzzedDataProvider.h>
-#include <ServiceManager.h>
+#include <fakeservicemanager/FakeServiceManager.h>
#include <android-base/logging.h>
#include <android/binder_interface_utils.h>
#include <fuzzbinder/random_binder.h>
@@ -29,7 +29,7 @@
[[clang::no_destroy]] static std::once_flag gSmOnce;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager();
+ static android::sp<android::FakeServiceManager> fakeServiceManager = new android::FakeServiceManager();
std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); });
fakeServiceManager->clear();
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
index f04712c..3d148e1 100644
--- a/services/sensorservice/hidl/SensorManager.cpp
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -60,6 +60,9 @@
if (mPollThread.joinable()) {
mPollThread.join();
}
+
+ ::android::SensorManager::removeInstanceForPackage(
+ String16(ISensorManager::descriptor));
}
// Methods from ::android::frameworks::sensorservice::V1_0::ISensorManager follow.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 32684fc..eae5871 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -18,6 +18,7 @@
#include <cstdint>
+#include <android/gui/CachingHint.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/BlurRegion.h>
@@ -212,8 +213,7 @@
float currentSdrHdrRatio = 1.f;
float desiredSdrHdrRatio = 1.f;
- bool isInternalDisplayOverlay = false;
-
+ gui::CachingHint cachingHint = gui::CachingHint::Enabled;
virtual ~LayerFECompositionState();
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 24a7744..d26ca9d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -149,6 +149,9 @@
bool hasSolidColorLayers() const;
+ // True if any layer in this cached set has CachingHint::Disabled
+ bool cachingHintExcludesLayers() const;
+
private:
const NonBufferHash mFingerprint;
std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index e309442..d5c488e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -73,6 +73,7 @@
BackgroundBlurRadius = 1u << 17,
BlurRegions = 1u << 18,
HasProtectedContent = 1u << 19,
+ CachingHint = 1u << 20,
};
// clang-format on
@@ -250,6 +251,8 @@
bool isProtected() const { return mIsProtected.get(); }
+ gui::CachingHint getCachingHint() const { return mCachingHint.get(); }
+
bool hasSolidColorCompositionType() const {
return getOutputLayer()->getLayerFE().getCompositionState()->compositionType ==
aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
@@ -487,7 +490,15 @@
return layer->getLayerFE().getCompositionState()->hasProtectedContent;
}};
- static const constexpr size_t kNumNonUniqueFields = 18;
+ OutputLayerState<gui::CachingHint, LayerStateField::CachingHint>
+ mCachingHint{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->cachingHint;
+ },
+ [](const gui::CachingHint& cachingHint) {
+ return std::vector<std::string>{toString(cachingHint)};
+ }};
+
+ static const constexpr size_t kNumNonUniqueFields = 19;
std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -501,13 +512,11 @@
}
std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
- return {
- &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode,
+ return {&mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode,
&mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
&mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
&mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions,
- &mFrameNumber, &mIsProtected,
- };
+ &mFrameNumber, &mIsProtected, &mCachingHint};
}
};
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 531b659..615d04b 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -126,6 +126,7 @@
dumpVal(out, "desired sdr/hdr ratio", desiredSdrHdrRatio);
}
dumpVal(out, "colorTransform", colorTransform);
+ dumpVal(out, "caching hint", toString(cachingHint));
out.append("\n");
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 7d94316..175dd1d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -538,8 +538,8 @@
return;
}
- bool computeAboveCoveredExcludingOverlays =
- coverage.aboveCoveredLayersExcludingOverlays && !layerFEState->isInternalDisplayOverlay;
+ bool computeAboveCoveredExcludingOverlays = coverage.aboveCoveredLayersExcludingOverlays &&
+ !layerFEState->outputFilter.toInternalDisplay;
/*
* opaqueRegion: area of a surface that is fully opaque.
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ed9a88d..a00ce57 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -393,6 +393,18 @@
});
}
+bool CachedSet::cachingHintExcludesLayers() const {
+ const bool shouldExcludeLayers =
+ std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
+ return layer.getState()->getCachingHint() == gui::CachingHint::Disabled;
+ });
+
+ LOG_ALWAYS_FATAL_IF(shouldExcludeLayers && getLayerCount() > 1,
+ "CachedSet is invalid: should be excluded but contains %zu layers",
+ getLayerCount());
+ return shouldExcludeLayers;
+}
+
void CachedSet::dump(std::string& result) const {
const auto now = std::chrono::steady_clock::now();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 9175dd0..13b6307 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -413,6 +413,7 @@
for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
bool layerIsInactive = now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
const bool layerHasBlur = currentSet->hasBlurBehind();
+ const bool layerDeniedFromCaching = currentSet->cachingHintExcludesLayers();
// Layers should also be considered inactive whenever their framerate is lower than 1fps.
if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) {
@@ -424,7 +425,8 @@
}
}
- if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
+ if (!layerDeniedFromCaching && layerIsInactive &&
+ (firstLayer || runHasFirstLayer || !layerHasBlur) &&
!currentSet->hasUnsupportedDataspace()) {
if (isPartOfRun) {
builder.increment();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 8555fd6..1a56ab7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -147,6 +147,7 @@
MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
getOverlaySupport, (), (const, override));
+ MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9ad2edb..aa83883 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1312,6 +1312,18 @@
false);
}
+TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) {
+ mLayerFEState.compositionType = Composition::REFRESH_RATE_INDICATOR;
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Composition::REFRESH_RATE_INDICATOR);
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
/*
* OutputLayer::uncacheBuffers
*/
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index d5d688e..ca5ba69 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -577,6 +577,20 @@
cachedSet.append(CachedSet(layer3));
}
+TEST_F(CachedSetTest, cachingHintIncludesLayersByDefault) {
+ CachedSet cachedSet(*mTestLayers[0]->cachedSetLayer.get());
+ EXPECT_FALSE(cachedSet.cachingHintExcludesLayers());
+}
+
+TEST_F(CachedSetTest, cachingHintExcludesLayersWhenDisabled) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled;
+ mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer);
+
+ CachedSet cachedSet(layer1);
+ EXPECT_TRUE(cachedSet.cachingHintExcludesLayers());
+}
+
TEST_F(CachedSetTest, holePunch_requiresBuffer) {
CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 86cfee6..778a0a8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -1112,6 +1112,55 @@
true);
}
+TEST_F(FlattenerTest, flattenLayers_skipsLayersDisabledFromCaching) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // The third layer has a CachingHint that prevents caching from running
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ mTestLayers[2]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled;
+ mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ mTime += 200ms;
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
auto& layerState1 = mTestLayers[0]->layerState;
const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 47b6820..044917e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -994,6 +994,45 @@
EXPECT_TRUE(mLayerState->hasBlurBehind());
}
+TEST_F(LayerStateTest, updateCachingHint) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.cachingHint = gui::CachingHint::Enabled;
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.cachingHint = gui::CachingHint::Disabled;
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(ftl::Flags<LayerStateField>(LayerStateField::CachingHint), updates);
+}
+
+TEST_F(LayerStateTest, compareCachingHint) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.cachingHint = gui::CachingHint::Enabled;
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.cachingHint = gui::CachingHint::Disabled;
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::CachingHint);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
TEST_F(LayerStateTest, dumpDoesNotCrash) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5f73fbc..064bbd2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -167,6 +167,7 @@
.receivesInput = receivesInput(),
.isSecure = isSecure(),
.isPrimary = isPrimary(),
+ .isVirtual = isVirtual(),
.rotationFlags = ui::Transform::toRotationFlags(mOrientation),
.transformHint = getTransformHint()};
}
@@ -408,8 +409,8 @@
capabilities.getDesiredMinLuminance());
}
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate,
- bool showInMiddle) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
+ bool showRenderRate, bool showInMiddle) {
if (!enable) {
mRefreshRateOverlay.reset();
return;
@@ -428,11 +429,22 @@
features |= RefreshRateOverlay::Features::ShowInMiddle;
}
+ if (setByHwc) {
+ features |= RefreshRateOverlay::Features::SetByHwc;
+ }
+
const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+ updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+}
+
+void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) {
+ ATRACE_CALL();
+ if (mRefreshRateOverlay && (!mRefreshRateOverlay->isSetByHwc() || setByHwc)) {
+ mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
+ }
}
bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
@@ -441,7 +453,7 @@
const auto newMode =
mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
if (newMode) {
- mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
+ updateRefreshRateOverlayRate(newMode->modePtr->getFps(), newMode->fps);
return true;
}
}
@@ -510,21 +522,21 @@
mDesiredActiveModeChanged = false;
}
-void DisplayDevice::adjustRefreshRate(Fps leaderDisplayRefreshRate) {
+void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
using fps_approx_ops::operator==;
if (mRequestedRefreshRate == 0_Hz) {
return;
}
using fps_approx_ops::operator>;
- if (mRequestedRefreshRate > leaderDisplayRefreshRate) {
- mAdjustedRefreshRate = leaderDisplayRefreshRate;
+ if (mRequestedRefreshRate > pacesetterDisplayRefreshRate) {
+ mAdjustedRefreshRate = pacesetterDisplayRefreshRate;
return;
}
unsigned divisor = static_cast<unsigned>(
- std::round(leaderDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
- mAdjustedRefreshRate = leaderDisplayRefreshRate / divisor;
+ std::round(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+ mAdjustedRefreshRate = pacesetterDisplayRefreshRate / divisor;
}
std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index b86d9be..d9c3e1c 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -237,8 +237,9 @@
}
// Enables an overlay to be displayed with the current refresh rate
- void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate,
+ 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();
@@ -247,9 +248,9 @@
Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; }
- // Round the requested refresh rate to match a divisor of the leader
+ // Round the requested refresh rate to match a divisor of the pacesetter
// display's refresh rate. Only supported for virtual displays.
- void adjustRefreshRate(Fps leaderDisplayRefreshRate);
+ void adjustRefreshRate(Fps pacesetterDisplayRefreshRate);
// release HWC resources (if any) for removable displays
void disconnect();
@@ -290,7 +291,7 @@
// for virtual displays to match this requested refresh rate.
const Fps mRequestedRefreshRate;
- // Adjusted refresh rate, rounded to match a divisor of the leader
+ // Adjusted refresh rate, rounded to match a divisor of the pacesetter
// display's refresh rate. Only supported for virtual displays.
Fps mAdjustedRefreshRate = 0_Hz;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 4194a7e..bd2680f 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1428,6 +1428,19 @@
return Error::NONE;
}
+Error AidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display displayId, bool enabled) {
+ const auto status =
+ mAidlComposerClient->setRefreshRateChangedCallbackDebugEnabled(translate<int64_t>(
+ displayId),
+ enabled);
+ if (!status.isOk()) {
+ ALOGE("setRefreshRateChangedCallbackDebugEnabled failed %s",
+ status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
Error AidlComposer::getClientTargetProperty(
Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
Error error = Error::NONE;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d163ff2..8313c09 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -239,6 +239,7 @@
void onHotplugDisconnect(Display) override;
Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override;
Error setHdrConversionStrategy(HdrConversionStrategy, Hdr*) override;
+ Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
private:
// Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 9b9b7fd..c65c572 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -294,6 +294,7 @@
std::vector<::aidl::android::hardware::graphics::common::HdrConversionCapability>*) = 0;
virtual Error setHdrConversionStrategy(
::aidl::android::hardware::graphics::common::HdrConversionStrategy, Hdr*) = 0;
+ virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 470bf76..28148ac 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -808,6 +808,21 @@
return NO_ERROR;
}
+status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId displayId,
+ bool enabled) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mComposer->setRefreshRateChangedCallbackDebugEnabled(mDisplayData[displayId]
+ .hwcDisplay->getId(),
+ enabled);
+ if (error != hal::Error::NONE) {
+ ALOGE("Error in setting refresh refresh rate change callback debug enabled %s",
+ to_string(error).c_str());
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
status_t HWComposer::getDisplayDecorationSupport(
PhysicalDisplayId displayId,
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 95568eb..7a3f41c 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -297,6 +297,7 @@
virtual status_t setHdrConversionStrategy(
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) = 0;
+ virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -453,6 +454,7 @@
status_t setHdrConversionStrategy(
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) override;
+ status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 9bc62b6..23de4fa 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1358,6 +1358,10 @@
return Error::UNSUPPORTED;
}
+Error HidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display, bool) {
+ return Error::UNSUPPORTED;
+}
+
Error HidlComposer::getClientTargetProperty(
Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
IComposerClient::ClientTargetProperty property;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 2bab1fe..d04652b 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -348,6 +348,7 @@
override;
Error setHdrConversionStrategy(aidl::android::hardware::graphics::common::HdrConversionStrategy,
Hdr*) override;
+ Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 925f111..ded734e 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -885,15 +885,17 @@
void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
nsecs_t previousPresentTime) {
- if (mPredictionState == PredictionState::Expired ||
- mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+ const bool presentTimeValid =
+ mSurfaceFlingerActuals.presentTime >= mSurfaceFlingerActuals.startTime;
+ if (mPredictionState == PredictionState::Expired || !presentTimeValid) {
// Cannot do jank classification with expired predictions or invalid signal times. Set the
// deltas to 0 as both negative and positive deltas are used as real values.
mJankType = JankType::Unknown;
deadlineDelta = 0;
deltaToVsync = 0;
- if (mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+ if (!presentTimeValid) {
mSurfaceFlingerActuals.presentTime = mSurfaceFlingerActuals.endTime;
+ mJankType |= JankType::DisplayHAL;
}
return;
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
index 6b9d7a2..76b36fe 100644
--- a/services/surfaceflinger/FrontEnd/DisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -30,6 +30,7 @@
bool isSecure;
// TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
bool isPrimary;
+ bool isVirtual;
ui::Transform::RotationFlags rotationFlags;
ui::Transform::RotationFlags transformHint;
std::string getDebugString() const {
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
index 6d492c0..ce21233 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -24,9 +24,13 @@
std::atomic<uint32_t> LayerCreationArgs::sSequence{1};
+uint32_t LayerCreationArgs::getInternalLayerId(uint32_t id) {
+ return id | INTERNAL_LAYER_PREFIX;
+}
+
LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
uint32_t flags, gui::LayerMetadata metadataArg,
- std::optional<uint32_t> id)
+ std::optional<uint32_t> id, bool internalLayer)
: flinger(flinger),
client(std::move(client)),
name(std::move(name)),
@@ -46,17 +50,28 @@
if (id) {
sequence = *id;
- sSequence = *id + 1;
+ if (internalLayer) {
+ sequence = getInternalLayerId(*id);
+ } else {
+ sSequence = *id + 1;
+ }
} else {
sequence = sSequence++;
- if (sequence == UNASSIGNED_LAYER_ID) {
+ if (sequence >= INTERNAL_LAYER_PREFIX) {
+ sSequence = 1;
ALOGW("Layer sequence id rolled over.");
sequence = sSequence++;
}
}
}
-LayerCreationArgs::LayerCreationArgs(const LayerCreationArgs& args)
- : LayerCreationArgs(args.flinger, args.client, args.name, args.flags, args.metadata) {}
+LayerCreationArgs::LayerCreationArgs(std::optional<uint32_t> id, bool internalLayer)
+ : LayerCreationArgs(nullptr, nullptr, /*name=*/"", /*flags=*/0, /*metadata=*/{}, id,
+ internalLayer) {}
+
+LayerCreationArgs LayerCreationArgs::fromOtherArgs(const LayerCreationArgs& other) {
+ // returns a new instance of LayerCreationArgs with a unique id.
+ return LayerCreationArgs(other.flinger, other.client, other.name, other.flags, other.metadata);
+}
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 9d2aaab..011250c 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -25,6 +25,7 @@
#include <optional>
constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max();
+constexpr uint32_t INTERNAL_LAYER_PREFIX = 1u << 31;
namespace android {
class SurfaceFlinger;
@@ -35,11 +36,14 @@
struct LayerCreationArgs {
static std::atomic<uint32_t> sSequence;
+ static uint32_t getInternalLayerId(uint32_t id);
+ static LayerCreationArgs fromOtherArgs(const LayerCreationArgs& other);
LayerCreationArgs(android::SurfaceFlinger*, sp<android::Client>, std::string name,
- uint32_t flags, gui::LayerMetadata,
- std::optional<uint32_t> id = std::nullopt);
- LayerCreationArgs(const LayerCreationArgs&);
+ uint32_t flags, gui::LayerMetadata, std::optional<uint32_t> id = std::nullopt,
+ bool internalLayer = false);
+ LayerCreationArgs(std::optional<uint32_t> id, bool internalLayer = false);
+ LayerCreationArgs() = default; // for tracing
android::SurfaceFlinger* flinger;
sp<android::Client> client;
@@ -54,6 +58,8 @@
wp<IBinder> parentHandle = nullptr;
wp<IBinder> mirrorLayerHandle = nullptr;
ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+ uint32_t parentId = UNASSIGNED_LAYER_ID;
+ uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index afe557e..c30465f 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -130,6 +130,14 @@
return mLayer;
}
+const LayerHierarchy* LayerHierarchy::getRelativeParent() const {
+ return mRelativeParent;
+}
+
+const LayerHierarchy* LayerHierarchy::getParent() const {
+ return mParent;
+}
+
std::string LayerHierarchy::getDebugStringShort() const {
std::string debug = "LayerHierarchy{";
debug += ((mLayer) ? mLayer->getDebugString() : "root") + " ";
@@ -449,7 +457,7 @@
traversalPath.id = layerId;
traversalPath.variant = variant;
if (variant == LayerHierarchy::Variant::Mirror) {
- traversalPath.mirrorRootId = layerId;
+ traversalPath.mirrorRootId = mParentPath.id;
} else if (variant == LayerHierarchy::Variant::Relative) {
if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
layerId) != traversalPath.relativeRootIds.end()) {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 2ab897b..3dd89ba 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -128,16 +128,24 @@
// Traverse the hierarchy and visit all child variants.
void traverse(const Visitor& visitor) const {
TraversalPath root = TraversalPath::ROOT;
+ if (mLayer) {
+ root.id = mLayer->id;
+ }
traverse(visitor, root);
}
// Traverse the hierarchy in z-order, skipping children that have relative parents.
void traverseInZOrder(const Visitor& visitor) const {
TraversalPath root = TraversalPath::ROOT;
+ if (mLayer) {
+ root.id = mLayer->id;
+ }
traverseInZOrder(visitor, root);
}
const RequestedLayerState* getLayer() const;
+ const LayerHierarchy* getRelativeParent() const;
+ const LayerHierarchy* getParent() const;
std::string getDebugString(const char* prefix = "") const;
std::string getDebugStringShort() const;
// Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 66197be..3706225 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -20,8 +20,7 @@
#define LOG_TAG "LayerLifecycleManager"
#include "LayerLifecycleManager.h"
-#include "Layer.h" // temporarily needed for LayerHandle
-#include "LayerHandle.h"
+#include "Client.h" // temporarily needed for LayerCreationArgs
#include "LayerLog.h"
#include "SwapErase.h"
@@ -37,7 +36,6 @@
mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
for (auto& newLayer : newLayers) {
RequestedLayerState& layer = *newLayer.get();
- LLOGV(layer.id, "%s layer %s", __func__, layer.getDebugStringShort().c_str());
auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
if (!inserted) {
LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
@@ -68,19 +66,23 @@
if (layer.isRoot()) {
updateDisplayMirrorLayers(layer);
}
+ LLOGV(layer.id, "%s", layer.getDebugString().c_str());
mLayers.emplace_back(std::move(newLayer));
}
}
-void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles) {
+void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles,
+ bool ignoreUnknownHandles) {
std::vector<uint32_t> layersToBeDestroyed;
for (const auto& layerId : destroyedHandles) {
auto it = mIdToLayer.find(layerId);
if (it == mIdToLayer.end()) {
- LOG_ALWAYS_FATAL("%s Layerid not found %d", __func__, layerId);
+ LOG_ALWAYS_FATAL_IF(!ignoreUnknownHandles, "%s Layerid not found %d", __func__,
+ layerId);
continue;
}
RequestedLayerState& layer = it->second.owner;
+ LLOGV(layer.id, "%s", layer.getDebugString().c_str());
layer.handleAlive = false;
if (!layer.canBeDestroyed()) {
continue;
@@ -148,7 +150,7 @@
while (it != mLayers.end()) {
RequestedLayerState* layer = it->get();
if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) {
- LLOGV(layer->id, "destroyed layer %s", layer->getDebugStringShort().c_str());
+ LLOGV(layer->id, "destroyed %s", layer->getDebugStringShort().c_str());
std::iter_swap(it, mLayers.end() - 1);
mDestroyedLayers.emplace_back(std::move(mLayers.back()));
if (it == mLayers.end() - 1) {
@@ -166,7 +168,7 @@
for (const auto& transaction : transactions) {
for (const auto& resolvedComposerState : transaction.states) {
const auto& clientState = resolvedComposerState.state;
- uint32_t layerId = LayerHandle::getLayerId(clientState.surface);
+ uint32_t layerId = resolvedComposerState.layerId;
if (layerId == UNASSIGNED_LAYER_ID) {
ALOGW("%s Handle %p is not valid", __func__, clientState.surface.get());
continue;
@@ -174,53 +176,57 @@
RequestedLayerState* layer = getLayerFromId(layerId);
if (layer == nullptr) {
- LOG_ALWAYS_FATAL("%s Layer with handle %p (layerid=%d) not found", __func__,
- clientState.surface.get(), layerId);
+ LOG_ALWAYS_FATAL("%s Layer with layerid=%d not found", __func__, layerId);
continue;
}
if (!layer->handleAlive) {
- LOG_ALWAYS_FATAL("%s Layer's handle %p (layerid=%d) is not alive. Possible out of "
+ LOG_ALWAYS_FATAL("%s Layer's with layerid=%d) is not alive. Possible out of "
"order LayerLifecycleManager updates",
- __func__, clientState.surface.get(), layerId);
+ __func__, layerId);
continue;
}
+ if (transaction.flags & ISurfaceComposer::eAnimation) {
+ layer->changes |= RequestedLayerState::Changes::Animation;
+ }
+
uint32_t oldParentId = layer->parentId;
uint32_t oldRelativeParentId = layer->relativeParentId;
uint32_t oldTouchCropId = layer->touchCropId;
layer->merge(resolvedComposerState);
if (layer->what & layer_state_t::eBackgroundColorChanged) {
- if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColorAlpha != 0) {
- LayerCreationArgs backgroundLayerArgs{nullptr,
- nullptr,
- layer->name + "BackgroundColorLayer",
- ISurfaceComposerClient::eFXSurfaceEffect,
- {}};
+ if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColor.a != 0) {
+ LayerCreationArgs backgroundLayerArgs(layer->id,
+ /*internalLayer=*/true);
+ backgroundLayerArgs.parentId = layer->id;
+ backgroundLayerArgs.name = layer->name + "BackgroundColorLayer";
+ backgroundLayerArgs.flags = ISurfaceComposerClient::eFXSurfaceEffect;
std::vector<std::unique_ptr<RequestedLayerState>> newLayers;
newLayers.emplace_back(
std::make_unique<RequestedLayerState>(backgroundLayerArgs));
RequestedLayerState* backgroundLayer = newLayers.back().get();
+ backgroundLayer->bgColorLayer = true;
backgroundLayer->handleAlive = false;
backgroundLayer->parentId = layer->id;
backgroundLayer->z = std::numeric_limits<int32_t>::min();
- backgroundLayer->color.rgb = layer->color.rgb;
- backgroundLayer->color.a = layer->bgColorAlpha;
+ backgroundLayer->color = layer->bgColor;
backgroundLayer->dataspace = layer->bgColorDataspace;
-
layer->bgColorLayerId = backgroundLayer->id;
addLayers({std::move(newLayers)});
- } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID &&
- layer->bgColorAlpha == 0) {
+ } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID && layer->bgColor.a == 0) {
RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
- bgColorLayer->parentId = UNASSIGNED_LAYER_ID;
- onHandlesDestroyed({layer->bgColorLayerId});
+ layer->bgColorLayerId = UNASSIGNED_LAYER_ID;
+ bgColorLayer->parentId = unlinkLayer(bgColorLayer->parentId, bgColorLayer->id);
+ onHandlesDestroyed({bgColorLayer->id});
} else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) {
RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
- bgColorLayer->color.rgb = layer->color.rgb;
- bgColorLayer->color.a = layer->bgColorAlpha;
+ bgColorLayer->color = layer->bgColor;
bgColorLayer->dataspace = layer->bgColorDataspace;
+ bgColorLayer->what |= layer_state_t::eColorChanged |
+ layer_state_t::eDataspaceChanged | layer_state_t::eAlphaChanged;
+ bgColorLayer->changes |= RequestedLayerState::Changes::Content;
mGlobalChanges |= RequestedLayerState::Changes::Content;
}
}
@@ -256,8 +262,7 @@
listener->onLayerAdded(*layer);
}
}
- layer->what = 0;
- layer->changes.clear();
+ layer->clearChanges();
}
for (auto& destroyedLayer : mDestroyedLayers) {
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index 25d27ee..3d9a74c 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -40,7 +40,10 @@
// External state changes should be updated in the following order:
void addLayers(std::vector<std::unique_ptr<RequestedLayerState>>);
void applyTransactions(const std::vector<TransactionState>&);
- void onHandlesDestroyed(const std::vector<uint32_t>&);
+ // Ignore unknown handles when iteroping with legacy front end. In the old world, we
+ // would create child layers which are not necessary with the new front end. This means
+ // we will get notified for handle changes that don't exist in the new front end.
+ void onHandlesDestroyed(const std::vector<uint32_t>&, bool ignoreUnknownHandles = false);
// Detaches the layer from its relative parent to prevent a loop in the
// layer hierarchy. This overrides the RequestedLayerState and leaves
diff --git a/services/surfaceflinger/FrontEnd/LayerLog.h b/services/surfaceflinger/FrontEnd/LayerLog.h
index 47e1e30..4943483 100644
--- a/services/surfaceflinger/FrontEnd/LayerLog.h
+++ b/services/surfaceflinger/FrontEnd/LayerLog.h
@@ -25,3 +25,5 @@
#else
#define LLOGV(LAYER_ID, x, ...) ALOGV("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
#endif
+
+#define LLOGD(LAYER_ID, x, ...) ALOGD("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index dbb7fbf..8a45093 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -27,14 +27,22 @@
LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
const LayerHierarchy::TraversalPath& path)
: path(path) {
+ static uint32_t sUniqueSequenceId = 0;
+ // Provide a unique id for clones otherwise keeping using the sequence id.
+ // The seq id can still be useful for debugging if its available.
+ uniqueSequence = (path.isClone()) ? sUniqueSequenceId++ : state.id;
sequence = static_cast<int32_t>(state.id);
name = state.name;
textureName = state.textureName;
premultipliedAlpha = state.premultipliedAlpha;
inputInfo.name = state.name;
- inputInfo.id = static_cast<int32_t>(state.id);
+ inputInfo.id = static_cast<int32_t>(uniqueSequence);
inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid);
inputInfo.ownerPid = state.ownerPid;
+ changes = RequestedLayerState::Changes::Created;
+ mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror
+ ? path
+ : LayerHierarchy::TraversalPath::ROOT;
}
// As documented in libhardware header, formats in the range
@@ -139,7 +147,8 @@
// visible
std::stringstream reason;
if (sidebandStream != nullptr) reason << " sidebandStream";
- if (externalTexture != nullptr) reason << " buffer";
+ if (externalTexture != nullptr)
+ 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;
@@ -164,7 +173,8 @@
std::string LayerSnapshot::getDebugString() const {
std::stringstream debug;
debug << "Snapshot{" << path.toString() << name << " isVisible=" << isVisible << " {"
- << getIsVisibleReason() << "} changes=" << changes.string() << "}";
+ << getIsVisibleReason() << "} changes=" << changes.string()
+ << " layerStack=" << outputFilter.layerStack.id << "}";
return debug.str();
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 5d74203..6fb2f57 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -57,6 +57,12 @@
bool isHiddenByPolicyFromParent = false;
bool isHiddenByPolicyFromRelativeParent = false;
ftl::Flags<RequestedLayerState::Changes> changes;
+ // 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.
+ uint32_t uniqueSequence;
+ // Layer id used to create this snapshot. Multiple snapshots will have the same sequence if they
+ // generated from the same layer, for example when mirroring.
int32_t sequence;
std::string name;
uint32_t textureName;
@@ -84,6 +90,9 @@
scheduler::LayerInfo::FrameRate frameRate;
ui::Transform::RotationFlags fixedTransformHint;
bool handleSkipScreenshotFlag = false;
+ int32_t frameRateSelectionPriority;
+ LayerHierarchy::TraversalPath mirrorRootPath;
+ bool unreachable = true;
ChildState childState;
static bool isOpaqueFormat(PixelFormat format);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index c9aeb24..a16de1b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -274,7 +274,7 @@
// InputDispatcher, and obviously if they aren't visible they can't occlude
// anything.
const bool visibleForInput =
- (snapshot.inputInfo.token != nullptr) ? snapshot.canReceiveInput() : snapshot.isVisible;
+ snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
}
@@ -296,6 +296,26 @@
gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
+void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested,
+ const LayerSnapshotBuilder::Args& args) {
+ snapshot.metadata.clear();
+ for (const auto& [key, mandatory] : args.supportedLayerGenericMetadata) {
+ auto compatIter = args.genericLayerMetadataKeyMap.find(key);
+ if (compatIter == std::end(args.genericLayerMetadataKeyMap)) {
+ continue;
+ }
+ const uint32_t id = compatIter->second;
+ auto it = requested.metadata.mMap.find(id);
+ if (it == std::end(requested.metadata.mMap)) {
+ continue;
+ }
+
+ snapshot.metadata.emplace(key,
+ compositionengine::GenericLayerMetadataEntry{mandatory,
+ it->second});
+ }
+}
+
void clearChanges(LayerSnapshot& snapshot) {
snapshot.changes.clear();
snapshot.contentDirty = false;
@@ -308,6 +328,7 @@
LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() {
LayerSnapshot snapshot;
+ snapshot.path = LayerHierarchy::TraversalPath::ROOT;
snapshot.changes = ftl::Flags<RequestedLayerState::Changes>();
snapshot.isHiddenByPolicyFromParent = false;
snapshot.isHiddenByPolicyFromRelativeParent = false;
@@ -337,21 +358,17 @@
LayerSnapshotBuilder::LayerSnapshotBuilder() : mRootSnapshot(getRootSnapshot()) {}
LayerSnapshotBuilder::LayerSnapshotBuilder(Args args) : LayerSnapshotBuilder() {
- args.forceUpdate = true;
+ args.forceUpdate = ForceUpdateFlags::ALL;
updateSnapshots(args);
}
bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) {
- if (args.forceUpdate || args.displayChanges) {
+ if (args.forceUpdate != ForceUpdateFlags::NONE || args.displayChanges) {
// force update requested, or we have display changes, so skip the fast path
return false;
}
if (args.layerLifecycleManager.getGlobalChanges().get() == 0) {
- // there are no changes, so just clear the change flags from before.
- for (auto& snapshot : mSnapshots) {
- clearChanges(*snapshot);
- }
return true;
}
@@ -376,14 +393,12 @@
// Walk through the snapshots, clearing previous change flags and updating the snapshots
// if needed.
for (auto& snapshot : mSnapshots) {
- clearChanges(*snapshot);
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,
- /*newSnapshot=*/false);
+ updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root);
}
}
return true;
@@ -393,37 +408,45 @@
ATRACE_NAME("UpdateSnapshots");
if (args.parentCrop) {
mRootSnapshot.geomLayerBounds = *args.parentCrop;
- } else if (args.forceUpdate || args.displayChanges) {
+ } else if (args.forceUpdate == ForceUpdateFlags::ALL || args.displayChanges) {
mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays);
}
if (args.displayChanges) {
mRootSnapshot.changes = RequestedLayerState::Changes::AffectsChildren |
RequestedLayerState::Changes::Geometry;
}
+ if (args.forceUpdate == ForceUpdateFlags::HIERARCHY) {
+ mRootSnapshot.changes |=
+ RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility;
+ }
LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
- for (auto& [childHierarchy, variant] : args.root.mChildren) {
- LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
- childHierarchy->getLayer()->id,
- variant);
- updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot);
+ 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);
+ } else {
+ for (auto& [childHierarchy, variant] : args.root.mChildren) {
+ LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
+ childHierarchy->getLayer()->id,
+ variant);
+ updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot);
+ }
}
- sortSnapshotsByZ(args);
+ const bool hasUnreachableSnapshots = sortSnapshotsByZ(args);
clearChanges(mRootSnapshot);
// Destroy unreachable snapshots
- if (args.layerLifecycleManager.getDestroyedLayers().empty()) {
+ if (!hasUnreachableSnapshots) {
return;
}
- std::unordered_set<uint32_t> destroyedLayerIds;
- for (auto& destroyedLayer : args.layerLifecycleManager.getDestroyedLayers()) {
- destroyedLayerIds.emplace(destroyedLayer->id);
- }
auto it = mSnapshots.begin();
while (it < mSnapshots.end()) {
auto& traversalPath = it->get()->path;
- if (destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) {
+ if (!it->get()->unreachable) {
it++;
continue;
}
@@ -436,6 +459,10 @@
}
void LayerSnapshotBuilder::update(const Args& args) {
+ for (auto& snapshot : mSnapshots) {
+ clearChanges(*snapshot);
+ }
+
if (tryFastUpdate(args)) {
return;
}
@@ -449,8 +476,9 @@
LayerSnapshot* snapshot = getSnapshot(traversalPath);
const bool newSnapshot = snapshot == nullptr;
if (newSnapshot) {
- snapshot = createSnapshot(traversalPath, *layer);
+ snapshot = createSnapshot(traversalPath, *layer, parentSnapshot);
}
+ scheduler::LayerInfo::FrameRate oldFrameRate = snapshot->frameRate;
if (traversalPath.isRelative()) {
bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative;
updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args);
@@ -458,7 +486,7 @@
if (traversalPath.isAttached()) {
resetRelativeState(*snapshot);
}
- updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath, newSnapshot);
+ updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath);
}
for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
@@ -469,6 +497,10 @@
updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
updateChildState(*snapshot, childSnapshot, args);
}
+
+ if (oldFrameRate == snapshot->frameRate) {
+ snapshot->changes.clear(RequestedLayerState::Changes::FrameRate);
+ }
return *snapshot;
}
@@ -485,27 +517,34 @@
return it == mIdToSnapshot.end() ? nullptr : it->second;
}
-LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& id,
- const RequestedLayerState& layer) {
- mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, id));
+LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& path,
+ const RequestedLayerState& layer,
+ const LayerSnapshot& parentSnapshot) {
+ mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, path));
LayerSnapshot* snapshot = mSnapshots.back().get();
snapshot->globalZ = static_cast<size_t>(mSnapshots.size()) - 1;
- mIdToSnapshot[id] = snapshot;
+ if (path.isClone() && path.variant != LayerHierarchy::Variant::Mirror) {
+ snapshot->mirrorRootPath = parentSnapshot.mirrorRootPath;
+ }
+ mIdToSnapshot[path] = snapshot;
return snapshot;
}
-void LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) {
- if (!mResortSnapshots && !args.forceUpdate &&
+bool LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) {
+ if (!mResortSnapshots && args.forceUpdate == ForceUpdateFlags::NONE &&
!args.layerLifecycleManager.getGlobalChanges().any(
RequestedLayerState::Changes::Hierarchy |
RequestedLayerState::Changes::Visibility)) {
// We are not force updating and there are no hierarchy or visibility changes. Avoid sorting
// the snapshots.
- return;
+ return false;
}
-
mResortSnapshots = false;
+ for (auto& snapshot : mSnapshots) {
+ snapshot->unreachable = true;
+ }
+
size_t globalZ = 0;
args.root.traverseInZOrder(
[this, &globalZ](const LayerHierarchy&,
@@ -515,11 +554,7 @@
return false;
}
- if (snapshot->isHiddenByPolicy() &&
- !snapshot->changes.test(RequestedLayerState::Changes::Visibility)) {
- return false;
- }
-
+ snapshot->unreachable = false;
if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
updateVisibility(*snapshot, snapshot->getIsVisible());
size_t oldZ = snapshot->globalZ;
@@ -537,12 +572,17 @@
return true;
});
mNumInterestingSnapshots = (int)globalZ;
+ bool hasUnreachableSnapshots = false;
while (globalZ < mSnapshots.size()) {
mSnapshots[globalZ]->globalZ = globalZ;
/* mark unreachable snapshots as explicitly invisible */
updateVisibility(*mSnapshots[globalZ], false);
+ if (mSnapshots[globalZ]->unreachable) {
+ hasUnreachableSnapshots = true;
+ }
globalZ++;
}
+ return hasUnreachableSnapshots;
}
void LayerSnapshotBuilder::updateRelativeState(LayerSnapshot& snapshot,
@@ -569,7 +609,8 @@
if (snapshot.childState.hasValidFrameRate) {
return;
}
- if (args.forceUpdate || childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) {
+ 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.
@@ -612,35 +653,32 @@
void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
- const LayerHierarchy::TraversalPath& path,
- bool newSnapshot) {
+ const LayerHierarchy::TraversalPath& path) {
// Always update flags and visibility
ftl::Flags<RequestedLayerState::Changes> parentChanges = parentSnapshot.changes &
(RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
- RequestedLayerState::Changes::AffectsChildren);
- snapshot.changes = parentChanges | requested.changes;
+ RequestedLayerState::Changes::AffectsChildren |
+ RequestedLayerState::Changes::FrameRate);
+ snapshot.changes |= parentChanges | requested.changes;
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 =
- (snapshot.contentDirty || requested.autoRefresh) && (requested.externalTexture);
- // TODO(b/238781169) how is this used? ag/15523870
- snapshot.sidebandStreamHasFrame = false;
+ snapshot.hasReadyFrame = requested.hasReadyFrame();
+ snapshot.sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage,
snapshot.surfaceDamage);
-
- const bool forceUpdate = newSnapshot || args.forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Visibility |
- RequestedLayerState::Changes::Created);
- snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID
- ? parentSnapshot.outputFilter.layerStack
- : requested.layerStack;
+ snapshot.outputFilter.layerStack = parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
+ ? requested.layerStack
+ : parentSnapshot.outputFilter.layerStack;
uint32_t displayRotationFlags =
getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack);
+ const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
+ 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) {
@@ -673,7 +711,8 @@
snapshot.desiredSdrHdrRatio = requested.desiredSdrHdrRatio;
}
- if (snapshot.isHiddenByPolicyFromParent && !newSnapshot) {
+ if (snapshot.isHiddenByPolicyFromParent &&
+ !snapshot.changes.test(RequestedLayerState::Changes::Created)) {
if (forceUpdate ||
snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
RequestedLayerState::Changes::Geometry |
@@ -690,9 +729,6 @@
snapshot.isSecure =
parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure);
snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
- snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID
- ? parentSnapshot.outputFilter.layerStack
- : requested.layerStack;
snapshot.outputFilter.toInternalDisplay = parentSnapshot.outputFilter.toInternalDisplay ||
(requested.flags & layer_state_t::eLayerSkipScreenshot);
snapshot.stretchEffect = (requested.stretchEffect.hasEffect())
@@ -708,11 +744,6 @@
snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
? requested.gameMode
: parentSnapshot.gameMode;
- snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
- (requested.requestedFrameRate.type ==
- scheduler::LayerInfo::FrameRateCompatibility::NoVote))
- ? requested.requestedFrameRate
- : parentSnapshot.frameRate;
snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
? requested.fixedTransformHint
: parentSnapshot.fixedTransformHint;
@@ -722,11 +753,27 @@
(requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
}
+ if (forceUpdate ||
+ snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
+ RequestedLayerState::Changes::Hierarchy)) {
+ snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
+ (requested.requestedFrameRate.type ==
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote))
+ ? requested.requestedFrameRate
+ : parentSnapshot.frameRate;
+ }
+
+ if (forceUpdate || requested.what & layer_state_t::eMetadataChanged) {
+ updateMetadata(snapshot, requested, args);
+ }
+
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)) {
@@ -764,18 +811,16 @@
}
snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
- snapshot.isOpaque = snapshot.isContentOpaque() && !snapshot.roundedCorner.hasRoundedCorners() &&
+ snapshot.contentOpaque = snapshot.isContentOpaque();
+ snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() &&
snapshot.color.a == 1.f;
snapshot.blendMode = getBlendMode(snapshot, requested);
- // TODO(b/238781169) pass this from flinger
- // snapshot.fps;
- // snapshot.metadata;
LLOGV(snapshot.sequence,
- "%supdated [%d]%s changes parent:%s global:%s local:%s requested:%s %s from parent %s",
- args.forceUpdate ? "Force " : "", requested.id, requested.name.c_str(),
- parentSnapshot.changes.string().c_str(), snapshot.changes.string().c_str(),
- requested.changes.string().c_str(), std::to_string(requested.what).c_str(),
- snapshot.getDebugString().c_str(), parentSnapshot.getDebugString().c_str());
+ "%supdated %s changes:%s parent:%s requested:%s requested:%s from parent %s",
+ args.forceUpdate == ForceUpdateFlags::ALL ? "Force " : "",
+ snapshot.getDebugString().c_str(), snapshot.changes.string().c_str(),
+ parentSnapshot.changes.string().c_str(), requested.changes.string().c_str(),
+ std::to_string(requested.what).c_str(), parentSnapshot.getDebugString().c_str());
}
void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot,
@@ -910,9 +955,14 @@
snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
} else {
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.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
+ snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
+ snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
if (!needsInputInfo(snapshot, requested)) {
return;
}
@@ -935,7 +985,9 @@
}
snapshot.inputInfo.alpha = snapshot.color.a;
- snapshot.inputInfo.touchOcclusionMode = parentSnapshot.inputInfo.touchOcclusionMode;
+ snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
+ ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
+ : parentSnapshot.inputInfo.touchOcclusionMode;
if (requested.dropInputMode == gui::DropInputMode::ALL ||
parentSnapshot.dropInputMode == gui::DropInputMode::ALL) {
snapshot.dropInputMode = gui::DropInputMode::ALL;
@@ -974,7 +1026,7 @@
// touches from going outside the cloned area.
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE;
- auto clonedRootSnapshot = getSnapshot(path.getMirrorRoot());
+ auto clonedRootSnapshot = getSnapshot(snapshot.mirrorRootPath);
if (clonedRootSnapshot) {
const Rect rect =
displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds});
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 0902ab8..3997a0a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -35,10 +35,15 @@
// snapshots when there are only buffer updates.
class LayerSnapshotBuilder {
public:
+ enum class ForceUpdateFlags {
+ NONE,
+ ALL,
+ HIERARCHY,
+ };
struct Args {
LayerHierarchy root;
const LayerLifecycleManager& layerLifecycleManager;
- bool forceUpdate = false;
+ ForceUpdateFlags forceUpdate = ForceUpdateFlags::NONE;
bool includeMetadata = false;
const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays;
// Set to true if there were display changes since last update.
@@ -48,6 +53,8 @@
bool forceFullDamage = false;
std::optional<FloatRect> parentCrop = std::nullopt;
std::unordered_set<uint32_t> excludeLayerIds;
+ const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata;
+ const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap;
};
LayerSnapshotBuilder();
@@ -92,8 +99,7 @@
LayerHierarchy::TraversalPath& traversalPath,
const LayerSnapshot& parentSnapshot);
void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
- const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&,
- bool newSnapshot);
+ const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&);
static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
bool parentIsRelative, const Args& args);
static void resetRelativeState(LayerSnapshot& snapshot);
@@ -106,9 +112,11 @@
void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
const Args& args);
- void sortSnapshotsByZ(const Args& args);
+ // Return true if there are unreachable snapshots
+ bool sortSnapshotsByZ(const Args& args);
LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id,
- const RequestedLayerState& layer);
+ const RequestedLayerState& layer,
+ const LayerSnapshot& parentSnapshot);
void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
const Args& args);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 2834084..a5fdaf4 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -24,7 +24,6 @@
#include "Layer.h"
#include "LayerCreationArgs.h"
-#include "LayerHandle.h"
#include "LayerLog.h"
#include "RequestedLayerState.h"
@@ -33,14 +32,6 @@
using namespace ftl::flag_operators;
namespace {
-uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) {
- if (!surfaceControl) {
- return UNASSIGNED_LAYER_ID;
- }
-
- return LayerHandle::getLayerId(surfaceControl->getHandle());
-}
-
std::string layerIdToString(uint32_t layerId) {
return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
}
@@ -64,17 +55,17 @@
layerCreationFlags(args.flags),
textureName(args.textureName),
ownerUid(args.ownerUid),
- ownerPid(args.ownerPid) {
+ ownerPid(args.ownerPid),
+ parentId(args.parentId),
+ layerIdToMirror(args.layerIdToMirror) {
layerId = static_cast<int32_t>(args.sequence);
changes |= RequestedLayerState::Changes::Created;
metadata.merge(args.metadata);
changes |= RequestedLayerState::Changes::Metadata;
handleAlive = true;
- parentId = LayerHandle::getLayerId(args.parentHandle.promote());
- if (args.parentHandle != nullptr) {
+ if (parentId != UNASSIGNED_LAYER_ID) {
canBeRoot = false;
}
- layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
if (layerIdToMirror != UNASSIGNED_LAYER_ID) {
changes |= RequestedLayerState::Changes::Mirror;
} else if (args.layerStackToMirror != ui::INVALID_LAYER_STACK) {
@@ -137,6 +128,7 @@
dataspace = ui::Dataspace::V0_SRGB;
gameMode = gui::GameMode::Unsupported;
requestedFrameRate = {};
+ cachingHint = gui::CachingHint::Enabled;
}
void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
@@ -164,8 +156,13 @@
if (hadBufferOrSideStream != hasBufferOrSideStream) {
changes |= RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::VisibleRegion |
- RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input |
- RequestedLayerState::Changes::Buffer;
+ RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
+ }
+ if (clientState.what & layer_state_t::eBufferChanged) {
+ changes |= RequestedLayerState::Changes::Buffer;
+ }
+ if (clientState.what & layer_state_t::eSidebandStreamChanged) {
+ changes |= RequestedLayerState::Changes::SidebandStream;
}
}
if (what & (layer_state_t::eAlphaChanged)) {
@@ -196,12 +193,14 @@
static const mat4 identityMatrix = mat4();
hasColorTransform = colorTransform != identityMatrix;
}
- if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) {
+ if (clientState.what &
+ (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
+ layer_state_t::eLayerStackChanged)) {
changes |= RequestedLayerState::Changes::Z;
}
if (clientState.what & layer_state_t::eReparent) {
changes |= RequestedLayerState::Changes::Parent;
- parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild);
+ parentId = resolvedComposerState.parentId;
parentSurfaceControlForChild = nullptr;
// Once a layer has be reparented, it cannot be placed at the root. It sounds odd
// but thats the existing logic and until we make this behavior more explicit, we need
@@ -210,7 +209,7 @@
}
if (clientState.what & layer_state_t::eRelativeLayerChanged) {
changes |= RequestedLayerState::Changes::RelativeParent;
- relativeParentId = getLayerIdFromSurfaceControl(clientState.relativeLayerSurfaceControl);
+ relativeParentId = resolvedComposerState.relativeParentId;
isRelativeOf = true;
relativeLayerSurfaceControl = nullptr;
}
@@ -227,10 +226,8 @@
changes |= RequestedLayerState::Changes::RelativeParent;
}
if (clientState.what & layer_state_t::eInputInfoChanged) {
- wp<IBinder>& touchableRegionCropHandle =
- windowInfoHandle->editInfo()->touchableRegionCropHandle;
- touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote());
- touchableRegionCropHandle.clear();
+ touchCropId = resolvedComposerState.touchCropId;
+ windowInfoHandle->editInfo()->touchableRegionCropHandle.clear();
}
if (clientState.what & layer_state_t::eStretchChanged) {
stretchEffect.sanitize();
@@ -452,4 +449,22 @@
return backgroundBlurRadius > 0 || blurRegions.size() > 0;
}
+bool RequestedLayerState::hasFrameUpdate() const {
+ return what & layer_state_t::CONTENT_DIRTY &&
+ (externalTexture || bgColorLayerId != UNASSIGNED_LAYER_ID);
+}
+
+bool RequestedLayerState::hasReadyFrame() const {
+ return hasFrameUpdate() || changes.test(Changes::SidebandStream) || autoRefresh;
+}
+
+bool RequestedLayerState::hasSidebandStreamFrame() const {
+ return hasFrameUpdate() && sidebandStream.get();
+}
+
+void RequestedLayerState::clearChanges() {
+ what = 0;
+ changes.clear();
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 6840b25..216e95f 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -52,10 +52,14 @@
FrameRate = 1u << 13,
VisibleRegion = 1u << 14,
Buffer = 1u << 15,
+ SidebandStream = 1u << 16,
+ Animation = 1u << 17,
};
static Rect reduce(const Rect& win, const Region& exclude);
RequestedLayerState(const LayerCreationArgs&);
void merge(const ResolvedComposerState&);
+ void clearChanges();
+
// Currently we only care about the primary display
ui::Transform getTransform(uint32_t displayRotationFlags) const;
ui::Size getUnrotatedBufferSize(uint32_t displayRotationFlags) const;
@@ -72,6 +76,9 @@
bool hasValidRelativeParent() const;
bool hasInputInfo() const;
bool hasBlur() const;
+ bool hasFrameUpdate() const;
+ bool hasReadyFrame() const;
+ bool hasSidebandStreamFrame() 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
@@ -98,18 +105,19 @@
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
gui::GameMode gameMode;
scheduler::LayerInfo::FrameRate requestedFrameRate;
- ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+ uint32_t parentId = UNASSIGNED_LAYER_ID;
+ uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
+ ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+ uint32_t touchCropId = UNASSIGNED_LAYER_ID;
+ uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
// book keeping states
bool handleAlive = true;
bool isRelativeOf = false;
- uint32_t parentId = UNASSIGNED_LAYER_ID;
- uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
std::vector<uint32_t> mirrorIds{};
- uint32_t touchCropId = UNASSIGNED_LAYER_ID;
- uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
ftl::Flags<RequestedLayerState::Changes> changes;
+ bool bgColorLayer = false;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h
new file mode 100644
index 0000000..e1449b6
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/Update.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <gui/DisplayInfo.h>
+
+#include "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "TransactionState.h"
+
+namespace android {
+struct LayerCreatedState {
+ LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot)
+ : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
+ wp<Layer> layer;
+ // Indicates the initial parent of the created layer, only used for creating layer in
+ // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
+ wp<Layer> initialParent;
+ // Indicates whether the layer getting created should be added at root if there's no parent
+ // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
+ // be added offscreen.
+ bool addToRoot;
+};
+} // namespace android
+
+namespace android::surfaceflinger::frontend {
+
+// Atomic set of changes affecting layer state. These changes are queued in binder threads and
+// applied every vsync.
+struct Update {
+ std::vector<TransactionState> transactions;
+ std::vector<LayerCreatedState> layerCreatedStates;
+ std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
+ std::vector<LayerCreationArgs> layerCreationArgs;
+ std::vector<uint32_t> destroyedHandles;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
index c88554e..9eefbe4 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.cpp
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -40,7 +40,8 @@
for (const auto& listener : toInvoke) {
ATRACE_NAME("invoking onHdrLayerInfoChanged");
- listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags);
+ listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags,
+ info.maxDesiredHdrSdrRatio);
}
}
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 9b70c16..bf7c775 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -43,27 +43,18 @@
// With peak display brightnesses exceeding 1,000 nits currently, HLG's request could
// actually be satisfied in some ambient conditions such that limiting that max for that
// content in theory makes sense
- float maxDesiredSdrHdrRatio = 0.f;
+ float maxDesiredHdrSdrRatio = 0.f;
bool operator==(const HdrLayerInfo& other) const {
return numberOfHdrLayers == other.numberOfHdrLayers && maxW == other.maxW &&
- maxH == other.maxH && flags == other.flags;
+ maxH == other.maxH && flags == other.flags &&
+ maxDesiredHdrSdrRatio == other.maxDesiredHdrSdrRatio;
}
bool operator!=(const HdrLayerInfo& other) const { return !(*this == other); }
void mergeDesiredRatio(float update) {
- if (maxDesiredSdrHdrRatio == 0.f) {
- // If nothing is set, take the incoming value
- maxDesiredSdrHdrRatio = update;
- } else if (update == 1.f) {
- // If the request is to "go to max", then take it regardless
- maxDesiredSdrHdrRatio = 1.f;
- } else if (maxDesiredSdrHdrRatio != 1.f) {
- // If we're not currently asked to "go to max", then take the max
- // of the incoming requests
- maxDesiredSdrHdrRatio = std::max(maxDesiredSdrHdrRatio, update);
- }
+ maxDesiredHdrSdrRatio = std::max(maxDesiredHdrSdrRatio, update);
}
};
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 084d9b9..9521371 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -196,7 +196,8 @@
mDrawingState.color.b = -1.0_hf;
}
- mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod().ns());
+ mFrameTracker.setDisplayRefreshPeriod(
+ args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
mOwnerUid = args.ownerUid;
mOwnerPid = args.ownerPid;
@@ -566,6 +567,7 @@
: Hwc2::IComposerClient::BlendMode::COVERAGE;
}
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
snapshot->outputFilter = getOutputFilter();
snapshot->isVisible = isVisible();
@@ -592,6 +594,7 @@
const auto& drawingState{getDrawingState()};
auto* snapshot = editLayerSnapshot();
+ // Please keep in sync with LayerSnapshotBuilder
snapshot->geomBufferSize = getBufferSize(drawingState);
snapshot->geomContentCrop = getBufferCrop();
snapshot->geomCrop = getCrop(drawingState);
@@ -624,6 +627,7 @@
void Layer::preparePerFrameCompositionState() {
const auto& drawingState{getDrawingState()};
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
snapshot->forceClientComposition = false;
@@ -637,6 +641,7 @@
snapshot->dimmingEnabled = isDimmingEnabled();
snapshot->currentSdrHdrRatio = getCurrentSdrHdrRatio();
snapshot->desiredSdrHdrRatio = getDesiredSdrHdrRatio();
+ snapshot->cachingHint = getCachingHint();
const bool usesRoundedCorners = hasRoundedCorners();
@@ -666,8 +671,9 @@
}
void Layer::preparePerFrameBufferCompositionState() {
- // Sideband layers
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
+ // Sideband layers
if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) {
snapshot->compositionType =
aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
@@ -675,6 +681,9 @@
} else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
snapshot->compositionType =
aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+ } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) {
+ snapshot->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR;
} else {
// Normal buffer layers
snapshot->hdrMetadata = mBufferInfo.mHdrMetadata;
@@ -690,6 +699,7 @@
}
void Layer::preparePerFrameEffectsCompositionState() {
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
snapshot->color = getColor();
snapshot->compositionType =
@@ -698,6 +708,7 @@
void Layer::prepareCursorCompositionState() {
const State& drawingState{getDrawingState()};
+ // Please keep in sync with LayerSnapshotBuilder
auto* snapshot = editLayerSnapshot();
// Apply the layer's transform, followed by the display's global transform
@@ -783,7 +794,7 @@
// transaction
// ----------------------------------------------------------------------------
-uint32_t Layer::doTransaction(uint32_t flags, nsecs_t latchTime) {
+uint32_t Layer::doTransaction(uint32_t flags) {
ATRACE_CALL();
// TODO: This is unfortunate.
@@ -811,12 +822,12 @@
mFlinger->mUpdateInputInfo = true;
}
- commitTransaction(mDrawingState, latchTime);
+ commitTransaction(mDrawingState);
return flags;
}
-void Layer::commitTransaction(State&, nsecs_t currentLatchTime) {
+void Layer::commitTransaction(State&) {
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -828,7 +839,6 @@
}
}
mDrawingState.bufferlessSurfaceFramesTX.clear();
- mLastLatchTime = currentLatchTime;
}
uint32_t Layer::clearTransactionFlags(uint32_t mask) {
@@ -1247,7 +1257,7 @@
return parentFrameRate;
}();
- *transactionNeeded |= setFrameRateForLayerTree(frameRate);
+ *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate);
// The frame rate is propagated to the children
bool childrenHaveFrameRate = false;
@@ -1261,7 +1271,7 @@
if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
childrenHaveFrameRate) {
*transactionNeeded |=
- setFrameRateForLayerTree(FrameRate(Fps(), FrameRateCompatibility::NoVote));
+ setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote));
}
// We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
@@ -1374,7 +1384,7 @@
surfaceFrame->setAcquireFenceTime(acquireFenceTime);
surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
- mLastLatchTime = currentLatchTime;
+ updateLastLatchTime(currentLatchTime);
}
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
@@ -1413,7 +1423,7 @@
return surfaceFrame;
}
-bool Layer::setFrameRateForLayerTree(FrameRate frameRate) {
+bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
}
@@ -1426,9 +1436,21 @@
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
- using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
- mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate);
+ mFlinger->mScheduler
+ ->recordLayerHistory(sequence, getLayerProps(), systemTime(),
+ scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
+ return true;
+}
+bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps) {
+ if (mDrawingState.frameRateForLayerTree == frameRate) {
+ return false;
+ }
+
+ mDrawingState.frameRateForLayerTree = frameRate;
+ mFlinger->mScheduler
+ ->recordLayerHistory(sequence, layerProps, systemTime(),
+ scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
return true;
}
@@ -2090,15 +2112,7 @@
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
- ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
-
- // Only populate for the primary display.
- if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
- const auto compositionType = getCompositionType(*display);
- layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
- LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
- [&]() { return layerProto->mutable_visible_region(); });
- }
+ writeCompositionStateToProto(layerProto);
}
for (const sp<Layer>& layer : mDrawingChildren) {
@@ -2108,6 +2122,18 @@
return layerProto;
}
+void Layer::writeCompositionStateToProto(LayerProto* layerProto) {
+ ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
+
+ // Only populate for the primary display.
+ if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
+ const auto compositionType = getCompositionType(*display);
+ layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
+ LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
+ [&]() { return layerProto->mutable_visible_region(); });
+ }
+}
+
void Layer::writeToProtoDrawingState(LayerProto* layerInfo) {
const ui::Transform transform = getTransform();
auto buffer = getExternalTexture();
@@ -2505,7 +2531,20 @@
compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
const DisplayDevice* display) const {
if (!display) return nullptr;
- return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
+ if (!mFlinger->mLayerLifecycleManagerEnabled) {
+ return display->getCompositionDisplay()->getOutputLayerForLayer(
+ getCompositionEngineLayerFE());
+ }
+ sp<LayerFE> layerFE;
+ frontend::LayerHierarchy::TraversalPath path{.id = static_cast<uint32_t>(sequence)};
+ 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 {
@@ -3014,6 +3053,10 @@
mLastClientCompositionFence);
mLastClientCompositionFence = nullptr;
}
+ } else {
+ // if we are latching a buffer for the first time then clear the mLastLatchTime since
+ // we don't want to incorrectly classify a frame if we miss the desired present time.
+ updateLastLatchTime(0);
}
mDrawingState.producerId = bufferData.producerId;
@@ -3033,7 +3076,7 @@
} else {
mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
}
-
+ mDrawingState.latchedVsyncId = info.vsyncId;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -3043,18 +3086,9 @@
mDrawingState.desiredPresentTime = desiredPresentTime;
mDrawingState.isAutoTimestamp = isAutoTimestamp;
- const nsecs_t presentTime = [&] {
- if (!isAutoTimestamp) return desiredPresentTime;
-
- const auto prediction =
- mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
- if (prediction.has_value()) return prediction->presentTime;
-
- return static_cast<nsecs_t>(0);
- }();
-
- using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
+ if (mFlinger->mLegacyFrontEndEnabled) {
+ recordLayerHistoryBufferUpdate(getLayerProps());
+ }
setFrameTimelineVsyncForBufferTransaction(info, postTime);
@@ -3071,6 +3105,32 @@
return true;
}
+void Layer::setDesiredPresentTime(nsecs_t desiredPresentTime, bool isAutoTimestamp) {
+ mDrawingState.desiredPresentTime = desiredPresentTime;
+ mDrawingState.isAutoTimestamp = isAutoTimestamp;
+}
+
+void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+ const nsecs_t presentTime = [&] {
+ if (!mDrawingState.isAutoTimestamp) return mDrawingState.desiredPresentTime;
+
+ const auto prediction = mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
+ mDrawingState.latchedVsyncId);
+ if (prediction.has_value()) return prediction->presentTime;
+
+ return static_cast<nsecs_t>(0);
+ }();
+ mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+ scheduler::LayerHistory::LayerUpdateType::Buffer);
+}
+
+void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps) {
+ const nsecs_t presentTime =
+ mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime;
+ mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+ scheduler::LayerHistory::LayerUpdateType::AnimationTX);
+}
+
bool Layer::setDataspace(ui::Dataspace dataspace) {
mDrawingState.dataspaceRequested = true;
if (mDrawingState.dataspace == dataspace) return false;
@@ -3091,6 +3151,14 @@
return true;
}
+bool Layer::setCachingHint(gui::CachingHint cachingHint) {
+ if (mDrawingState.cachingHint == cachingHint) return false;
+ mDrawingState.cachingHint = cachingHint;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
if (mDrawingState.hdrMetadata == hdrMetadata) return false;
mDrawingState.hdrMetadata = hdrMetadata;
@@ -3100,6 +3168,7 @@
}
bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+ if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
mDrawingState.surfaceDamageRegion = surfaceDamage;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -3260,11 +3329,11 @@
(c.buffer != nullptr || c.bgColorLayer != nullptr);
}
-void Layer::updateTexImage(nsecs_t latchTime) {
+void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) {
const State& s(getDrawingState());
if (!s.buffer) {
- if (s.bgColorLayer) {
+ if (bgColorOnly) {
for (auto& handle : mDrawingState.callbackHandles) {
handle->latchTime = latchTime;
}
@@ -3461,7 +3530,7 @@
}
if (s.what & layer_state_t::eBackgroundColorChanged) {
- if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) {
+ if (mDrawingState.bgColorLayer || s.bgColor.a != 0) {
ALOGV("%s: false [eBackgroundColorChanged changed]", __func__);
return false;
}
@@ -3785,6 +3854,11 @@
}
bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+ const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr;
+ return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly);
+}
+
+bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) {
ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
getDrawingState().frameNumber);
@@ -3801,8 +3875,7 @@
mFlinger->onLayerUpdate();
return false;
}
-
- updateTexImage(latchTime);
+ updateTexImage(latchTime, bgColorOnly);
if (mDrawingState.buffer == nullptr) {
return false;
}
@@ -4006,7 +4079,6 @@
snapshot->bufferSize = getBufferSize(mDrawingState);
snapshot->externalTexture = mBufferInfo.mBuffer;
snapshot->hasReadyFrame = hasReadyFrame();
- snapshot->isInternalDisplayOverlay = isInternalDisplayOverlay();
preparePerFrameCompositionState();
}
@@ -4049,7 +4121,7 @@
}
}
-void Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener) {
bool hadTrustedPresentationListener = hasTrustedPresentationListener();
mTrustedPresentationListener = listener;
@@ -4060,6 +4132,20 @@
} else if (hadTrustedPresentationListener && !haveTrustedPresentationListener) {
mFlinger->mNumTrustedPresentationListeners--;
}
+
+ // Reset trusted presentation states to ensure we start the time again.
+ mEnteredTrustedPresentationStateTime = -1;
+ mLastReportedTrustedPresentationState = false;
+ mLastComputedTrustedPresentationState = false;
+
+ // If there's a new trusted presentation listener, the code needs to go through the composite
+ // path to ensure it recomutes the current state and invokes the TrustedPresentationListener if
+ // we're already in the requested state.
+ return haveTrustedPresentationListener;
+}
+
+void Layer::updateLastLatchTime(nsecs_t latchTime) {
+ mLastLatchTime = latchTime;
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 11cb8b2..f7e1969 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -227,6 +227,8 @@
bool dimmingEnabled = true;
float currentSdrHdrRatio = 1.f;
float desiredSdrHdrRatio = 1.f;
+ gui::CachingHint cachingHint = gui::CachingHint::Enabled;
+ int64_t latchedVsyncId = 0;
};
explicit Layer(const LayerCreationArgs& args);
@@ -296,6 +298,7 @@
virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }
float getDesiredSdrHdrRatio() const { return getDrawingState().desiredSdrHdrRatio; }
float getCurrentSdrHdrRatio() const { return getDrawingState().currentSdrHdrRatio; }
+ gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; }
bool setTransform(uint32_t /*transform*/);
bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
@@ -303,8 +306,10 @@
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
+ void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
+ bool setCachingHint(gui::CachingHint cachingHint);
bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
bool setApi(int32_t /*api*/);
@@ -344,6 +349,7 @@
void useSurfaceDamage();
void useEmptyDamage();
Region getVisibleRegion(const DisplayDevice*) const;
+ void updateLastLatchTime(nsecs_t latchtime);
/*
* isOpaque - true if this surface is opaque
@@ -424,6 +430,9 @@
*/
bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/);
+ bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+ bool bgColorOnly);
+
/*
* Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
* This is used if the buffer is just latched and releases to free up the buffer
@@ -601,6 +610,7 @@
bool isRemovedFromCurrentState() const;
LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags);
+ void writeCompositionStateToProto(LayerProto* layerProto);
// Write states that are modified by the main thread. This includes drawing
// state as well as buffer data. This should be called in the main or tracing
@@ -620,7 +630,7 @@
* doTransaction - process the transaction. This is a good place to figure
* out which attributes of the surface have changed.
*/
- virtual uint32_t doTransaction(uint32_t transactionFlags, nsecs_t currentLatchTime);
+ virtual uint32_t doTransaction(uint32_t transactionFlags);
/*
* Remove relative z for the layer if its relative parent is not part of the
@@ -754,7 +764,7 @@
std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
- void setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+ bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener);
// Creates a new handle each time, so we only expect
@@ -848,6 +858,20 @@
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence);
+ bool setFrameRateForLayerTreeLegacy(FrameRate);
+ bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&);
+ void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&);
+ void recordLayerHistoryAnimationTx(const scheduler::LayerProps&);
+ auto getLayerProps() const {
+ return scheduler::LayerProps{
+ .visible = isVisible(),
+ .bounds = getBounds(),
+ .transform = getTransform(),
+ .setFrameRateVote = getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = getFrameRateSelectionPriority(),
+ };
+ };
+ bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
protected:
// For unit tests
@@ -862,7 +886,7 @@
void preparePerFrameCompositionState();
void preparePerFrameBufferCompositionState();
void preparePerFrameEffectsCompositionState();
- virtual void commitTransaction(State& stateToCommit, nsecs_t currentLatchTime = 0);
+ virtual void commitTransaction(State& stateToCommit);
void gatherBufferInfo();
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
@@ -1016,7 +1040,6 @@
void updateTreeHasFrameRateVote();
bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded);
- bool setFrameRateForLayerTree(FrameRate);
void setZOrderRelativeOf(const wp<Layer>& relativeOf);
bool isTrustedOverlay() const;
gui::DropInputMode getDropInputMode() const;
@@ -1044,7 +1067,7 @@
bool hasFrameUpdate() const;
- void updateTexImage(nsecs_t latchTime);
+ void updateTexImage(nsecs_t latchTime, bool bgColorOnly = false);
// Crop that applies to the buffer
Rect computeBufferCrop(const State& s);
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 01da019..c23bd31 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/gui/CachingHint.h>
#include <gui/LayerMetadata.h>
#include "FrontEnd/LayerSnapshot.h"
#include "compositionengine/LayerFE.h"
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 0506c47..5c91b91 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -247,6 +247,169 @@
outRegion.right = proto.right();
outRegion.bottom = proto.bottom();
}
+
+void LayerProtoHelper::writeHierarchyToProto(
+ LayersProto& outLayersProto, const frontend::LayerHierarchy& root,
+ const frontend::LayerSnapshotBuilder& snapshotBuilder,
+ const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers, uint32_t traceFlags) {
+ using Variant = frontend::LayerHierarchy::Variant;
+ frontend::LayerSnapshot defaultSnapshot;
+
+ LayerProto* layerProto = outLayersProto.add_layers();
+ const frontend::RequestedLayerState& layer = *root.getLayer();
+ frontend::LayerSnapshot* snapshot = snapshotBuilder.getSnapshot(layer.id);
+
+ if (!snapshot) {
+ defaultSnapshot.uniqueSequence = layer.id;
+ snapshot = &defaultSnapshot;
+ }
+ writeSnapshotToProto(layerProto, layer, *snapshot, traceFlags);
+ for (const auto& [child, variant] : root.mChildren) {
+ if (variant == Variant::Attached || variant == Variant::Detached) {
+ layerProto->add_children(child->getLayer()->id);
+ } else if (variant == Variant::Relative) {
+ layerProto->add_relatives(child->getLayer()->id);
+ }
+ }
+
+ auto parent = root.getParent();
+ if (parent && parent->getLayer()) {
+ layerProto->set_parent(parent->getLayer()->id);
+ } else {
+ layerProto->set_parent(-1);
+ }
+
+ auto relativeParent = root.getRelativeParent();
+ if (relativeParent && relativeParent->getLayer()) {
+ layerProto->set_z_order_relative_of(relativeParent->getLayer()->id);
+ } else {
+ layerProto->set_z_order_relative_of(-1);
+ }
+
+ if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
+ auto it = legacyLayers.find(layer.id);
+ if (it != legacyLayers.end()) {
+ it->second->writeCompositionStateToProto(layerProto);
+ }
+ }
+
+ for (const auto& [child, variant] : root.mChildren) {
+ // avoid visiting relative layers twice
+ if (variant == Variant::Detached) {
+ continue;
+ }
+ writeHierarchyToProto(outLayersProto, *child, snapshotBuilder, legacyLayers, traceFlags);
+ }
+}
+
+void LayerProtoHelper::writeSnapshotToProto(LayerProto* layerInfo,
+ const frontend::RequestedLayerState& requestedState,
+ const frontend::LayerSnapshot& snapshot,
+ uint32_t traceFlags) {
+ const ui::Transform transform = snapshot.geomLayerTransform;
+ auto buffer = requestedState.externalTexture;
+ if (buffer != nullptr) {
+ LayerProtoHelper::writeToProto(*buffer,
+ [&]() { return layerInfo->mutable_active_buffer(); });
+ LayerProtoHelper::writeToProtoDeprecated(ui::Transform(requestedState.bufferTransform),
+ layerInfo->mutable_buffer_transform());
+ }
+ layerInfo->set_invalidate(snapshot.contentDirty);
+ layerInfo->set_is_protected(snapshot.hasProtectedContent);
+ layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(snapshot.dataspace)));
+ layerInfo->set_curr_frame(requestedState.bufferData->frameNumber);
+ layerInfo->set_requested_corner_radius(requestedState.cornerRadius);
+ layerInfo->set_corner_radius(
+ (snapshot.roundedCorner.radius.x + snapshot.roundedCorner.radius.y) / 2.0);
+ layerInfo->set_background_blur_radius(snapshot.backgroundBlurRadius);
+ layerInfo->set_is_trusted_overlay(snapshot.isTrustedOverlay);
+ LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
+ LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+ [&]() { return layerInfo->mutable_position(); });
+ LayerProtoHelper::writeToProto(snapshot.geomLayerBounds,
+ [&]() { return layerInfo->mutable_bounds(); });
+ LayerProtoHelper::writeToProto(snapshot.surfaceDamage,
+ [&]() { return layerInfo->mutable_damage_region(); });
+
+ if (requestedState.hasColorTransform) {
+ LayerProtoHelper::writeToProto(snapshot.colorTransform,
+ layerInfo->mutable_color_transform());
+ }
+
+ LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(),
+ [&]() { return layerInfo->mutable_source_bounds(); });
+ LayerProtoHelper::writeToProto(snapshot.transformedBounds,
+ [&]() { return layerInfo->mutable_screen_bounds(); });
+ LayerProtoHelper::writeToProto(snapshot.roundedCorner.cropRect,
+ [&]() { return layerInfo->mutable_corner_radius_crop(); });
+ layerInfo->set_shadow_radius(snapshot.shadowRadius);
+
+ layerInfo->set_id(snapshot.uniqueSequence);
+ layerInfo->set_name(requestedState.name);
+ layerInfo->set_type("Layer");
+
+ LayerProtoHelper::writeToProto(requestedState.transparentRegion,
+ [&]() { return layerInfo->mutable_transparent_region(); });
+
+ layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id);
+ layerInfo->set_z(requestedState.z);
+
+ ui::Transform requestedTransform = requestedState.getTransform(0);
+ LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
+ return layerInfo->mutable_requested_position();
+ });
+
+ LayerProtoHelper::writeToProto(requestedState.crop,
+ [&]() { return layerInfo->mutable_crop(); });
+
+ layerInfo->set_is_opaque(snapshot.contentOpaque);
+ if (requestedState.externalTexture)
+ layerInfo->set_pixel_format(
+ decodePixelFormat(requestedState.externalTexture->getPixelFormat()));
+ LayerProtoHelper::writeToProto(snapshot.color, [&]() { return layerInfo->mutable_color(); });
+ LayerProtoHelper::writeToProto(requestedState.color,
+ [&]() { return layerInfo->mutable_requested_color(); });
+ layerInfo->set_flags(requestedState.flags);
+
+ LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
+ layerInfo->mutable_requested_transform());
+
+ layerInfo->set_is_relative_of(requestedState.isRelativeOf);
+
+ layerInfo->set_owner_uid(requestedState.ownerUid);
+
+ if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
+ LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
+ [&]() { return layerInfo->mutable_input_window_info(); });
+ }
+
+ if (traceFlags & LayerTracing::TRACE_EXTRA) {
+ auto protoMap = layerInfo->mutable_metadata();
+ for (const auto& entry : requestedState.metadata.mMap) {
+ (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
+ }
+ }
+
+ LayerProtoHelper::writeToProto(requestedState.destinationFrame,
+ [&]() { return layerInfo->mutable_destination_frame(); });
+}
+
+google::protobuf::RepeatedPtrField<DisplayProto> LayerProtoHelper::writeDisplayInfoToProto(
+ const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos) {
+ google::protobuf::RepeatedPtrField<DisplayProto> displays;
+ displays.Reserve(displayInfos.size());
+ for (const auto& [layerStack, displayInfo] : displayInfos) {
+ auto displayProto = displays.Add();
+ displayProto->set_id(displayInfo.info.displayId);
+ displayProto->set_layer_stack(layerStack.id);
+ displayProto->mutable_size()->set_w(displayInfo.info.logicalWidth);
+ displayProto->mutable_size()->set_h(displayInfo.info.logicalHeight);
+ writeTransformToProto(displayInfo.transform, displayProto->mutable_transform());
+ displayProto->set_is_virtual(displayInfo.isVirtual);
+ }
+ return displays;
+}
+
} // namespace surfaceflinger
} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 6ade143..38d73f6 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -58,6 +58,16 @@
static void readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix);
static void writeToProto(const android::BlurRegion region, BlurRegion*);
static void readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion);
+ static void writeHierarchyToProto(LayersProto& layersProto,
+ const frontend::LayerHierarchy& root,
+ const frontend::LayerSnapshotBuilder& snapshotBuilder,
+ const std::unordered_map<uint32_t, sp<Layer>>& mLegacyLayers,
+ uint32_t traceFlags);
+ static void writeSnapshotToProto(LayerProto* outProto,
+ const frontend::RequestedLayerState& requestedState,
+ const frontend::LayerSnapshot& snapshot, uint32_t traceFlags);
+ static google::protobuf::RepeatedPtrField<DisplayProto> writeDisplayInfoToProto(
+ const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos);
};
} // namespace surfaceflinger
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 0ade467..9a4261d 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -29,7 +29,6 @@
#include <SkBlendMode.h>
#include <SkRect.h>
#include <SkSurface.h>
-#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
#undef LOG_TAG
@@ -46,15 +45,6 @@
constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
constexpr int kBufferHeight = kDigitHeight;
-SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
- constexpr float kFrameRate = 0.f;
- constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
- constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
-
- return SurfaceComposerClient::Transaction().setFrameRate(surface, kFrameRate, kCompatibility,
- kSeamlessness);
-}
-
} // namespace
SurfaceControlHolder::~SurfaceControlHolder() {
@@ -242,7 +232,7 @@
return;
}
- createTransaction(mSurfaceControl->get())
+ createTransaction()
.setLayer(mSurfaceControl->get(), INT32_MAX - 2)
.setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
@@ -272,14 +262,14 @@
}
}();
- createTransaction(mSurfaceControl->get())
- .setTransform(mSurfaceControl->get(), transform)
- .apply();
+ createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
BufferCache::const_iterator it =
mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
- const int minFps = mFpsRange.min.getIntValue();
+ // HWC minFps is not known by the framework in order
+ // to consider lower rates we set minFps to 0.
+ const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue();
const int maxFps = mFpsRange.max.getIntValue();
// Clamp to the range. The current displayFps may be outside of this range if the display
@@ -327,7 +317,7 @@
frame.offsetBy(width >> 1, height >> 4);
}
- createTransaction(mSurfaceControl->get())
+ createTransaction()
.setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
.setPosition(mSurfaceControl->get(), frame.left, frame.top)
@@ -335,14 +325,14 @@
}
void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
- createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
+ createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
}
void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
mDisplayFps = displayFps;
mRenderFps = renderFps;
const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
- createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
+ createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
@@ -351,7 +341,23 @@
const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
- createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
+ createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+}
+
+SurfaceComposerClient::Transaction RefreshRateOverlay::createTransaction() const {
+ constexpr float kFrameRate = 0.f;
+ constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+ constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
+
+ const sp<SurfaceControl>& surface = mSurfaceControl->get();
+
+ SurfaceComposerClient::Transaction transaction;
+ if (isSetByHwc()) {
+ transaction.setFlags(surface, layer_state_t::eLayerIsRefreshRateIndicator,
+ layer_state_t::eLayerIsRefreshRateIndicator);
+ }
+ transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
+ return transaction;
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index b68a88c..0b89b8e 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -21,6 +21,7 @@
#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>
@@ -55,6 +56,7 @@
Spinner = 1 << 0,
RenderRate = 1 << 1,
ShowInMiddle = 1 << 2,
+ SetByHwc = 1 << 3,
};
RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
@@ -63,6 +65,7 @@
void setViewport(ui::Size);
void changeRefreshRate(Fps, Fps);
void animate();
+ bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
private:
using Buffers = std::vector<sp<GraphicBuffer>>;
@@ -82,6 +85,8 @@
const Buffers& getOrCreateBuffers(Fps, Fps);
+ SurfaceComposerClient::Transaction createTransaction() const;
+
struct Key {
int displayFps;
int renderFps;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 839500f..327ca3f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -285,46 +285,72 @@
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
- auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
- bool stopLayerFound = false;
- auto filterVisitor = [&](Layer* layer) {
- // We don't want to capture any layers beyond the stop layer
- if (stopLayerFound) return;
+ auto layerFilterFn = [&](const char* layerName, uint32_t layerId, const Rect& bounds,
+ const ui::Transform transform, bool& outStopTraversal) -> bool {
+ // Likewise if we just found a stop layer, set the flag and abort
+ for (const auto& [area, stopLayerId, listener] : descriptors) {
+ if (stopLayerId != UNASSIGNED_LAYER_ID && layerId == stopLayerId) {
+ outStopTraversal = true;
+ return false;
+ }
+ }
- // Likewise if we just found a stop layer, set the flag and abort
- for (const auto& [area, stopLayerId, listener] : descriptors) {
- if (stopLayerId != UNASSIGNED_LAYER_ID && layer->getSequence() == stopLayerId) {
- stopLayerFound = true;
+ // Compute the layer's position on the screen
+ constexpr bool roundOutwards = true;
+ Rect transformed = transform.transform(bounds, roundOutwards);
+
+ // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
+ Rect ignore;
+ if (!transformed.intersect(sampledBounds, &ignore)) return false;
+
+ // If the layer doesn't intersect a sampling area, skip capturing it
+ bool intersectsAnyArea = false;
+ for (const auto& [area, stopLayer, listener] : descriptors) {
+ if (transformed.intersect(area, &ignore)) {
+ intersectsAnyArea = true;
+ listeners.insert(listener);
+ }
+ }
+ if (!intersectsAnyArea) return false;
+
+ ALOGV("Traversing [%s] [%d, %d, %d, %d]", layerName, bounds.left, bounds.top, bounds.right,
+ bounds.bottom);
+
+ return true;
+ };
+
+ std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshots;
+ if (mFlinger.mLayerLifecycleManagerEnabled) {
+ auto filterFn = [&](const frontend::LayerSnapshot& snapshot,
+ bool& outStopTraversal) -> bool {
+ const Rect bounds =
+ frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+ snapshot.transparentRegionHint);
+ const ui::Transform transform = snapshot.geomLayerTransform;
+ return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform,
+ outStopTraversal);
+ };
+ getLayerSnapshots =
+ mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+ filterFn);
+ } else {
+ auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
+ bool stopLayerFound = false;
+ auto filterVisitor = [&](Layer* layer) {
+ // We don't want to capture any layers beyond the stop layer
+ if (stopLayerFound) return;
+
+ if (!layerFilterFn(layer->getDebugName(), layer->getSequence(),
+ Rect(layer->getBounds()), layer->getTransform(),
+ stopLayerFound)) {
return;
}
- }
-
- // Compute the layer's position on the screen
- const Rect bounds = Rect(layer->getBounds());
- const ui::Transform transform = layer->getTransform();
- constexpr bool roundOutwards = true;
- Rect transformed = transform.transform(bounds, roundOutwards);
-
- // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
- Rect ignore;
- if (!transformed.intersect(sampledBounds, &ignore)) return;
-
- // If the layer doesn't intersect a sampling area, skip capturing it
- bool intersectsAnyArea = false;
- for (const auto& [area, stopLayer, listener] : descriptors) {
- if (transformed.intersect(area, &ignore)) {
- intersectsAnyArea = true;
- listeners.insert(listener);
- }
- }
- if (!intersectsAnyArea) return;
-
- ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left,
- bounds.top, bounds.right, bounds.bottom);
- visitor(layer);
+ visitor(layer);
+ };
+ mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
};
- mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
- };
+ getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ }
std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
@@ -344,7 +370,6 @@
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
}
- auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
constexpr bool kRegionSampling = true;
constexpr bool kGrayscale = false;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index eb6d7e4..57661f1 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -238,7 +238,7 @@
namespace impl {
-EventThread::EventThread(const char* name, scheduler::VsyncSchedule& vsyncSchedule,
+EventThread::EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule> vsyncSchedule,
android::frametimeline::TokenManager* tokenManager,
ThrottleVsyncCallback throttleVsyncCallback,
GetVsyncPeriodFunction getVsyncPeriodFunction,
@@ -248,13 +248,8 @@
mVsyncTracer(base::StringPrintf("VSYNC-%s", name), 0),
mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration),
mReadyDuration(readyDuration),
- mVsyncSchedule(vsyncSchedule),
- mVsyncRegistration(
- vsyncSchedule.getDispatch(),
- [this](nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
- onVsync(vsyncTime, wakeupTime, readyTime);
- },
- name),
+ mVsyncSchedule(std::move(vsyncSchedule)),
+ mVsyncRegistration(mVsyncSchedule->getDispatch(), createDispatchCallback(), name),
mTokenManager(tokenManager),
mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)) {
@@ -375,7 +370,7 @@
vsyncEventData.frameInterval = frameInterval;
const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> {
std::lock_guard<std::mutex> lock(mMutex);
- const auto vsyncTime = mVsyncSchedule.getTracker().nextAnticipatedVSyncTimeFrom(
+ const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(
systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
return {vsyncTime, vsyncTime - mReadyDuration.count()};
}();
@@ -384,23 +379,13 @@
return vsyncEventData;
}
-void EventThread::onScreenReleased() {
+void EventThread::enableSyntheticVsync(bool enable) {
std::lock_guard<std::mutex> lock(mMutex);
- if (!mVSyncState || mVSyncState->synthetic) {
+ if (!mVSyncState || mVSyncState->synthetic == enable) {
return;
}
- mVSyncState->synthetic = true;
- mCondition.notify_all();
-}
-
-void EventThread::onScreenAcquired() {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mVSyncState || !mVSyncState->synthetic) {
- return;
- }
-
- mVSyncState->synthetic = false;
+ mVSyncState->synthetic = enable;
mCondition.notify_all();
}
@@ -543,7 +528,7 @@
const auto throttleVsync = [&] {
const auto& vsyncData = event.vsync.vsyncData;
if (connection->frameRate.isValid()) {
- return !mVsyncSchedule.getTracker()
+ return !mVsyncSchedule->getTracker()
.isVSyncInPhase(vsyncData.preferredExpectedPresentationTime(),
connection->frameRate);
}
@@ -706,6 +691,26 @@
}
}
+void EventThread::onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule> schedule) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ const bool reschedule = mVsyncRegistration.cancel() == scheduler::CancelResult::Cancelled;
+ mVsyncSchedule = std::move(schedule);
+ mVsyncRegistration =
+ scheduler::VSyncCallbackRegistration(mVsyncSchedule->getDispatch(),
+ createDispatchCallback(), mThreadName);
+ if (reschedule) {
+ mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
+ .readyDuration = mReadyDuration.count(),
+ .earliestVsync = mLastVsyncCallbackTime.ns()});
+ }
+}
+
+scheduler::VSyncDispatch::Callback EventThread::createDispatchCallback() {
+ return [this](nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
+ onVsync(vsyncTime, wakeupTime, readyTime);
+ };
+}
+
} // namespace impl
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 347dc4a..87e20a0 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -106,11 +106,8 @@
virtual sp<EventThreadConnection> createEventConnection(
ResyncCallback, EventRegistrationFlags eventRegistration = {}) const = 0;
- // called before the screen is turned off from main thread
- virtual void onScreenReleased() = 0;
-
- // called after the screen is turned on from main thread
- virtual void onScreenAcquired() = 0;
+ // Feed clients with fake VSYNC, e.g. while the display is off.
+ virtual void enableSyntheticVsync(bool) = 0;
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
@@ -136,6 +133,8 @@
// Retrieves the number of event connections tracked by this EventThread.
virtual size_t getEventThreadConnectionCount() = 0;
+
+ virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
};
namespace impl {
@@ -145,8 +144,8 @@
using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
- EventThread(const char* name, scheduler::VsyncSchedule&, frametimeline::TokenManager*,
- ThrottleVsyncCallback, GetVsyncPeriodFunction,
+ EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule>,
+ frametimeline::TokenManager*, ThrottleVsyncCallback, GetVsyncPeriodFunction,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration);
~EventThread();
@@ -159,11 +158,7 @@
VsyncEventData getLatestVsyncEventData(
const sp<EventThreadConnection>& connection) const override;
- // called before the screen is turned off from main thread
- void onScreenReleased() override;
-
- // called after the screen is turned on from main thread
- void onScreenAcquired() override;
+ void enableSyntheticVsync(bool) override;
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
@@ -179,6 +174,8 @@
size_t getEventThreadConnectionCount() override;
+ void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override;
+
private:
friend EventThreadTest;
@@ -202,11 +199,13 @@
nsecs_t timestamp, nsecs_t preferredExpectedPresentationTime,
nsecs_t preferredDeadlineTimestamp) const;
+ scheduler::VSyncDispatch::Callback createDispatchCallback();
+
const char* const mThreadName;
TracedOrdinal<int> mVsyncTracer;
TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mMutex);
std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex);
- scheduler::VsyncSchedule& mVsyncSchedule;
+ std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now();
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index c4de749..92c2189 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -18,12 +18,14 @@
#include <vector>
+#include <ui/DisplayId.h>
+
#include "Display/DisplayModeRequest.h"
namespace android::scheduler {
struct ISchedulerCallback {
- virtual void setVsyncEnabled(bool) = 0;
+ virtual void setVsyncEnabled(PhysicalDisplayId, bool) = 0;
virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
virtual void triggerOnFrameRateOverridesChanged() = 0;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index e853833..beaf972 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -118,27 +118,17 @@
}
}
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
- LayerUpdateType updateType) {
+void LayerHistory::record(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
+ nsecs_t now, LayerUpdateType updateType) {
std::lock_guard lock(mLock);
- auto id = layer->getSequence();
-
auto [found, layerPair] = findLayer(id);
if (found == LayerStatus::NotFound) {
// Offscreen layer
- ALOGV("%s: %s not registered", __func__, layer->getName().c_str());
+ ALOGV("%s: %d not registered", __func__, id);
return;
}
const auto& info = layerPair->second;
- const auto layerProps = LayerInfo::LayerProps{
- .visible = layer->isVisible(),
- .bounds = layer->getBounds(),
- .transform = layer->getTransform(),
- .setFrameRateVote = layer->getFrameRateForLayerTree(),
- .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
- };
-
info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
// Set frame rate to attached choreographer.
@@ -149,7 +139,7 @@
while (it != range.second) {
sp<EventThreadConnection> choreographerConnection = it->second.promote();
if (choreographerConnection) {
- choreographerConnection->frameRate = layer->getFrameRateForLayerTree().rate;
+ choreographerConnection->frameRate = layerProps.setFrameRateVote.rate;
it++;
} else {
it = mAttachedChoreographers.erase(it);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 68e7030..69caf9f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -38,6 +38,7 @@
namespace scheduler {
class LayerInfo;
+struct LayerProps;
class LayerHistory {
public:
@@ -63,7 +64,8 @@
};
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType);
+ void record(int32_t id, const LayerProps& props, nsecs_t presentTime, nsecs_t now,
+ LayerUpdateType updateType);
// Updates the default frame rate compatibility which takes effect when the app
// does not set a preference for refresh rate.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 0142ccd..5a90d58 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -44,14 +44,17 @@
mOwnerUid(ownerUid),
mDefaultVote(defaultVote),
mLayerVote({defaultVote, Fps()}),
- mRefreshRateHistory(name) {}
+ mLayerProps(std::make_unique<LayerProps>()),
+ mRefreshRateHistory(name) {
+ ;
+}
void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
- bool pendingModeChange, LayerProps props) {
+ bool pendingModeChange, const LayerProps& props) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
mLastUpdatedTime = std::max(lastPresentTime, now);
- mLayerProps = props;
+ *mLayerProps = props;
switch (updateType) {
case LayerUpdateType::AnimationTX:
mLastAnimationTime = std::max(lastPresentTime, now);
@@ -305,6 +308,26 @@
return mTraceTags.at(type).c_str();
}
+LayerInfo::FrameRate LayerInfo::getSetFrameRateVote() const {
+ return mLayerProps->setFrameRateVote;
+}
+
+bool LayerInfo::isVisible() const {
+ return mLayerProps->visible;
+}
+
+int32_t LayerInfo::getFrameRateSelectionPriority() const {
+ return mLayerProps->frameRateSelectionPriority;
+}
+
+FloatRect LayerInfo::getBounds() const {
+ return mLayerProps->bounds;
+}
+
+ui::Transform LayerInfo::getTransform() const {
+ return mLayerProps->transform;
+}
+
LayerInfo::RefreshRateHistory::HeuristicTraceTagData
LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
const std::string prefix = "LFPS ";
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 93485be..a3523ac 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -37,7 +37,7 @@
namespace scheduler {
using namespace std::chrono_literals;
-
+struct LayerProps;
// Maximum period between presents for a layer to be considered active.
constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
@@ -132,19 +132,11 @@
LayerInfo(const LayerInfo&) = delete;
LayerInfo& operator=(const LayerInfo&) = delete;
- struct LayerProps {
- bool visible = false;
- FloatRect bounds;
- ui::Transform transform;
- FrameRate setFrameRateVote;
- int32_t frameRateSelectionPriority = -1;
- };
-
// Records the last requested present time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
- bool pendingModeChange, LayerProps props);
+ bool pendingModeChange, const LayerProps& props);
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
@@ -168,13 +160,11 @@
// updated time, the updated time is the present time.
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
- FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; }
- bool isVisible() const { return mLayerProps.visible; }
- int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; }
-
- FloatRect getBounds() const { return mLayerProps.bounds; }
-
- ui::Transform getTransform() const { return mLayerProps.transform; }
+ FrameRate getSetFrameRateVote() const;
+ bool isVisible() const;
+ int32_t getFrameRateSelectionPriority() const;
+ FloatRect getBounds() const;
+ ui::Transform getTransform() const;
// Returns a C string for tracing a vote
const char* getTraceTag(LayerHistory::LayerVoteType type) const;
@@ -294,7 +284,7 @@
static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
- LayerProps mLayerProps;
+ std::unique_ptr<LayerProps> mLayerProps;
RefreshRateHistory mRefreshRateHistory;
@@ -304,5 +294,13 @@
static bool sTraceEnabled;
};
+struct LayerProps {
+ bool visible = false;
+ FloatRect bounds;
+ ui::Transform transform;
+ LayerInfo::FrameRate setFrameRateVote;
+ int32_t frameRateSelectionPriority = -1;
+};
+
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index dec8f59..7457b84 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -75,19 +75,36 @@
mHandler->dispatchFrame(vsyncId, expectedVsyncTime);
}
-void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
+void MessageQueue::initVsync(std::shared_ptr<scheduler::VSyncDispatch> dispatch,
frametimeline::TokenManager& tokenManager,
std::chrono::nanoseconds workDuration) {
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
mVsync.tokenManager = &tokenManager;
+ onNewVsyncScheduleLocked(std::move(dispatch));
+}
+
+void MessageQueue::onNewVsyncSchedule(std::shared_ptr<scheduler::VSyncDispatch> dispatch) {
+ std::lock_guard lock(mVsync.mutex);
+ onNewVsyncScheduleLocked(std::move(dispatch));
+}
+
+void MessageQueue::onNewVsyncScheduleLocked(std::shared_ptr<scheduler::VSyncDispatch> dispatch) {
+ const bool reschedule = mVsync.registration &&
+ mVsync.registration->cancel() == scheduler::CancelResult::Cancelled;
mVsync.registration = std::make_unique<
- scheduler::VSyncCallbackRegistration>(dispatch,
+ scheduler::VSyncCallbackRegistration>(std::move(dispatch),
std::bind(&MessageQueue::vsyncCallback, this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3),
"sf");
+ if (reschedule) {
+ mVsync.scheduledFrameTime =
+ mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ .readyDuration = 0,
+ .earliestVsync = mVsync.lastCallbackTime.ns()});
+ }
}
void MessageQueue::destroyVsync() {
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 0d59337..9c9b2f3 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -65,7 +65,7 @@
public:
virtual ~MessageQueue() = default;
- virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ virtual void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&,
std::chrono::nanoseconds workDuration) = 0;
virtual void destroyVsync() = 0;
virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
@@ -106,6 +106,8 @@
void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
+ void onNewVsyncSchedule(std::shared_ptr<scheduler::VSyncDispatch>) EXCLUDES(mVsync.mutex);
+
private:
virtual void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) = 0;
@@ -127,10 +129,12 @@
Vsync mVsync;
+ void onNewVsyncScheduleLocked(std::shared_ptr<scheduler::VSyncDispatch>) REQUIRES(mVsync.mutex);
+
public:
explicit MessageQueue(ICompositor&);
- void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&,
std::chrono::nanoseconds workDuration) override;
void destroyVsync() override;
void setDuration(std::chrono::nanoseconds workDuration) override;
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index f95646c..02e8719 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -40,7 +40,7 @@
OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
const TimeoutCallback& timeoutCallback,
- std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
+ std::unique_ptr<android::Clock> clock = std::make_unique<SteadyClock>());
~OneShotTimer();
Duration interval() const { return mInterval; }
@@ -82,7 +82,7 @@
std::thread mThread;
// Clock object for the timer. Mocked in unit tests.
- std::unique_ptr<Clock> mClock;
+ std::unique_ptr<android::Clock> mClock;
// Semaphore to keep mThread synchronized.
sem_t mSemaphore;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index f6fe468..eec7c08 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -308,7 +308,7 @@
// significantly faster than the display rate, at it would cause a significant frame drop.
// It is more appropriate to choose a higher display rate even if
// a pull-down will be required.
- constexpr float kMinMultiplier = 0.25f;
+ constexpr float kMinMultiplier = 0.75f;
if (multiplier >= kMinMultiplier &&
isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
return kScoreForFractionalPairs;
@@ -958,7 +958,7 @@
}
const bool ascending = (refreshRateOrder == RefreshRateOrder::Ascending);
- const auto id = frameRateMode.modePtr->getId();
+ const auto id = modePtr->getId();
if (ascending && frameRateMode.fps < *maxRenderRateForMode.get(id)) {
// TODO(b/266481656): Once this bug is fixed, we can remove this workaround and actually
// use a lower frame rate when we want Ascending frame rates.
@@ -970,14 +970,20 @@
if (ascending) {
score = 1.0f / score;
}
+
+ constexpr float kScore = std::numeric_limits<float>::max();
if (preferredDisplayModeOpt) {
if (*preferredDisplayModeOpt == modePtr->getId()) {
- constexpr float kScore = std::numeric_limits<float>::max();
ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
return;
}
constexpr float kNonPreferredModePenalty = 0.95f;
score *= kNonPreferredModePenalty;
+ } else if (ascending && id == getMinRefreshRateByPolicyLocked()->getId()) {
+ // TODO(b/266481656): Once this bug is fixed, we can remove this workaround
+ // and actually use a lower frame rate when we want Ascending frame rates.
+ ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
+ return;
}
ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 4f5842a..5052e6e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -32,7 +32,6 @@
#include <scheduler/Seamlessness.h>
#include "DisplayHardware/DisplayMode.h"
-#include "DisplayHardware/HWComposer.h"
#include "Scheduler/OneShotTimer.h"
#include "Scheduler/StrongTyping.h"
#include "ThreadContext.h"
@@ -297,6 +296,8 @@
RefreshRateSelector(const RefreshRateSelector&) = delete;
RefreshRateSelector& operator=(const RefreshRateSelector&) = delete;
+ const DisplayModes& displayModes() const { return mDisplayModes; }
+
// Returns whether switching modes (refresh rate or resolution) is possible.
// TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
// differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index dab01ba..6e33272 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -81,7 +81,7 @@
mTouchTimer.reset();
// Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
- demoteLeaderDisplay();
+ demotePacesetterDisplay();
}
void Scheduler::startTimers() {
@@ -106,34 +106,43 @@
}
}
-void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
- demoteLeaderDisplay();
+void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+ demotePacesetterDisplay();
std::scoped_lock lock(mDisplayLock);
- promoteLeaderDisplay(leaderIdOpt);
+ promotePacesetterDisplay(pacesetterIdOpt);
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
- demoteLeaderDisplay();
+ registerDisplayInternal(displayId, std::move(selectorPtr),
+ std::make_shared<VsyncSchedule>(displayId, mFeatures));
+}
+
+void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
+ RefreshRateSelectorPtr selectorPtr,
+ std::shared_ptr<VsyncSchedule> vsyncSchedule) {
+ demotePacesetterDisplay();
std::scoped_lock lock(mDisplayLock);
mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
+ mVsyncSchedules.emplace_or_replace(displayId, std::move(vsyncSchedule));
- promoteLeaderDisplay();
+ promotePacesetterDisplay();
}
void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
- demoteLeaderDisplay();
+ demotePacesetterDisplay();
std::scoped_lock lock(mDisplayLock);
mRefreshRateSelectors.erase(displayId);
+ mVsyncSchedules.erase(displayId);
// Do not allow removing the final display. Code in the scheduler expects
// there to be at least one display. (This may be relaxed in the future with
// headless virtual display.)
LOG_ALWAYS_FATAL_IF(mRefreshRateSelectors.empty(), "Cannot unregister all displays!");
- promoteLeaderDisplay();
+ promotePacesetterDisplay();
}
void Scheduler::run() {
@@ -154,13 +163,9 @@
compositor.sample();
}
-void Scheduler::createVsyncSchedule(FeatureFlags features) {
- mVsyncSchedule = std::make_unique<VsyncSchedule>(features);
-}
-
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
const bool supportsFrameRateOverrideByContent =
- leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
+ pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
return mFrameRateOverrideMappings
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
@@ -172,11 +177,11 @@
}
ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str());
- return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate);
+ return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate);
}
bool Scheduler::isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const {
- return mVsyncSchedule->getTracker().isVSyncInPhase(timePoint.ns(), frameRate);
+ return getVsyncSchedule()->getTracker().isVSyncInPhase(timePoint.ns(), frameRate);
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
@@ -187,8 +192,9 @@
impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
return [this](uid_t uid) {
- const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps;
- const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
+ const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().fps;
+ const nsecs_t currentPeriod =
+ getVsyncSchedule()->period().ns() ?: refreshRate.getPeriodNsecs();
const auto frameRate = getFrameRateOverride(uid);
if (!frameRate.has_value()) {
@@ -208,7 +214,7 @@
std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) {
auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
- *mVsyncSchedule, tokenManager,
+ getVsyncSchedule(), tokenManager,
makeThrottleVsyncCallback(),
makeGetVsyncPeriodFunction(),
workDuration, readyDuration);
@@ -265,29 +271,21 @@
thread->onHotplugReceived(displayId, connected);
}
-void Scheduler::onScreenAcquired(ConnectionHandle handle) {
+void Scheduler::enableSyntheticVsync(bool enable) {
+ // TODO(b/241285945): Remove connection handles.
+ const ConnectionHandle handle = mAppConnectionHandle;
android::EventThread* thread;
{
std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle);
thread = mConnections[handle].thread.get();
}
- thread->onScreenAcquired();
-}
-
-void Scheduler::onScreenReleased(ConnectionHandle handle) {
- android::EventThread* thread;
- {
- std::lock_guard<std::mutex> lock(mConnectionsLock);
- RETURN_IF_INVALID_HANDLE(handle);
- thread = mConnections[handle].thread.get();
- }
- thread->onScreenReleased();
+ thread->enableSyntheticVsync(enable);
}
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
const bool supportsFrameRateOverrideByContent =
- leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
+ pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -328,7 +326,7 @@
// If the mode is not the current mode, this means that a
// mode change is in progress. In that case we shouldn't dispatch an event
// as it will be dispatched when the current mode changes.
- if (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
+ if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
return;
}
@@ -393,32 +391,60 @@
setDuration(config.sfWorkDuration);
}
-void Scheduler::enableHardwareVsync() {
- mVsyncSchedule->enableHardwareVsync(mSchedulerCallback);
+void Scheduler::enableHardwareVsync(PhysicalDisplayId id) {
+ auto schedule = getVsyncSchedule(id);
+ schedule->enableHardwareVsync(mSchedulerCallback);
}
-void Scheduler::disableHardwareVsync(bool disallow) {
- mVsyncSchedule->disableHardwareVsync(mSchedulerCallback, disallow);
+void Scheduler::disableHardwareVsync(PhysicalDisplayId id, bool disallow) {
+ auto schedule = getVsyncSchedule(id);
+ schedule->disableHardwareVsync(mSchedulerCallback, disallow);
}
-void Scheduler::resyncToHardwareVsync(bool allowToEnable, Fps refreshRate) {
- if (mVsyncSchedule->isHardwareVsyncAllowed(allowToEnable) && refreshRate.isValid()) {
- mVsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod());
+void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ for (const auto& [id, _] : mRefreshRateSelectors) {
+ resyncToHardwareVsyncLocked(id, allowToEnable);
}
}
-void Scheduler::setRenderRate(Fps renderFrameRate) {
- const auto mode = leaderSelectorPtr()->getActiveMode();
+void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable,
+ std::optional<Fps> refreshRate) {
+ auto schedule = getVsyncScheduleLocked(id);
+ if (schedule->isHardwareVsyncAllowed(allowToEnable)) {
+ if (!refreshRate) {
+ auto selectorPtr = mRefreshRateSelectors.get(id);
+ LOG_ALWAYS_FATAL_IF(!selectorPtr);
+ refreshRate = selectorPtr->get()->getActiveMode().modePtr->getFps();
+ }
+ if (refreshRate->isValid()) {
+ schedule->startPeriodTransition(mSchedulerCallback, refreshRate->getPeriod(),
+ false /* force */);
+ }
+ }
+}
+
+void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ auto selectorPtr = mRefreshRateSelectors.get(id);
+ LOG_ALWAYS_FATAL_IF(!selectorPtr);
+ const auto mode = selectorPtr->get()->getActiveMode();
using fps_approx_ops::operator!=;
LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps,
- "Mismatch in render frame rates. Selector: %s, Scheduler: %s",
- to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str());
+ "Mismatch in render frame rates. Selector: %s, Scheduler: %s, Display: "
+ "%" PRIu64,
+ to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str(), id.value);
ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
to_string(mode.modePtr->getFps()).c_str());
- mVsyncSchedule->getTracker().setRenderRate(renderFrameRate);
+ getVsyncScheduleLocked(id)->getTracker().setRenderRate(renderFrameRate);
}
void Scheduler::resync() {
@@ -428,24 +454,26 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- const auto refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
- resyncToHardwareVsync(false, refreshRate);
+ resyncAllToHardwareVsync(false /* allowToEnable */);
}
}
-bool Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriodIn) {
+bool Scheduler::addResyncSample(PhysicalDisplayId id, nsecs_t timestamp,
+ std::optional<nsecs_t> hwcVsyncPeriodIn) {
const auto hwcVsyncPeriod = ftl::Optional(hwcVsyncPeriodIn).transform([](nsecs_t nanos) {
return Period::fromNs(nanos);
});
- return mVsyncSchedule->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp),
- hwcVsyncPeriod);
+ return getVsyncSchedule(id)->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp),
+ hwcVsyncPeriod);
}
-void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) {
- if (mVsyncSchedule->getController().addPresentFence(std::move(fence))) {
- enableHardwareVsync();
+void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
+ auto schedule = getVsyncSchedule(id);
+ const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence));
+ if (needMoreSignals) {
+ schedule->enableHardwareVsync(mSchedulerCallback);
} else {
- disableHardwareVsync(false);
+ schedule->disableHardwareVsync(mSchedulerCallback, false /* disallow */);
}
}
@@ -460,10 +488,10 @@
mLayerHistory.deregisterLayer(layer);
}
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
- if (leaderSelectorPtr()->canSwitch()) {
- mLayerHistory.record(layer, presentTime, systemTime(), updateType);
+ if (pacesetterSelectorPtr()->canSwitch()) {
+ mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType);
}
}
@@ -477,7 +505,7 @@
}
void Scheduler::chooseRefreshRateForContent() {
- const auto selectorPtr = leaderSelectorPtr();
+ const auto selectorPtr = pacesetterSelectorPtr();
if (!selectorPtr->canSwitch()) return;
ATRACE_CALL();
@@ -487,22 +515,32 @@
}
void Scheduler::resetIdleTimer() {
- leaderSelectorPtr()->resetIdleTimer();
+ pacesetterSelectorPtr()->resetIdleTimer();
}
void Scheduler::onTouchHint() {
if (mTouchTimer) {
mTouchTimer->reset();
- leaderSelectorPtr()->resetKernelIdleTimer();
+ pacesetterSelectorPtr()->resetKernelIdleTimer();
}
}
-void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) {
- {
+void Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMode) {
+ const bool isPacesetter = [this, id]() REQUIRES(kMainThreadContext) {
+ ftl::FakeGuard guard(mDisplayLock);
+ return id == mPacesetterDisplayId;
+ }();
+ if (isPacesetter) {
+ // TODO (b/255657128): This needs to be handled per display.
std::lock_guard<std::mutex> lock(mPolicyLock);
mPolicy.displayPowerMode = powerMode;
}
- mVsyncSchedule->getController().setDisplayPowerMode(powerMode);
+ {
+ std::scoped_lock lock(mDisplayLock);
+ auto vsyncSchedule = getVsyncScheduleLocked(id);
+ vsyncSchedule->getController().setDisplayPowerMode(powerMode);
+ }
+ if (!isPacesetter) return;
if (mDisplayPowerTimer) {
mDisplayPowerTimer->reset();
@@ -513,12 +551,30 @@
mLayerHistory.clear();
}
+std::shared_ptr<const VsyncSchedule> Scheduler::getVsyncSchedule(
+ std::optional<PhysicalDisplayId> idOpt) const {
+ std::scoped_lock lock(mDisplayLock);
+ return getVsyncScheduleLocked(idOpt);
+}
+
+std::shared_ptr<const VsyncSchedule> Scheduler::getVsyncScheduleLocked(
+ std::optional<PhysicalDisplayId> idOpt) const {
+ ftl::FakeGuard guard(kMainThreadContext);
+ if (!idOpt) {
+ LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId, "Missing a pacesetter!");
+ idOpt = mPacesetterDisplayId;
+ }
+ auto scheduleOpt = mVsyncSchedules.get(*idOpt);
+ LOG_ALWAYS_FATAL_IF(!scheduleOpt);
+ return std::const_pointer_cast<const VsyncSchedule>(scheduleOpt->get());
+}
+
void Scheduler::kernelIdleTimerCallback(TimerState state) {
ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
- const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
+ const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().modePtr->getFps();
constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
using namespace fps_approx_ops;
@@ -527,12 +583,17 @@
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
- resyncToHardwareVsync(true /* makeAvailable */, refreshRate);
+ resyncAllToHardwareVsync(true /* allowToEnable */);
} else if (state == TimerState::Expired && refreshRate <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// Disable HW VSYNC if the timer expired, as we don't need it enabled if
// we're not pushing frames, and if we're in PERFORMANCE mode then we'll
// need to update the VsyncController model anyway.
- disableHardwareVsync(false /* makeUnavailable */);
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+ constexpr bool disallow = false;
+ for (auto& [_, schedule] : mVsyncSchedules) {
+ schedule->disableHardwareVsync(mSchedulerCallback, disallow);
+ }
}
mSchedulerCallback.kernelTimerChanged(state == TimerState::Expired);
@@ -577,7 +638,7 @@
{
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- dumper.dump("leaderDisplayId"sv, mLeaderDisplayId);
+ dumper.dump("pacesetterDisplayId"sv, mPacesetterDisplayId);
}
dumper.dump("layerHistory"sv, mLayerHistory.dump());
dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
@@ -589,46 +650,72 @@
}
void Scheduler::dumpVsync(std::string& out) const {
- mVsyncSchedule->dump(out);
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+ if (mPacesetterDisplayId) {
+ base::StringAppendF(&out, "VsyncSchedule for pacesetter %s:\n",
+ to_string(*mPacesetterDisplayId).c_str());
+ getVsyncScheduleLocked()->dump(out);
+ }
+ for (auto& [id, vsyncSchedule] : mVsyncSchedules) {
+ if (id == mPacesetterDisplayId) {
+ continue;
+ }
+ base::StringAppendF(&out, "VsyncSchedule for follower %s:\n", to_string(id).c_str());
+ vsyncSchedule->dump(out);
+ }
}
bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
if (consideredSignals.idle) return false;
const auto frameRateOverrides =
- leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
- displayRefreshRate, consideredSignals);
+ pacesetterSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+ displayRefreshRate, consideredSignals);
// Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
// the FrameRateOverrideMappings rather than here.
return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
}
-void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
- // TODO(b/241286431): Choose the leader display.
- mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first);
- ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str());
+void Scheduler::promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+ // TODO(b/241286431): Choose the pacesetter display.
+ mPacesetterDisplayId = pacesetterIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+ ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
- if (const auto leaderPtr = leaderSelectorPtrLocked()) {
- leaderPtr->setIdleTimerCallbacks(
+ auto vsyncSchedule = getVsyncScheduleLocked(*mPacesetterDisplayId);
+ if (const auto pacesetterPtr = pacesetterSelectorPtrLocked()) {
+ pacesetterPtr->setIdleTimerCallbacks(
{.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
.onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
.kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
.onExpired =
[this] { kernelIdleTimerCallback(TimerState::Expired); }}});
- leaderPtr->startIdleTimer();
+ pacesetterPtr->startIdleTimer();
+
+ const Fps refreshRate = pacesetterPtr->getActiveMode().modePtr->getFps();
+ vsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
+ true /* force */);
+ }
+
+ onNewVsyncSchedule(vsyncSchedule->getDispatch());
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ for (auto& [_, connection] : mConnections) {
+ connection.thread->onNewVsyncSchedule(vsyncSchedule);
+ }
}
}
-void Scheduler::demoteLeaderDisplay() {
+void Scheduler::demotePacesetterDisplay() {
// No need to lock for reads on kMainThreadContext.
- if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) {
- leaderPtr->stopIdleTimer();
- leaderPtr->clearIdleTimerCallbacks();
+ if (const auto pacesetterPtr = FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
+ pacesetterPtr->stopIdleTimer();
+ pacesetterPtr->clearIdleTimerCallbacks();
}
- // Clear state that depends on the leader's RefreshRateSelector.
+ // Clear state that depends on the pacesetter's RefreshRateSelector.
std::scoped_lock lock(mPolicyLock);
mPolicy = {};
}
@@ -657,10 +744,11 @@
modeChoices = chooseDisplayModes();
- // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
- // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
+ // TODO(b/240743786): The pacesetter display's mode must change for any
+ // DisplayModeRequest to go through. Fix this by tracking per-display Scheduler::Policy
+ // and timers.
std::tie(modeOpt, consideredSignals) =
- modeChoices.get(*mLeaderDisplayId)
+ modeChoices.get(*mPacesetterDisplayId)
.transform([](const DisplayModeChoice& choice) {
return std::make_pair(choice.mode, choice.consideredSignals);
})
@@ -793,7 +881,7 @@
FrameRateMode Scheduler::getPreferredDisplayMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
const auto frameRateMode =
- leaderSelectorPtr()
+ pacesetterSelectorPtr()
->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
.ranking.front()
.frameRateMode;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a340919..74547d5 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -102,8 +102,8 @@
void startTimers();
- // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}.
- void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+ // TODO(b/241285191): Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
+ void setPacesetterDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
EXCLUDES(mDisplayLock);
using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
@@ -114,8 +114,6 @@
void run();
- void createVsyncSchedule(FeatureFlags);
-
using Impl::initVsync;
using Impl::getScheduledFrameTime;
@@ -157,8 +155,8 @@
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock);
void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&);
- void onScreenAcquired(ConnectionHandle);
- void onScreenReleased(ConnectionHandle);
+
+ void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
EXCLUDES(mConnectionsLock);
@@ -167,42 +165,62 @@
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration);
- const VsyncModulator& vsyncModulator() const { return *mVsyncModulator; }
+ VsyncModulator& vsyncModulator() { return *mVsyncModulator; }
+ // In some cases, we should only modulate for the pacesetter display. In those
+ // cases, the caller should pass in the relevant display, and the method
+ // will no-op if it's not the pacesetter. Other cases are not specific to a
+ // display.
template <typename... Args,
typename Handler = std::optional<VsyncConfig> (VsyncModulator::*)(Args...)>
- void modulateVsync(Handler handler, Args... args) {
+ void modulateVsync(std::optional<PhysicalDisplayId> id, Handler handler, Args... args) {
+ if (id) {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+ if (id != mPacesetterDisplayId) {
+ return;
+ }
+ }
+
if (const auto config = (*mVsyncModulator.*handler)(args...)) {
- setVsyncConfig(*config, getLeaderVsyncPeriod());
+ setVsyncConfig(*config, getPacesetterVsyncPeriod());
}
}
void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod);
// Sets the render rate for the scheduler to run at.
- void setRenderRate(Fps);
+ void setRenderRate(PhysicalDisplayId, Fps);
- void enableHardwareVsync();
- void disableHardwareVsync(bool disallow);
+ void enableHardwareVsync(PhysicalDisplayId);
+ void disableHardwareVsync(PhysicalDisplayId, bool disallow);
// Resyncs the scheduler to hardware vsync.
// If allowToEnable is true, then hardware vsync will be turned on.
// Otherwise, if hardware vsync is not already enabled then this method will
// no-op.
- void resyncToHardwareVsync(bool allowToEnable, Fps refreshRate);
+ // If refreshRate is nullopt, use the existing refresh rate of the display.
+ void resyncToHardwareVsync(PhysicalDisplayId id, bool allowToEnable,
+ std::optional<Fps> refreshRate = std::nullopt)
+ EXCLUDES(mDisplayLock) {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+ resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate);
+ }
void resync() EXCLUDES(mDisplayLock);
void forceNextResync() { mLastResyncTime = 0; }
// Passes a vsync sample to VsyncController. Returns true if
// VsyncController detected that the vsync period changed and false
// otherwise.
- bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod);
- void addPresentFence(std::shared_ptr<FenceTime>);
+ bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
+ std::optional<nsecs_t> hwcVsyncPeriod);
+ void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock);
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType)
- EXCLUDES(mDisplayLock);
+ void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
+ LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
void setDefaultFrameRateCompatibility(Layer*);
void deregisterLayer(Layer*);
@@ -215,22 +233,28 @@
// Indicates that touch interaction is taking place.
void onTouchHint();
- void setDisplayPowerMode(hal::PowerMode powerMode);
+ void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode)
+ REQUIRES(kMainThreadContext);
- VsyncSchedule& getVsyncSchedule() { return *mVsyncSchedule; }
+ std::shared_ptr<const VsyncSchedule> getVsyncSchedule(
+ std::optional<PhysicalDisplayId> idOpt = std::nullopt) const EXCLUDES(mDisplayLock);
+ std::shared_ptr<VsyncSchedule> getVsyncSchedule(
+ std::optional<PhysicalDisplayId> idOpt = std::nullopt) EXCLUDES(mDisplayLock) {
+ return std::const_pointer_cast<VsyncSchedule>(
+ static_cast<const Scheduler*>(this)->getVsyncSchedule(idOpt));
+ }
// Returns true if a given vsync timestamp is considered valid vsync
// for a given uid
bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const;
- // Checks if a vsync timestamp is in phase for a frame rate
- bool isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const;
+ bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const;
void dump(utils::Dumper&) const;
void dump(ConnectionHandle, std::string&) const;
- void dumpVsync(std::string&) const;
+ void dumpVsync(std::string&) const EXCLUDES(mDisplayLock);
- // Returns the preferred refresh rate and frame rate for the leader display.
+ // Returns the preferred refresh rate and frame rate for the pacesetter display.
FrameRateMode getPreferredDisplayMode();
// Notifies the scheduler about a refresh rate timeline change.
@@ -253,12 +277,12 @@
// Retrieves the overridden refresh rate for a given uid.
std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
- Period getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
- return leaderSelectorPtr()->getActiveMode().fps.getPeriod();
+ Period getPacesetterVsyncPeriod() const EXCLUDES(mDisplayLock) {
+ return pacesetterSelectorPtr()->getActiveMode().fps.getPeriod();
}
- Fps getLeaderRefreshRate() const EXCLUDES(mDisplayLock) {
- return leaderSelectorPtr()->getActiveMode().fps;
+ Fps getPacesetterRefreshRate() const EXCLUDES(mDisplayLock) {
+ return pacesetterSelectorPtr()->getActiveMode().fps;
}
// Returns the framerate of the layer with the given sequence ID
@@ -288,16 +312,24 @@
void touchTimerCallback(TimerState);
void displayPowerTimerCallback(TimerState);
+ void resyncToHardwareVsyncLocked(PhysicalDisplayId, bool allowToEnable,
+ std::optional<Fps> refreshRate = std::nullopt)
+ REQUIRES(kMainThreadContext, mDisplayLock);
+ void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock);
void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
- // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
- // `mLeaderDisplayId` is never `std::nullopt`.
- void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt)
+ // Chooses a pacesetter among the registered displays, unless `pacesetterIdOpt` is specified.
+ // The new `mPacesetterDisplayId` is never `std::nullopt`.
+ void promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
REQUIRES(kMainThreadContext, mDisplayLock);
- // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the
- // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
- void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+ // Blocks until the pacesetter's idle timer thread exits. `mDisplayLock` must not be locked by
+ // the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+ void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+
+ void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr,
+ std::shared_ptr<VsyncSchedule>) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
struct Policy;
@@ -355,7 +387,6 @@
std::atomic<nsecs_t> mLastResyncTime = 0;
const FeatureFlags mFeatures;
- std::unique_ptr<VsyncSchedule> mVsyncSchedule;
// Shifts the VSYNC phase during certain transactions and refresh rate changes.
const sp<VsyncModulator> mVsyncModulator;
@@ -380,23 +411,35 @@
display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors
GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
- ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock)
+ // TODO (b/266715559): Store in the same map as mRefreshRateSelectors.
+ display::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<VsyncSchedule>> mVsyncSchedules
+ GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+
+ ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
GUARDED_BY(kMainThreadContext);
- RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) {
+ RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) {
std::scoped_lock lock(mDisplayLock);
- return leaderSelectorPtrLocked();
+ return pacesetterSelectorPtrLocked();
}
- RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) {
+ RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) {
ftl::FakeGuard guard(kMainThreadContext);
- const RefreshRateSelectorPtr noLeader;
- return mLeaderDisplayId
- .and_then([this](PhysicalDisplayId leaderId)
+ const RefreshRateSelectorPtr noPacesetter;
+ return mPacesetterDisplayId
+ .and_then([this](PhysicalDisplayId pacesetterId)
REQUIRES(mDisplayLock, kMainThreadContext) {
- return mRefreshRateSelectors.get(leaderId);
+ return mRefreshRateSelectors.get(pacesetterId);
})
- .value_or(std::cref(noLeader));
+ .value_or(std::cref(noPacesetter));
+ }
+
+ std::shared_ptr<const VsyncSchedule> getVsyncScheduleLocked(
+ std::optional<PhysicalDisplayId> idOpt = std::nullopt) const REQUIRES(mDisplayLock);
+ std::shared_ptr<VsyncSchedule> getVsyncScheduleLocked(
+ std::optional<PhysicalDisplayId> idOpt = std::nullopt) REQUIRES(mDisplayLock) {
+ return std::const_pointer_cast<VsyncSchedule>(
+ static_cast<const Scheduler*>(this)->getVsyncScheduleLocked(idOpt));
}
struct Policy {
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 9520131..77875e3 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -161,7 +161,8 @@
*/
class VSyncCallbackRegistration {
public:
- VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback, std::string callbackName);
+ VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch>, VSyncDispatch::Callback,
+ std::string callbackName);
~VSyncCallbackRegistration();
VSyncCallbackRegistration(VSyncCallbackRegistration&&);
@@ -177,7 +178,7 @@
CancelResult cancel();
private:
- std::reference_wrapper<VSyncDispatch> mDispatch;
+ std::shared_ptr<VSyncDispatch> mDispatch;
VSyncDispatch::CallbackToken mToken;
bool mValidToken;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 73d52cf..26389eb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -215,10 +215,10 @@
}
VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
- VSyncTracker& tracker, nsecs_t timerSlack,
- nsecs_t minVsyncDistance)
+ VsyncSchedule::TrackerPtr tracker,
+ nsecs_t timerSlack, nsecs_t minVsyncDistance)
: mTimeKeeper(std::move(tk)),
- mTracker(tracker),
+ mTracker(std::move(tracker)),
mTimerSlack(timerSlack),
mMinVsyncDistance(minVsyncDistance) {}
@@ -255,7 +255,7 @@
}
if (it != skipUpdateIt) {
- callback->update(mTracker, now);
+ callback->update(*mTracker, now);
}
auto const wakeupTime = *callback->wakeupTime();
if (!min || *min > wakeupTime) {
@@ -365,10 +365,10 @@
auto const rearmImminent = now > mIntendedWakeupTime;
if (CC_UNLIKELY(rearmImminent)) {
callback->addPendingWorkloadUpdate(scheduleTiming);
- return getExpectedCallbackTime(mTracker, now, scheduleTiming);
+ return getExpectedCallbackTime(*mTracker, now, scheduleTiming);
}
- const ScheduleResult result = callback->schedule(scheduleTiming, mTracker, now);
+ const ScheduleResult result = callback->schedule(scheduleTiming, *mTracker, now);
if (!result.has_value()) {
return {};
}
@@ -434,15 +434,15 @@
}
}
-VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
+VSyncCallbackRegistration::VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch> dispatch,
VSyncDispatch::Callback callback,
std::string callbackName)
- : mDispatch(dispatch),
- mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))),
+ : mDispatch(std::move(dispatch)),
+ mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))),
mValidToken(true) {}
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
- : mDispatch(other.mDispatch),
+ : mDispatch(std::move(other.mDispatch)),
mToken(std::move(other.mToken)),
mValidToken(std::move(other.mValidToken)) {
other.mValidToken = false;
@@ -457,28 +457,28 @@
}
VSyncCallbackRegistration::~VSyncCallbackRegistration() {
- if (mValidToken) mDispatch.get().unregisterCallback(mToken);
+ if (mValidToken) mDispatch->unregisterCallback(mToken);
}
ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mValidToken) {
return std::nullopt;
}
- return mDispatch.get().schedule(mToken, scheduleTiming);
+ return mDispatch->schedule(mToken, scheduleTiming);
}
ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mValidToken) {
return std::nullopt;
}
- return mDispatch.get().update(mToken, scheduleTiming);
+ return mDispatch->update(mToken, scheduleTiming);
}
CancelResult VSyncCallbackRegistration::cancel() {
if (!mValidToken) {
return CancelResult::Error;
}
- return mDispatch.get().cancel(mToken);
+ return mDispatch->cancel(mToken);
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index c3af136..6499d69 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -26,11 +26,11 @@
#include <android-base/thread_annotations.h>
#include "VSyncDispatch.h"
+#include "VsyncSchedule.h"
namespace android::scheduler {
class TimeKeeper;
-class VSyncTracker;
// VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
// VSyncDispatchTimerQueue hoisted to public for unit testing.
@@ -120,8 +120,8 @@
// should be grouped into one wakeup.
// \param[in] minVsyncDistance The minimum distance between two vsync estimates before the
// vsyncs are considered the same vsync event.
- VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper>, VSyncTracker&, nsecs_t timerSlack,
- nsecs_t minVsyncDistance);
+ VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper>, VsyncSchedule::TrackerPtr,
+ nsecs_t timerSlack, nsecs_t minVsyncDistance);
~VSyncDispatchTimerQueue();
CallbackToken registerCallback(Callback, std::string callbackName) final;
@@ -148,7 +148,7 @@
static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
std::unique_ptr<TimeKeeper> const mTimeKeeper;
- VSyncTracker& mTracker;
+ VsyncSchedule::TrackerPtr mTracker;
nsecs_t const mTimerSlack;
nsecs_t const mMinVsyncDistance;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index de7b338..e969fdc 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -31,6 +31,7 @@
#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
+#include <ftl/concat.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
@@ -45,9 +46,10 @@
VSyncPredictor::~VSyncPredictor() = default;
-VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize,
+VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize,
size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
- : mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
+ : mId(id),
+ mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
kHistorySize(historySize),
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
@@ -57,12 +59,12 @@
inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const {
if (CC_UNLIKELY(mTraceOn)) {
- ATRACE_INT64(name, value);
+ traceInt64(name, value);
}
}
inline void VSyncPredictor::traceInt64(const char* name, int64_t value) const {
- ATRACE_INT64(name, value);
+ ATRACE_INT64(ftl::Concat(ftl::truncated<14>(name), " ", mId.value).c_str(), value);
}
inline size_t VSyncPredictor::next(size_t i) const {
@@ -214,8 +216,8 @@
it->second = {anticipatedPeriod, intercept};
- ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
- anticipatedPeriod, intercept);
+ ALOGV("model update ts %" PRIu64 ": %" PRId64 " slope: %" PRId64 " intercept: %" PRId64,
+ mId.value, timestamp, anticipatedPeriod, intercept);
return true;
}
@@ -331,7 +333,7 @@
}
void VSyncPredictor::setRenderRate(Fps fps) {
- ALOGV("%s: %s", __func__, to_string(fps).c_str());
+ ALOGV("%s %s: %s", __func__, to_string(mId).c_str(), to_string(fps).c_str());
std::lock_guard lock(mMutex);
mRenderRate = fps;
}
@@ -347,7 +349,7 @@
}
void VSyncPredictor::setPeriod(nsecs_t period) {
- ATRACE_CALL();
+ ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str());
traceInt64("VSP-setPeriod", period);
std::lock_guard lock(mMutex);
@@ -376,7 +378,6 @@
mTimestamps.clear();
mLastTimestampIndex = 0;
}
- mLastVsyncSequence.reset();
}
bool VSyncPredictor::needsMoreSamples() const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index cd5d9ef..c01c44d 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -21,6 +21,7 @@
#include <vector>
#include <android-base/thread_annotations.h>
+#include <ui/DisplayId.h>
#include "VSyncTracker.h"
@@ -29,14 +30,15 @@
class VSyncPredictor : public VSyncTracker {
public:
/*
+ * \param [in] PhysicalDisplayid The display this corresponds to.
* \param [in] idealPeriod The initial ideal period to use.
* \param [in] historySize The internal amount of entries to store in the model.
* \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
* predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
* samples that fall outlierTolerancePercent from an anticipated vsync event.
*/
- VSyncPredictor(nsecs_t idealPeriod, size_t historySize, size_t minimumSamplesForPrediction,
- uint32_t outlierTolerancePercent);
+ VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize,
+ size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent);
~VSyncPredictor();
bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
@@ -76,6 +78,8 @@
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
void clearTimestamps() REQUIRES(mMutex);
+ const PhysicalDisplayId mId;
+
inline void traceInt64If(const char* name, int64_t value) const;
inline void traceInt64(const char* name, int64_t value) const;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index b5f212e..2938aa3 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -21,6 +21,8 @@
#include <assert.h>
#include <cutils/properties.h>
+#include <ftl/concat.h>
+#include <gui/TraceUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
@@ -39,12 +41,13 @@
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker,
- size_t pendingFenceLimit, bool supportKernelIdleTimer)
- : mClock(std::move(clock)),
+VSyncReactor::VSyncReactor(PhysicalDisplayId id, std::unique_ptr<Clock> clock,
+ VSyncTracker& tracker, size_t pendingFenceLimit,
+ bool supportKernelIdleTimer)
+ : mId(id),
+ mClock(std::move(clock)),
mTracker(tracker),
mPendingLimit(pendingFenceLimit),
- // TODO(adyabr): change mSupportKernelIdleTimer when the active display changes
mSupportKernelIdleTimer(supportKernelIdleTimer) {}
VSyncReactor::~VSyncReactor() = default;
@@ -114,7 +117,7 @@
}
void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
- ATRACE_CALL();
+ ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
mMoreSamplesNeeded = true;
@@ -122,18 +125,18 @@
}
void VSyncReactor::endPeriodTransition() {
- ATRACE_CALL();
+ ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
mPeriodTransitioningTo.reset();
mPeriodConfirmationInProgress = false;
mLastHwVsync.reset();
}
-void VSyncReactor::startPeriodTransition(nsecs_t period) {
- ATRACE_INT64("VSR-startPeriodTransition", period);
+void VSyncReactor::startPeriodTransition(nsecs_t period, bool force) {
+ ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), period);
std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) {
+ if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod() && !force) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
@@ -181,7 +184,7 @@
std::lock_guard lock(mMutex);
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
- ATRACE_NAME("VSR: period confirmed");
+ ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value);
if (mPeriodTransitioningTo) {
mTracker.setPeriod(*mPeriodTransitioningTo);
*periodFlushed = true;
@@ -195,12 +198,12 @@
endPeriodTransition();
mMoreSamplesNeeded = mTracker.needsMoreSamples();
} else if (mPeriodConfirmationInProgress) {
- ATRACE_NAME("VSR: still confirming period");
+ ATRACE_FORMAT("VSR %" PRIu64 ": still confirming period", mId.value);
mLastHwVsync = timestamp;
mMoreSamplesNeeded = true;
*periodFlushed = false;
} else {
- ATRACE_NAME("VSR: adding sample");
+ ATRACE_FORMAT("VSR %" PRIu64 ": adding sample", mId.value);
*periodFlushed = false;
mTracker.addVsyncTimestamp(timestamp);
mMoreSamplesNeeded = mTracker.needsMoreSamples();
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 4501487..f230242 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -22,6 +22,7 @@
#include <vector>
#include <android-base/thread_annotations.h>
+#include <ui/DisplayId.h>
#include <ui/FenceTime.h>
#include <scheduler/TimeKeeper.h>
@@ -37,14 +38,14 @@
// TODO (b/145217110): consider renaming.
class VSyncReactor : public VsyncController {
public:
- VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, size_t pendingFenceLimit,
- bool supportKernelIdleTimer);
+ VSyncReactor(PhysicalDisplayId, std::unique_ptr<Clock> clock, VSyncTracker& tracker,
+ size_t pendingFenceLimit, bool supportKernelIdleTimer);
~VSyncReactor();
bool addPresentFence(std::shared_ptr<FenceTime>) final;
void setIgnorePresentFences(bool ignore) final;
- void startPeriodTransition(nsecs_t period) final;
+ void startPeriodTransition(nsecs_t period, bool force) final;
bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed) final;
@@ -61,6 +62,7 @@
bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
REQUIRES(mMutex);
+ const PhysicalDisplayId mId;
std::unique_ptr<Clock> const mClock;
VSyncTracker& mTracker;
size_t const mPendingLimit;
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 726a420..9177899 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -63,8 +63,9 @@
* itself. The controller will end the period transition internally.
*
* \param [in] period The period that the system is changing into.
+ * \param [in] force True to recalibrate even if period matches the existing period.
*/
- virtual void startPeriodTransition(nsecs_t period) = 0;
+ virtual void startPeriodTransition(nsecs_t period, bool force) = 0;
/*
* Tells the tracker to stop using present fences to get a vsync signal.
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index c9af4c2..586357f 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -187,9 +187,9 @@
static_cast<void>(updateVsyncConfigLocked());
}
-bool VsyncModulator::isVsyncConfigDefault() const {
+bool VsyncModulator::isVsyncConfigEarly() const {
std::lock_guard<std::mutex> lock(mMutex);
- return getNextVsyncConfigType() == VsyncConfigType::Late;
+ return getNextVsyncConfigType() != VsyncConfigType::Late;
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index dc4dafd..be0d334 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -53,8 +53,12 @@
explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
+ bool isVsyncConfigEarly() const EXCLUDES(mMutex);
+
VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
+ void cancelRefreshRateChange() { mRefreshRateChangePending = false; }
+
[[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
// Changes offsets in response to transaction flags or commit.
@@ -72,8 +76,6 @@
[[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
- [[nodiscard]] bool isVsyncConfigDefault() const;
-
protected:
// Called from unit tests as well
void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 5245556..84671ae 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -42,8 +42,8 @@
}
public:
- explicit PredictedVsyncTracer(VsyncDispatch& dispatch)
- : mRegistration(dispatch, makeVsyncCallback(), __func__) {
+ explicit PredictedVsyncTracer(std::shared_ptr<VsyncDispatch> dispatch)
+ : mRegistration(std::move(dispatch), makeVsyncCallback(), __func__) {
schedule();
}
@@ -54,16 +54,19 @@
VSyncCallbackRegistration mRegistration;
};
-VsyncSchedule::VsyncSchedule(FeatureFlags features)
- : mTracker(createTracker()),
- mDispatch(createDispatch(*mTracker)),
- mController(createController(*mTracker, features)),
+VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features)
+ : mId(id),
+ mTracker(createTracker(id)),
+ mDispatch(createDispatch(mTracker)),
+ mController(createController(id, *mTracker, features)),
mTracer(features.test(Feature::kTracePredictedVsync)
- ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
+ ? std::make_unique<PredictedVsyncTracer>(mDispatch)
: nullptr) {}
-VsyncSchedule::VsyncSchedule(TrackerPtr tracker, DispatchPtr dispatch, ControllerPtr controller)
- : mTracker(std::move(tracker)),
+VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, TrackerPtr tracker, DispatchPtr dispatch,
+ ControllerPtr controller)
+ : mId(id),
+ mTracker(std::move(tracker)),
mDispatch(std::move(dispatch)),
mController(std::move(controller)) {}
@@ -95,45 +98,46 @@
mDispatch->dump(out);
}
-VsyncSchedule::TrackerPtr VsyncSchedule::createTracker() {
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) {
// TODO(b/144707443): Tune constants.
constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
constexpr size_t kHistorySize = 20;
constexpr size_t kMinSamplesForPrediction = 6;
constexpr uint32_t kDiscardOutlierPercent = 20;
- return std::make_unique<VSyncPredictor>(kInitialPeriod, kHistorySize, kMinSamplesForPrediction,
- kDiscardOutlierPercent);
+ return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize,
+ kMinSamplesForPrediction, kDiscardOutlierPercent);
}
-VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(VsyncTracker& tracker) {
+VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) {
using namespace std::chrono_literals;
// TODO(b/144707443): Tune constants.
constexpr std::chrono::nanoseconds kGroupDispatchWithin = 500us;
constexpr std::chrono::nanoseconds kSnapToSameVsyncWithin = 3ms;
- return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+ return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), std::move(tracker),
kGroupDispatchWithin.count(),
kSnapToSameVsyncWithin.count());
}
-VsyncSchedule::ControllerPtr VsyncSchedule::createController(VsyncTracker& tracker,
+VsyncSchedule::ControllerPtr VsyncSchedule::createController(PhysicalDisplayId id,
+ VsyncTracker& tracker,
FeatureFlags features) {
// TODO(b/144707443): Tune constants.
constexpr size_t kMaxPendingFences = 20;
const bool hasKernelIdleTimer = features.test(Feature::kKernelIdleTimer);
- auto reactor = std::make_unique<VSyncReactor>(std::make_unique<SystemClock>(), tracker,
+ auto reactor = std::make_unique<VSyncReactor>(id, std::make_unique<SystemClock>(), tracker,
kMaxPendingFences, hasKernelIdleTimer);
reactor->setIgnorePresentFences(!features.test(Feature::kPresentFences));
return reactor;
}
-void VsyncSchedule::startPeriodTransition(ISchedulerCallback& callback, Period period) {
+void VsyncSchedule::startPeriodTransition(ISchedulerCallback& callback, Period period, bool force) {
std::lock_guard<std::mutex> lock(mHwVsyncLock);
- mController->startPeriodTransition(period.ns());
+ mController->startPeriodTransition(period.ns(), force);
enableHardwareVsyncLocked(callback);
}
@@ -165,17 +169,23 @@
void VsyncSchedule::enableHardwareVsyncLocked(ISchedulerCallback& callback) {
if (mHwVsyncState == HwVsyncState::Disabled) {
getTracker().resetModel();
- callback.setVsyncEnabled(true);
+ callback.setVsyncEnabled(mId, true);
mHwVsyncState = HwVsyncState::Enabled;
}
}
void VsyncSchedule::disableHardwareVsync(ISchedulerCallback& callback, bool disallow) {
std::lock_guard<std::mutex> lock(mHwVsyncLock);
- if (mHwVsyncState == HwVsyncState::Enabled) {
- callback.setVsyncEnabled(false);
+ switch (mHwVsyncState) {
+ case HwVsyncState::Enabled:
+ callback.setVsyncEnabled(mId, false);
+ [[fallthrough]];
+ case HwVsyncState::Disabled:
+ mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled;
+ break;
+ case HwVsyncState::Disallowed:
+ break;
}
- mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled;
}
bool VsyncSchedule::isHardwareVsyncAllowed(bool makeAllowed) {
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index d88f1d1..763d058 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -20,13 +20,16 @@
#include <string>
#include <ThreadContext.h>
+#include <android-base/thread_annotations.h>
#include <ftl/enum.h>
#include <ftl/optional.h>
#include <scheduler/Features.h>
#include <scheduler/Time.h>
+#include <ui/DisplayId.h>
namespace android {
class EventThreadTest;
+class VsyncScheduleTest;
}
namespace android::fuzz {
@@ -48,7 +51,7 @@
// Schedule that synchronizes to hardware VSYNC of a physical display.
class VsyncSchedule {
public:
- explicit VsyncSchedule(FeatureFlags);
+ VsyncSchedule(PhysicalDisplayId, FeatureFlags);
~VsyncSchedule();
Period period() const;
@@ -59,7 +62,9 @@
// enable hardware VSYNCs in order to calibrate.
//
// \param [in] period The period that the system is changing into.
- void startPeriodTransition(ISchedulerCallback&, Period period);
+ // \param [in] force True to force a transition even if it is not a
+ // change.
+ void startPeriodTransition(ISchedulerCallback&, Period period, bool force);
// Pass a VSYNC sample to VsyncController. Return true if
// VsyncController detected that the VSYNC period changed. Enable or disable
@@ -72,8 +77,13 @@
VsyncTracker& getTracker() { return *mTracker; }
VsyncController& getController() { return *mController; }
+ // TODO(b/185535769): Once these are hidden behind the API, they may no
+ // longer need to be shared_ptrs.
+ using DispatchPtr = std::shared_ptr<VsyncDispatch>;
+ using TrackerPtr = std::shared_ptr<VsyncTracker>;
+
// TODO(b/185535769): Remove once VsyncSchedule owns all registrations.
- VsyncDispatch& getDispatch() { return *mDispatch; }
+ DispatchPtr getDispatch() { return mDispatch; }
void dump(std::string&) const;
@@ -82,7 +92,8 @@
void enableHardwareVsync(ISchedulerCallback&) EXCLUDES(mHwVsyncLock);
// Disable hardware VSYNCs. If `disallow` is true, future calls to
- // enableHardwareVsync are ineffective until allowHardwareVsync is called.
+ // enableHardwareVsync are ineffective until isHardwareVsyncAllowed is
+ // called with `makeAllowed` set to true.
void disableHardwareVsync(ISchedulerCallback&, bool disallow) EXCLUDES(mHwVsyncLock);
// If true, enableHardwareVsync can enable hardware VSYNC (if not already
@@ -93,21 +104,21 @@
bool getPendingHardwareVsyncState() const REQUIRES(kMainThreadContext);
-private:
- friend class TestableScheduler;
- friend class android::EventThreadTest;
- friend class android::fuzz::SchedulerFuzzer;
-
- using TrackerPtr = std::unique_ptr<VsyncTracker>;
- using DispatchPtr = std::unique_ptr<VsyncDispatch>;
+protected:
using ControllerPtr = std::unique_ptr<VsyncController>;
// For tests.
- VsyncSchedule(TrackerPtr, DispatchPtr, ControllerPtr);
+ VsyncSchedule(PhysicalDisplayId, TrackerPtr, DispatchPtr, ControllerPtr);
- static TrackerPtr createTracker();
- static DispatchPtr createDispatch(VsyncTracker&);
- static ControllerPtr createController(VsyncTracker&, FeatureFlags);
+private:
+ friend class TestableScheduler;
+ friend class android::EventThreadTest;
+ friend class android::VsyncScheduleTest;
+ friend class android::fuzz::SchedulerFuzzer;
+
+ static TrackerPtr createTracker(PhysicalDisplayId);
+ static DispatchPtr createDispatch(TrackerPtr);
+ static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags);
void enableHardwareVsyncLocked(ISchedulerCallback&) REQUIRES(mHwVsyncLock);
@@ -135,6 +146,7 @@
class PredictedVsyncTracer;
using TracerPtr = std::unique_ptr<PredictedVsyncTracer>;
+ const PhysicalDisplayId mId;
const TrackerPtr mTracker;
const DispatchPtr mDispatch;
const ControllerPtr mController;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1393eb8..c9d17d5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -105,6 +105,7 @@
#include <memory>
#include <mutex>
#include <optional>
+#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
@@ -130,6 +131,7 @@
#include "FrameTracer/FrameTracer.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerLifecycleManager.h"
#include "FrontEnd/LayerSnapshot.h"
#include "HdrLayerInfoReporter.h"
#include "Layer.h"
@@ -292,6 +294,13 @@
displayHdrCapabilities.getDesiredMinLuminance()};
}
+uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) {
+ if (!surfaceControl) {
+ return UNASSIGNED_LAYER_ID;
+ }
+ return LayerHandle::getLayerId(surfaceControl->getHandle());
+}
+
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -456,6 +465,7 @@
android::hardware::details::setTrebleTestingOverride(true);
}
+ // TODO (b/270966065) Update the HWC based refresh rate overlay to support spinner
mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
mRefreshRateOverlayRenderRate =
property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
@@ -473,9 +483,9 @@
{.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
.early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)};
mLayerLifecycleManagerEnabled =
- base::GetBoolProperty("debug.sf.enable_layer_lifecycle_manager"s, false);
+ base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, false);
mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
- base::GetBoolProperty("debug.sf.enable_legacy_frontend"s, true);
+ base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -496,14 +506,13 @@
// the window manager died on us. prepare its eulogy.
mBootFinished = false;
- // Sever the link to inputflinger since it's gone as well.
- static_cast<void>(mScheduler->schedule(
- [this] { mInputFlinger.clear(); }));
+ static_cast<void>(mScheduler->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
+ // Sever the link to inputflinger since it's gone as well.
+ mInputFlinger.clear();
- // restore initial conditions (default device unblank, etc)
- initializeDisplays();
+ initializeDisplays();
+ }));
- // restart the boot-animation
startBootAnim();
}
@@ -872,7 +881,9 @@
mDrawingState = mCurrentState;
onActiveDisplayChangedLocked(nullptr, *display);
- initializeDisplays();
+
+ static_cast<void>(mScheduler->schedule(
+ [this]() FTL_FAKE_GUARD(kMainThreadContext) { initializeDisplays(); }));
mPowerAdvisor->init();
@@ -1130,21 +1141,33 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* outStats) {
+status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken,
+ DisplayStatInfo* outStats) {
if (!outStats) {
return BAD_VALUE;
}
- const auto& schedule = mScheduler->getVsyncSchedule();
- outStats->vsyncTime = schedule.vsyncDeadlineAfter(TimePoint::now()).ns();
- outStats->vsyncPeriod = schedule.period().ns();
+ std::optional<PhysicalDisplayId> displayIdOpt;
+ {
+ Mutex::Autolock lock(mStateLock);
+ displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
+ }
+
+ if (!displayIdOpt) {
+ ALOGE("%s: Invalid physical display token %p", __func__, displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+ const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt);
+ outStats->vsyncTime = schedule->vsyncDeadlineAfter(TimePoint::now()).ns();
+ outStats->vsyncPeriod = schedule->period().ns();
return NO_ERROR;
}
void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
ATRACE_CALL();
- auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId());
+ const auto displayId = request.mode.modePtr->getPhysicalDisplayId();
+ const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
ALOGW("%s: display is no longer valid", __func__);
return;
@@ -1157,23 +1180,25 @@
force)) {
case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
// Set the render rate as setDesiredActiveMode updated it.
- mScheduler->setRenderRate(display->refreshRateSelector().getActiveMode().fps);
+ mScheduler->setRenderRate(displayId,
+ display->refreshRateSelector().getActiveMode().fps);
// Schedule a new frame to initiate the display mode switch.
scheduleComposite(FrameHint::kNone);
// Start receiving vsync samples now, so that we can detect a period
// switch.
- mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps());
+ mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */,
+ mode.modePtr->getFps());
+
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
- mScheduler->modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
-
+ mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated);
updatePhaseConfiguration(mode.fps);
mScheduler->setModeChangePending(true);
break;
case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
- mScheduler->setRenderRate(mode.fps);
+ mScheduler->setRenderRate(displayId, mode.fps);
updatePhaseConfiguration(mode.fps);
mRefreshRateStats->setRefreshRate(mode.fps);
if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
@@ -1289,11 +1314,14 @@
}
void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) {
- const auto displayFps = display->getDesiredActiveMode()->modeOpt->modePtr->getFps();
- const auto renderFps = display->getDesiredActiveMode()->modeOpt->fps;
+ const auto desiredActiveMode = display->getDesiredActiveMode();
+ const auto& modeOpt = desiredActiveMode->modeOpt;
+ const auto displayId = modeOpt->modePtr->getPhysicalDisplayId();
+ const auto displayFps = modeOpt->modePtr->getFps();
+ const auto renderFps = modeOpt->fps;
clearDesiredActiveModeState(display);
- mScheduler->resyncToHardwareVsync(true, displayFps);
- mScheduler->setRenderRate(renderFps);
+ mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, displayFps);
+ mScheduler->setRenderRate(displayId, renderFps);
updatePhaseConfiguration(renderFps);
}
@@ -1571,8 +1599,8 @@
const auto aidlConversionCapability = getHwComposer().getHdrConversionCapabilities();
for (auto capability : aidlConversionCapability) {
gui::HdrConversionCapability tempCapability;
- tempCapability.sourceType = static_cast<int>(capability.sourceType.hdr);
- tempCapability.outputType = static_cast<int>(capability.outputType->hdr);
+ tempCapability.sourceType = static_cast<int>(capability.sourceType);
+ tempCapability.outputType = static_cast<int>(capability.outputType);
tempCapability.addsLatency = capability.addsLatency;
hdrConversionCapabilities->push_back(tempCapability);
}
@@ -2031,27 +2059,16 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- const std::string tracePeriod = [vsyncPeriod]() {
- if (ATRACE_ENABLED() && vsyncPeriod) {
- std::stringstream ss;
- ss << "(" << *vsyncPeriod << ")";
- return ss.str();
- }
- return std::string();
- }();
- ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
+ ATRACE_NAME(vsyncPeriod
+ ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str()
+ : ftl::Concat(__func__, ' ', hwcDisplayId).c_str());
Mutex::Autolock lock(mStateLock);
-
- if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp);
- displayIdOpt != mActiveDisplayId) {
- // Ignore VSYNC for invalid/inactive displays.
- return;
- }
-
- const bool periodFlushed = mScheduler->addResyncSample(timestamp, vsyncPeriod);
- if (periodFlushed) {
- mScheduler->modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
+ if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp)) {
+ if (mScheduler->addResyncSample(*displayIdOpt, timestamp, vsyncPeriod)) {
+ // period flushed
+ mScheduler->modulateVsync(displayIdOpt, &VsyncModulator::onRefreshRateChangeCompleted);
+ }
}
}
@@ -2092,23 +2109,40 @@
mScheduler->forceNextResync();
}
-void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) {
- // TODO(b/202734676) update refresh rate value on the RefreshRateOverlay
+void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
+ ATRACE_CALL();
+ if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) {
+ const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos);
+ ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue());
+ static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ {
+ {
+ const auto display = getDisplayDeviceLocked(*displayId);
+ FTL_FAKE_GUARD(kMainThreadContext,
+ display->updateRefreshRateOverlayRate(fps,
+ display->getActiveMode()
+ .fps,
+ /* setByHwc */ true));
+ }
+ }
+ }));
+ }
}
-void SurfaceFlinger::setVsyncEnabled(bool enabled) {
- ATRACE_CALL();
+void SurfaceFlinger::setVsyncEnabled(PhysicalDisplayId id, bool enabled) {
+ const char* const whence = __func__;
+ ATRACE_FORMAT("%s (%d) for %" PRIu64, whence, enabled, id.value);
// On main thread to avoid race conditions with display power state.
static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
{
ftl::FakeGuard guard(kMainThreadContext);
- mScheduler->getVsyncSchedule().setPendingHardwareVsyncState(enabled);
+ mScheduler->getVsyncSchedule(id)->setPendingHardwareVsyncState(enabled);
}
- if (const auto display = getDefaultDisplayDeviceLocked();
- display && display->isPoweredOn()) {
- setHWCVsyncEnabled(display->getPhysicalId(), enabled);
+ ATRACE_FORMAT("%s (%d) for %" PRIu64 " (main thread)", whence, enabled, id.value);
+ if (const auto display = getDisplayDeviceLocked(id); display && display->isPoweredOn()) {
+ setHWCVsyncEnabled(id, enabled);
}
}));
}
@@ -2135,13 +2169,13 @@
TimePoint SurfaceFlinger::calculateExpectedPresentTime(TimePoint frameTime) const {
const auto& schedule = mScheduler->getVsyncSchedule();
- const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(frameTime);
+ 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();
+ return vsyncDeadline + schedule->period();
}
void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
@@ -2151,7 +2185,7 @@
}
}
-bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, LifecycleUpdate& update,
+bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
bool transactionsFlushed,
bool& outTransactionsAreEmpty) {
bool needsTraversal = false;
@@ -2171,7 +2205,39 @@
return mustComposite;
}
-bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update,
+void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) {
+ using Changes = frontend::RequestedLayerState::Changes;
+ if (snapshot.path.isClone() ||
+ !snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation)) {
+ return;
+ }
+
+ const auto layerProps = scheduler::LayerProps{
+ .visible = snapshot.isVisible,
+ .bounds = snapshot.geomLayerBounds,
+ .transform = snapshot.geomLayerTransform,
+ .setFrameRateVote = snapshot.frameRate,
+ .frameRateSelectionPriority = snapshot.frameRateSelectionPriority,
+ };
+
+ auto it = mLegacyLayers.find(snapshot.sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+ snapshot.getDebugString().c_str());
+
+ if (snapshot.changes.test(Changes::Animation)) {
+ it->second->recordLayerHistoryAnimationTx(layerProps);
+ }
+
+ if (snapshot.changes.test(Changes::FrameRate)) {
+ it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps);
+ }
+
+ if (snapshot.changes.test(Changes::Buffer)) {
+ it->second->recordLayerHistoryBufferUpdate(layerProps);
+ }
+}
+
+bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update,
bool transactionsFlushed, bool& outTransactionsAreEmpty) {
using Changes = frontend::RequestedLayerState::Changes;
ATRACE_NAME("updateLayerSnapshots");
@@ -2192,23 +2258,27 @@
mLayerLifecycleManager.getDestroyedLayers());
}
- applyAndCommitDisplayTransactionStates(update.transactions);
+ bool mustComposite = false;
+ mustComposite |= applyAndCommitDisplayTransactionStates(update.transactions);
{
ATRACE_NAME("LayerSnapshotBuilder:update");
- frontend::LayerSnapshotBuilder::Args args{.root = mLayerHierarchyBuilder.getHierarchy(),
- .layerLifecycleManager = mLayerLifecycleManager,
- .displays = mFrontEndDisplayInfos,
- .displayChanges = mFrontEndDisplayInfosChanged,
- .globalShadowSettings =
- mDrawingState.globalShadowSettings,
- .supportsBlur = mSupportsBlur,
- .forceFullDamage = mForceFullDamage};
+ frontend::LayerSnapshotBuilder::Args
+ args{.root = mLayerHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLayerLifecycleManager,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = mFrontEndDisplayInfosChanged,
+ .globalShadowSettings = mDrawingState.globalShadowSettings,
+ .supportsBlur = mSupportsBlur,
+ .forceFullDamage = mForceFullDamage,
+ .supportedLayerGenericMetadata =
+ getHwComposer().getSupportedLayerGenericMetadata(),
+ .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
mLayerSnapshotBuilder.update(args);
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input |
- Changes::Hierarchy)) {
+ Changes::Hierarchy | Changes::Visibility)) {
mUpdateInputInfo = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
@@ -2216,23 +2286,38 @@
mVisibleRegionsDirty = true;
}
outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
- const bool mustComposite = mLayerLifecycleManager.getGlobalChanges().get() != 0;
- {
- ATRACE_NAME("LLM:commitChanges");
- mLayerLifecycleManager.commitChanges();
- }
+ mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
+ bool newDataLatched = false;
if (!mLegacyFrontEndEnabled) {
ATRACE_NAME("DisplayCallbackAndStatsUpdates");
applyTransactions(update.transactions, vsyncId);
+ const nsecs_t latchTime = systemTime();
+ bool unused = false;
- bool newDataLatched = false;
- for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
- if (!snapshot->changes.test(Changes::Buffer)) continue;
- auto it = mLegacyLayers.find(snapshot->sequence);
+ for (auto& layer : mLayerLifecycleManager.getLayers()) {
+ if (layer->changes.test(frontend::RequestedLayerState::Changes::Created) &&
+ layer->bgColorLayer) {
+ sp<Layer> bgColorLayer = getFactory().createEffectLayer(
+ LayerCreationArgs(this, nullptr, layer->name,
+ ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(),
+ std::make_optional(layer->parentId), true));
+ mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
+ }
+ if (!layer->hasReadyFrame()) continue;
+
+ auto it = mLegacyLayers.find(layer->id);
LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
+ layer->getDebugString().c_str());
+ const bool bgColorOnly =
+ !layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
+ it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
mLayersWithQueuedFrames.emplace(it->second);
+ }
+
+ for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ updateLayerHistory(*snapshot);
+ if (!snapshot->hasReadyFrame) continue;
newDataLatched = true;
if (!snapshot->isVisible) break;
@@ -2245,13 +2330,20 @@
mLegacyLayers.erase(destroyedLayer->id);
}
+ {
+ ATRACE_NAME("LLM:commitChanges");
+ mLayerLifecycleManager.commitChanges();
+ }
+
+ commitTransactions();
+
// enter boot animation on first buffer latch
if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
ALOGI("Enter boot animation");
mBootStage = BootStage::BOOTANIMATION;
}
- commitTransactions();
}
+ mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched;
return mustComposite;
}
@@ -2272,7 +2364,7 @@
ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
- const Period vsyncPeriod = mScheduler->getVsyncSchedule().period();
+ 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
@@ -2395,9 +2487,14 @@
Fps::fromPeriodNsecs(vsyncPeriod.ns()));
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
- LifecycleUpdate updates;
+ frontend::Update updates;
if (flushTransactions) {
updates = flushLifecycleUpdates();
+ if (mTransactionTracing) {
+ mTransactionTracing->addCommittedTransactions(vsyncId.value, frameTime.ns(),
+ updates, mFrontEndDisplayInfos,
+ mFrontEndDisplayInfosChanged);
+ }
}
bool transactionsAreEmpty;
if (mLegacyFrontEndEnabled) {
@@ -2439,7 +2536,7 @@
if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and tracing should only be enabled for debugging.
- mLayerTracing.notify(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
+ addToLayerTracing(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
}
mLastCommittedVsyncId = vsyncId;
@@ -2522,7 +2619,7 @@
refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
}
- const auto prevVsyncTime = mExpectedPresentTime - mScheduler->getVsyncSchedule().period();
+ const auto prevVsyncTime = mExpectedPresentTime - mScheduler->getVsyncSchedule()->period();
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
@@ -2607,12 +2704,12 @@
// TODO(b/160583065): Enable skip validation when SF caches all client composition layers.
const bool hasGpuUseOrReuse =
mCompositionCoverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse);
- mScheduler->modulateVsync(&VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
+ mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
mLayersWithQueuedFrames.clear();
if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and should only be used for debugging.
- mLayerTracing.notify(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
+ addToLayerTracing(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
}
if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
@@ -2751,9 +2848,9 @@
? mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime)
: Duration::zero();
- const auto& schedule = mScheduler->getVsyncSchedule();
- const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(presentTime);
- const Period vsyncPeriod = schedule.period();
+ const auto schedule = mScheduler->getVsyncSchedule();
+ const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
+ const Period vsyncPeriod = schedule->period();
const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset;
const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
@@ -2802,7 +2899,12 @@
const auto* outputLayer =
compositionDisplay->getOutputLayerForLayer(layerFe);
if (outputLayer) {
- info.mergeDesiredRatio(snapshot.desiredSdrHdrRatio);
+ // TODO(b/267350616): Rename SdrHdrRatio -> HdrSdrRatio
+ // everywhere
+ const float desiredHdrSdrRatio = snapshot.desiredSdrHdrRatio <= 1.f
+ ? std::numeric_limits<float>::infinity()
+ : snapshot.desiredSdrHdrRatio;
+ info.mergeDesiredRatio(desiredHdrSdrRatio);
info.numberOfHdrLayers++;
const auto displayFrame = outputLayer->getState().displayFrame;
const int32_t area = displayFrame.width() * displayFrame.height();
@@ -2828,15 +2930,19 @@
mTimeStats->incrementTotalFrames();
mTimeStats->setPresentFenceGlobal(presentFenceTime);
- const bool isInternalDisplay = defaultDisplay &&
- FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)
- .get(defaultDisplay->getPhysicalId())
- .transform(&PhysicalDisplay::isInternal)
- .value_or(false);
-
- if (isInternalDisplay && defaultDisplay && defaultDisplay->getPowerMode() == hal::PowerMode::ON &&
- presentFenceTime->isValid()) {
- mScheduler->addPresentFence(std::move(presentFenceTime));
+ {
+ 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 isDisplayConnected =
@@ -2844,7 +2950,7 @@
if (!hasSyncFramework) {
if (isDisplayConnected && defaultDisplay->isPoweredOn()) {
- mScheduler->enableHardwareVsync();
+ mScheduler->enableHardwareVsync(defaultDisplay->getPhysicalId());
}
}
@@ -2887,14 +2993,19 @@
}
// We avoid any reverse traversal upwards so this shouldn't be too expensive
- mDrawingState.traverse([&](Layer* layer) {
+ traverseLegacyLayers([&](Layer* layer) {
if (!layer->hasTrustedPresentationListener()) {
return;
}
- const std::optional<const DisplayDevice*> displayOpt =
- layerStackToDisplay.get(layer->getLayerSnapshot()->outputFilter.layerStack);
+ const frontend::LayerSnapshot* snapshot = (mLayerLifecycleManagerEnabled)
+ ? mLayerSnapshotBuilder.getSnapshot(layer->sequence)
+ : layer->getLayerSnapshot();
+ std::optional<const DisplayDevice*> displayOpt = std::nullopt;
+ if (snapshot) {
+ displayOpt = layerStackToDisplay.get(snapshot->outputFilter.layerStack);
+ }
const DisplayDevice* display = displayOpt.value_or(nullptr);
- layer->updateTrustedPresentationState(display, layer->getLayerSnapshot(),
+ layer->updateTrustedPresentationState(display, snapshot,
nanoseconds_to_milliseconds(callTime), false);
});
}
@@ -2955,7 +3066,7 @@
// so we can call commitTransactionsLocked unconditionally.
// We clear the flags with mStateLock held to guarantee that
// mCurrentState won't change until the transaction is committed.
- mScheduler->modulateVsync(&VsyncModulator::onTransactionCommit);
+ mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit);
commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
mDebugInTransaction = 0;
@@ -3327,7 +3438,7 @@
}
if (display->isVirtual()) {
- display->adjustRefreshRate(mScheduler->getLeaderRefreshRate());
+ display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate());
}
mDisplays.try_emplace(displayToken, std::move(display));
@@ -3396,7 +3507,7 @@
// TODO(b/175678251) Call a listener instead.
if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
- updateActiveDisplayVsyncLocked(*display);
+ resetPhaseConfiguration(display->getActiveMode().fps);
}
}
return;
@@ -3430,9 +3541,11 @@
}
}
-void SurfaceFlinger::updateActiveDisplayVsyncLocked(const DisplayDevice& activeDisplay) {
+void SurfaceFlinger::resetPhaseConfiguration(Fps refreshRate) {
+ // Cancel the pending refresh rate change, if any, before updating the phase configuration.
+ mScheduler->vsyncModulator().cancelRefreshRateChange();
+
mVsyncConfiguration->reset();
- const Fps refreshRate = activeDisplay.getActiveMode().fps;
updatePhaseConfiguration(refreshRate);
mRefreshRateStats->setRefreshRate(refreshRate);
}
@@ -3578,6 +3691,10 @@
});
}
+ if (transactionFlags & eInputInfoUpdateNeeded) {
+ mUpdateInputInfo = true;
+ }
+
doCommitTransactions();
}
@@ -3781,10 +3898,9 @@
mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
static_cast<ISchedulerCallback&>(*this), features,
std::move(modulatorPtr));
- mScheduler->createVsyncSchedule(features);
mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
- setVsyncEnabled(false);
+ setVsyncEnabled(display->getPhysicalId(), false);
mScheduler->startTimers();
const auto configs = mVsyncConfiguration->getCurrentConfigs();
@@ -3800,7 +3916,7 @@
/* workDuration */ activeRefreshRate.getPeriod(),
/* readyDuration */ configs.late.sfWorkDuration);
- mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
+ mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(),
*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
mRegionSamplingThread =
@@ -3865,7 +3981,7 @@
for (Layer* offscreenLayer : mOffscreenLayers) {
offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
if (layer->clearTransactionFlags(eTransactionNeeded)) {
- layer->doTransaction(0, 0);
+ layer->doTransaction(0);
layer->commitChildList();
}
});
@@ -3901,7 +4017,7 @@
// second frame. But layer 0's second frame could be waiting on display.
mDrawingState.traverse([&](Layer* layer) {
if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) {
- const uint32_t flags = layer->doTransaction(0, latchTime);
+ const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion) {
mVisibleRegionsDirty = true;
}
@@ -3912,6 +4028,14 @@
mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
} else {
layer->useEmptyDamage();
+ if (!layer->hasBuffer()) {
+ // The last latch time is used to classify a missed frame as buffer stuffing
+ // instead of a missed frame. This is used to identify scenarios where we
+ // could not latch a buffer or apply a transaction due to backpressure.
+ // We only update the latch time for buffer less layers here, the latch time
+ // is updated for buffer layers when the buffer is latched.
+ layer->updateLastLatchTime(latchTime);
+ }
}
});
mForceTransactionDisplayChange = false;
@@ -3961,7 +4085,7 @@
return !mLayersWithQueuedFrames.empty() && newDataLatched;
}
-status_t SurfaceFlinger::addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle,
+status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
const sp<Layer>& layer, const wp<Layer>& parent,
uint32_t* outTransformHint) {
if (mNumLayers >= MAX_LAYERS) {
@@ -3991,11 +4115,15 @@
if (outTransformHint) {
*outTransformHint = mActiveDisplayTransformHint;
}
-
+ args.parentId = LayerHandle::getLayerId(args.parentHandle.promote());
+ args.layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
{
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
mCreatedLayers.emplace_back(layer, parent, args.addToRoot);
mNewLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
+ args.mirrorLayerHandle.clear();
+ args.parentHandle.clear();
+ mNewLayerArgs.emplace_back(std::move(args));
}
setTransactionFlags(eTransactionNeeded);
@@ -4014,11 +4142,12 @@
void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
const sp<IBinder>& applyToken, FrameHint frameHint) {
- mScheduler->modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
+ mScheduler->modulateVsync({}, &VsyncModulator::setTransactionSchedule, schedule, applyToken);
uint32_t transactionFlags = mTransactionFlags.fetch_or(mask);
ATRACE_INT("mTransactionFlags", transactionFlags);
if (const bool scheduled = transactionFlags & mask; !scheduled) {
+ mScheduler->resync();
scheduleCommit(frameHint);
} else if (frameHint == FrameHint::kActive) {
// Even if the next frame is already scheduled, we should reset the idle timer
@@ -4062,7 +4191,12 @@
const TransactionHandler::TransactionFlushState& flushState) {
using TransactionReadiness = TransactionHandler::TransactionReadiness;
auto ready = TransactionReadiness::Ready;
- flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool {
+ flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s,
+ const std::shared_ptr<
+ renderengine::
+ ExternalTexture>&
+ externalTexture)
+ -> bool {
sp<Layer> layer = LayerHandle::getLayer(s.surface);
const auto& transaction = *flushState.transaction;
// check for barrier frames
@@ -4072,7 +4206,8 @@
// don't wait on the barrier since we know that's stale information.
if (layer->getDrawingState().producerId > s.bufferData->producerId) {
layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener,
- s.bufferData->buffer, s.bufferData->frameNumber,
+ externalTexture->getBuffer(),
+ s.bufferData->frameNumber,
s.bufferData->acquireFence);
// 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.
@@ -4155,7 +4290,7 @@
bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
VsyncId vsyncId) {
- Mutex::Autolock _l(mStateLock);
+ Mutex::Autolock lock(mStateLock);
return applyTransactionsLocked(transactions, vsyncId);
}
@@ -4174,10 +4309,6 @@
transaction.listenerCallbacks, transaction.originPid,
transaction.originUid, transaction.id);
}
-
- if (mTransactionTracing) {
- mTransactionTracing->addCommittedTransactions(transactions, vsyncId.value);
- }
return needsTraversal;
}
@@ -4201,7 +4332,7 @@
return false;
}
- const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule().period() / 2;
+ const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule()->period() / 2;
return predictedPresentTime >= expectedPresentTime &&
predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
@@ -4236,9 +4367,8 @@
// We don't want to latch unsignaled if are in early / client composition
// as it leads to jank due to RenderEngine waiting for unsignaled buffer
// or window animations being slow.
- const auto isDefaultVsyncConfig = mScheduler->vsyncModulator().isVsyncConfigDefault();
- if (!isDefaultVsyncConfig) {
- ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; !isDefaultVsyncConfig)",
+ if (mScheduler->vsyncModulator().isVsyncConfigEarly()) {
+ ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; isVsyncConfigEarly)",
__func__);
return false;
}
@@ -4317,6 +4447,21 @@
layerName.c_str(), transactionId);
mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
}
+ resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
+ if (resolvedState.state.what & layer_state_t::eReparent) {
+ resolvedState.parentId =
+ getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
+ }
+ if (resolvedState.state.what & layer_state_t::eRelativeLayerChanged) {
+ resolvedState.relativeParentId =
+ getLayerIdFromSurfaceControl(resolvedState.state.relativeLayerSurfaceControl);
+ }
+ if (resolvedState.state.what & layer_state_t::eInputInfoChanged) {
+ wp<IBinder>& touchableRegionCropHandle =
+ resolvedState.state.windowInfoHandle->editInfo()->touchableRegionCropHandle;
+ resolvedState.touchCropId =
+ LayerHandle::getLayerId(touchableRegionCropHandle.promote());
+ }
}
TransactionState state{frameTimelineInfo,
@@ -4391,10 +4536,14 @@
}
if ((flags & eAnimation) && resolvedState.state.surface) {
if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
- using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
- mScheduler->recordLayerHistory(layer.get(),
- isAutoTimestamp ? 0 : desiredPresentTime,
- LayerUpdateType::AnimationTX);
+ const auto layerProps = scheduler::LayerProps{
+ .visible = layer->isVisible(),
+ .bounds = layer->getBounds(),
+ .transform = layer->getTransform(),
+ .setFrameRateVote = layer->getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+ };
+ layer->recordLayerHistoryAnimationTx(layerProps);
}
}
}
@@ -4437,7 +4586,7 @@
bool SurfaceFlinger::applyAndCommitDisplayTransactionStates(
std::vector<TransactionState>& transactions) {
- Mutex::Autolock _l(mStateLock);
+ Mutex::Autolock lock(mStateLock);
bool needsTraversal = false;
uint32_t transactionFlags = 0;
for (auto& transaction : transactions) {
@@ -4466,6 +4615,7 @@
for (const auto& [_, display] : mDisplays) {
mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
}
+ needsTraversal = true;
}
return needsTraversal;
@@ -4645,7 +4795,7 @@
}
}
if (what & layer_state_t::eBackgroundColorChanged) {
- if (layer->setBackgroundColor(s.color.rgb, s.bgColorAlpha, s.bgColorDataspace)) {
+ if (layer->setBackgroundColor(s.bgColor.rgb, s.bgColor.a, s.bgColorDataspace)) {
flags |= eTraversalNeeded;
}
}
@@ -4785,6 +4935,11 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eCachingHintChanged) {
+ if (layer->setCachingHint(s.cachingHint)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eHdrMetadataChanged) {
if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
}
@@ -4849,9 +5004,15 @@
layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
+ if ((what & layer_state_t::eBufferChanged) == 0) {
+ layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
+ }
+
if (what & layer_state_t::eTrustedPresentationInfoChanged) {
- layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
- s.trustedPresentationListener);
+ if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
+ s.trustedPresentationListener)) {
+ flags |= eTraversalNeeded;
+ }
}
if (what & layer_state_t::eFlushJankData) {
@@ -4883,8 +5044,6 @@
uint64_t transactionId) {
layer_state_t& s = composerState.state;
s.sanitize(permissions);
- const nsecs_t latchTime = systemTime();
- bool unused;
std::vector<ListenerCallbacks> filteredListeners;
for (auto& listener : s.listeners) {
@@ -4937,6 +5096,12 @@
sp<CallbackHandle>::make(listener, callbackIds, s.surface));
}
}
+ // TODO(b/238781169) remove after screenshot refactor, currently screenshots
+ // requires to read drawing state from binder thread. So we need to fix that
+ // before removing this.
+ if (what & layer_state_t::eCropChanged) {
+ if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eSidebandStreamChanged) {
if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
}
@@ -4944,7 +5109,6 @@
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
frameTimelineInfo)) {
- layer->latchBuffer(unused, latchTime);
flags |= eTraversalNeeded;
}
mLayersWithQueuedFrames.emplace(layer);
@@ -4952,16 +5116,23 @@
layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
- if (what & layer_state_t::eTrustedPresentationInfoChanged) {
- layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
- s.trustedPresentationListener);
+ if ((what & layer_state_t::eBufferChanged) == 0) {
+ layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
}
- const auto& snapshot = mLayerSnapshotBuilder.getSnapshot(layer->getSequence());
+ if (what & layer_state_t::eTrustedPresentationInfoChanged) {
+ if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
+ s.trustedPresentationListener)) {
+ flags |= eTraversalNeeded;
+ }
+ }
+
+ const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence());
bool willPresentCurrentTransaction =
- snapshot && (snapshot->hasReadyFrame || snapshot->sidebandStreamHasFrame);
+ requestedLayerState && requestedLayerState->hasReadyFrame();
if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction))
flags |= eTraversalNeeded;
+
return flags;
}
@@ -4979,7 +5150,7 @@
sp<Layer> mirrorLayer;
sp<Layer> mirrorFrom;
- LayerCreationArgs mirrorArgs(args);
+ LayerCreationArgs mirrorArgs = LayerCreationArgs::fromOtherArgs(args);
{
Mutex::Autolock _l(mStateLock);
mirrorFrom = LayerHandle::getLayer(mirrorFromHandle);
@@ -4999,11 +5170,6 @@
outResult.layerId = mirrorLayer->sequence;
outResult.layerName = String16(mirrorLayer->getDebugName());
- if (mTransactionTracing) {
- mTransactionTracing->onMirrorLayerAdded(outResult.handle->localBinder(),
- mirrorLayer->sequence, args.name,
- mirrorFrom->sequence);
- }
return addClientLayer(mirrorArgs, outResult.handle, mirrorLayer /* layer */,
nullptr /* parent */, nullptr /* outTransformHint */);
}
@@ -5030,7 +5196,7 @@
}
layerStack = display->getLayerStack();
- LayerCreationArgs mirrorArgs(args);
+ LayerCreationArgs mirrorArgs = LayerCreationArgs::fromOtherArgs(args);
mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
mirrorArgs.addToRoot = true;
mirrorArgs.layerStackToMirror = layerStack;
@@ -5045,12 +5211,7 @@
return result;
}
- if (mTransactionTracing) {
- mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), outResult.layerId,
- args.name, args.flags, -1 /* parentId */);
- }
-
- {
+ if (mLegacyFrontEndEnabled) {
std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client);
}
@@ -5097,12 +5258,6 @@
args.addToRoot = false;
}
- const int parentId = parent ? parent->getSequence() : -1;
- if (mTransactionTracing) {
- mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), layer->sequence,
- args.name, args.flags, parentId);
- }
-
uint32_t outTransformHint;
result = addClientLayer(args, outResult.handle, layer, parent, &outTransformHint);
if (result != NO_ERROR) {
@@ -5146,22 +5301,29 @@
markLayerPendingRemovalLocked(layer);
mBufferCountTracker.remove(handle);
layer.clear();
- if (mTransactionTracing) {
- mTransactionTracing->onHandleRemoved(handle);
- }
setTransactionFlags(eTransactionFlushNeeded);
}
-void SurfaceFlinger::onInitializeDisplays() {
- const auto display = getDefaultDisplayDeviceLocked();
+void SurfaceFlinger::initializeDisplays() {
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) return;
const sp<IBinder> token = display->getDisplayToken().promote();
LOG_ALWAYS_FATAL_IF(token == nullptr);
+ TransactionState state;
+ state.inputWindowCommands = mInputWindowCommands;
+ const nsecs_t now = systemTime();
+ state.desiredPresentTime = now;
+ state.postTime = now;
+ state.permissions = layer_state_t::ACCESS_SURFACE_FLINGER;
+ state.originPid = mPid;
+ state.originUid = static_cast<int>(getuid());
+ const uint64_t transactionId = (static_cast<uint64_t>(mPid) << 32) | mUniqueTransactionId++;
+ state.id = transactionId;
+
// reset screen orientation and use primary layer stack
- std::vector<ResolvedComposerState> state;
Vector<DisplayState> displays;
DisplayState d;
d.what = DisplayState::eDisplayProjectionChanged |
@@ -5173,24 +5335,21 @@
d.layerStackSpaceRect.makeInvalid();
d.width = 0;
d.height = 0;
- displays.add(d);
+ state.displays.add(d);
- nsecs_t now = systemTime();
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(state);
- int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
- // It should be on the main thread, apply it directly.
- applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
- /* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
- {}, mPid, getuid(), transactionId);
+ if (mLegacyFrontEndEnabled) {
+ applyTransactions(transactions, VsyncId{0});
+ } else {
+ applyAndCommitDisplayTransactionStates(transactions);
+ }
- setPowerModeInternal(display, hal::PowerMode::ON);
-}
-
-void SurfaceFlinger::initializeDisplays() {
- // Async since we may be called from the main thread.
- static_cast<void>(mScheduler->schedule(
- [this]() FTL_FAKE_GUARD(mStateLock)
- FTL_FAKE_GUARD(kMainThreadContext) { onInitializeDisplays(); }));
+ {
+ ftl::FakeGuard guard(mStateLock);
+ setPowerModeInternal(display, hal::PowerMode::ON);
+ }
}
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -5207,7 +5366,6 @@
return;
}
- const bool isActiveDisplay = displayId == mActiveDisplayId;
const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
.transform(&PhysicalDisplay::isInternal)
.value_or(false);
@@ -5244,11 +5402,12 @@
ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
}
getHwComposer().setPowerMode(displayId, mode);
- if (isActiveDisplay && mode != hal::PowerMode::DOZE_SUSPEND) {
+ if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) {
setHWCVsyncEnabled(displayId,
- mScheduler->getVsyncSchedule().getPendingHardwareVsyncState());
- mScheduler->onScreenAcquired(mAppConnectionHandle);
- mScheduler->resyncToHardwareVsync(true, refreshRate);
+ mScheduler->getVsyncSchedule(displayId)
+ ->getPendingHardwareVsyncState());
+ mScheduler->enableSyntheticVsync(false);
+ mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate);
}
mVisibleRegionsDirty = true;
@@ -5261,9 +5420,9 @@
if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
}
- if (isActiveDisplay && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
- mScheduler->disableHardwareVsync(true);
- mScheduler->onScreenReleased(mAppConnectionHandle);
+ if (displayId == mActiveDisplayId && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
+ mScheduler->disableHardwareVsync(displayId, true);
+ mScheduler->enableSyntheticVsync();
}
// Make sure HWVsync is disabled before turning off the display
@@ -5275,18 +5434,18 @@
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (isActiveDisplay && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
+ if (displayId == mActiveDisplayId && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
mVisibleRegionsDirty = true;
scheduleRepaint();
- mScheduler->onScreenAcquired(mAppConnectionHandle);
- mScheduler->resyncToHardwareVsync(true, refreshRate);
+ mScheduler->enableSyntheticVsync(false);
+ mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate);
}
} else if (mode == hal::PowerMode::DOZE_SUSPEND) {
// Leave display going to doze
- if (isActiveDisplay) {
- mScheduler->disableHardwareVsync(true);
- mScheduler->onScreenReleased(mAppConnectionHandle);
+ if (displayId == mActiveDisplayId) {
+ mScheduler->disableHardwareVsync(displayId, true);
+ mScheduler->enableSyntheticVsync();
}
getHwComposer().setPowerMode(displayId, mode);
} else {
@@ -5294,10 +5453,10 @@
getHwComposer().setPowerMode(displayId, mode);
}
- if (isActiveDisplay) {
+ if (displayId == mActiveDisplayId) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats->setPowerMode(mode);
- mScheduler->setDisplayPowerMode(mode);
+ mScheduler->setDisplayPowerMode(displayId, mode);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
@@ -5392,7 +5551,8 @@
LayersTraceProto* layersTrace = traceFileProto.add_entry();
LayersProto layersProto = dumpProtoFromMainThread();
layersTrace->mutable_layers()->Swap(&layersProto);
- dumpDisplayProto(*layersTrace);
+ auto displayProtos = dumpDisplayProto();
+ layersTrace->mutable_displays()->Swap(&displayProtos);
if (asProto) {
result.append(traceFileProto.SerializeAsString());
@@ -5620,20 +5780,34 @@
}
}
- LayersProto layersProto;
- for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
- if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
- continue;
+ if (mLegacyFrontEndEnabled) {
+ LayersProto layersProto;
+ for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
+ if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
+ continue;
+ }
+ layer->writeToProto(layersProto, traceFlags);
}
- layer->writeToProto(layersProto, traceFlags);
+ return layersProto;
}
+ const frontend::LayerHierarchy& root = mLayerHierarchyBuilder.getHierarchy();
+ LayersProto layersProto;
+ for (auto& [child, variant] : root.mChildren) {
+ if (variant != frontend::LayerHierarchy::Variant::Attached ||
+ stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
+ continue;
+ }
+ LayerProtoHelper::writeHierarchyToProto(layersProto, *child, mLayerSnapshotBuilder,
+ mLegacyLayers, traceFlags);
+ }
return layersProto;
}
-void SurfaceFlinger::dumpDisplayProto(LayersTraceProto& layersTraceProto) const {
+google::protobuf::RepeatedPtrField<DisplayProto> SurfaceFlinger::dumpDisplayProto() const {
+ google::protobuf::RepeatedPtrField<DisplayProto> displays;
for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
- DisplayProto* displayProto = layersTraceProto.add_displays();
+ DisplayProto* displayProto = displays.Add();
displayProto->set_id(display->getId().value);
displayProto->set_name(display->getDisplayName());
displayProto->set_layer_stack(display->getLayerStack().id);
@@ -5646,6 +5820,7 @@
displayProto->mutable_transform());
displayProto->set_is_virtual(display->isVirtual());
}
+ return displays;
}
void SurfaceFlinger::dumpHwc(std::string& result) const {
@@ -6176,9 +6351,10 @@
int64_t startingTime =
(fixedStartingTime) ? fixedStartingTime : systemTime();
mScheduler
- ->schedule([&]() FTL_FAKE_GUARD(mStateLock) {
- mLayerTracing.notify(true /* visibleRegionDirty */,
- startingTime, mLastCommittedVsyncId.value);
+ ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+ kMainThreadContext) {
+ addToLayerTracing(true /* visibleRegionDirty */, startingTime,
+ mLastCommittedVsyncId.value);
})
.wait();
}
@@ -6709,7 +6885,8 @@
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
- getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, args.uid);
+ getLayerSnapshots =
+ getLayerSnapshotsForScreenshots(layerStack, args.uid, /*snapshotFilterFn=*/nullptr);
} else {
auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
traverseLayersInLayerStack(layerStack, args.uid, visitor);
@@ -6751,7 +6928,8 @@
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
- getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID);
+ getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+ /*snapshotFilterFn=*/nullptr);
} else {
auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
@@ -6919,7 +7097,7 @@
->schedule([=]() {
bool protectedLayerFound = false;
auto layers = getLayerSnapshots();
- for (auto& [layer, layerFe] : layers) {
+ for (auto& [_, layerFe] : layers) {
protectedLayerFound |=
(layerFe->mSnapshot->isVisible &&
layerFe->mSnapshot->hasProtectedContent);
@@ -7014,12 +7192,14 @@
ATRACE_CALL();
auto layers = getLayerSnapshots();
- for (auto& [layer, layerFE] : layers) {
+ for (auto& [_, layerFE] : layers) {
frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
layerFE->mSnapshot->geomLayerTransform =
renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
+ layerFE->mSnapshot->geomInverseLayerTransform =
+ layerFE->mSnapshot->geomLayerTransform.inverse();
}
// We allow the system server to take screenshots of secure layers for
@@ -7130,6 +7310,16 @@
return presentFuture;
}
+void SurfaceFlinger::traverseLegacyLayers(const LayerVector::Visitor& visitor) const {
+ if (mLayerLifecycleManagerEnabled) {
+ for (auto& layer : mLegacyLayers) {
+ visitor(layer.second.get());
+ }
+ } else {
+ mDrawingState.traverse(visitor);
+ }
+}
+
// ---------------------------------------------------------------------------
void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
@@ -7418,10 +7608,20 @@
}
void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
+ bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
for (const auto& [id, display] : mPhysicalDisplays) {
if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+ if (setByHwc) {
+ const auto status =
+ getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+ if (status != NO_ERROR) {
+ ALOGE("Error updating the refresh rate changed callback debug enabled");
+ return;
+ }
+ }
+
if (const auto device = getDisplayDeviceLocked(id)) {
- device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+ device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
mRefreshRateOverlayRenderRate,
mRefreshRateOverlayShowInMiddle);
}
@@ -7517,10 +7717,6 @@
if (hintDisplay) {
layer->updateTransformHint(hintDisplay->getTransformHint());
}
-
- if (mTransactionTracing) {
- mTransactionTracing->onLayerAddedToDrawingState(layer->getSequence(), vsyncId.value);
- }
}
void SurfaceFlinger::sample() {
@@ -7540,31 +7736,38 @@
const DisplayDevice& activeDisplay) {
ATRACE_CALL();
+ // For the first display activated during boot, there is no need to force setDesiredActiveMode,
+ // because DM is about to send its policy via setDesiredDisplayModeSpecs.
+ bool forceApplyPolicy = false;
+
if (inactiveDisplayPtr) {
inactiveDisplayPtr->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+ forceApplyPolicy = true;
}
mActiveDisplayId = activeDisplay.getPhysicalId();
activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
- updateActiveDisplayVsyncLocked(activeDisplay);
+ resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
+
mScheduler->setModeChangePending(false);
- mScheduler->setLeaderDisplay(mActiveDisplayId);
+ mScheduler->setPacesetterDisplay(mActiveDisplayId);
onActiveDisplaySizeChanged(activeDisplay);
mActiveDisplayTransformHint = activeDisplay.getTransformHint();
- // The policy of the new active/leader display may have changed while it was inactive. In that
- // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
- // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
- // and the kernel idle timer of the newly active display must be toggled.
- constexpr bool kForce = true;
- applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(), kForce);
+ // The policy of the new active/pacesetter display may have changed while it was inactive. In
+ // that case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In
+ // either case, the Scheduler's cachedModeChangedParams must be initialized to the newly active
+ // mode, and the kernel idle timer of the newly active display must be toggled.
+ applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(),
+ forceApplyPolicy);
}
status_t SurfaceFlinger::addWindowInfosListener(
- const sp<IWindowInfosListener>& windowInfosListener) const {
+ const sp<IWindowInfosListener>& windowInfosListener) {
mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
+ setTransactionFlags(eInputInfoUpdateNeeded);
return NO_ERROR;
}
@@ -7654,10 +7857,6 @@
sp<Layer> childMirror;
createEffectLayer(mirrorArgs, &unused, &childMirror);
childMirror->setClonedChild(layer->createClone());
- if (mTransactionTracing) {
- mTransactionTracing->onLayerAddedToDrawingState(childMirror->getSequence(),
- vsyncId.value);
- }
childMirror->reparent(mirrorDisplay.rootHandle);
}
}
@@ -7725,6 +7924,7 @@
compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly, int64_t vsyncId) {
std::vector<std::pair<Layer*, LayerFE*>> layers;
if (mLayerLifecycleManagerEnabled) {
+ nsecs_t currentTime = systemTime();
mLayerSnapshotBuilder.forEachVisibleSnapshot(
[&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
if (cursorOnly &&
@@ -7743,6 +7943,7 @@
snapshot->getDebugString().c_str());
auto& legacyLayer = it->second;
sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+ snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence);
layerFE->mSnapshot = std::move(snapshot);
refreshArgs.layers.push_back(layerFE);
layers.emplace_back(legacyLayer.get(), layerFE.get());
@@ -7767,29 +7968,40 @@
}
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
-SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack,
- uint32_t uid) {
- return [this, layerStack, uid]() {
+SurfaceFlinger::getLayerSnapshotsForScreenshots(
+ std::optional<ui::LayerStack> layerStack, uint32_t uid,
+ std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
+ snapshotFilterFn) {
+ return [&, layerStack, uid]() {
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
- if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
- continue;
- }
- if (uid != CaptureArgs::UNSET_UID && snapshot->inputInfo.ownerUid != uid) {
- continue;
- }
- if (!snapshot->isVisible || !snapshot->hasSomethingToDraw()) {
- continue;
- }
+ bool stopTraversal = false;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (stopTraversal) {
+ return;
+ }
+ if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
+ return;
+ }
+ if (uid != CaptureArgs::UNSET_UID && snapshot->inputInfo.ownerUid != uid) {
+ return;
+ }
+ if (!snapshot->hasSomethingToDraw()) {
+ return;
+ }
+ if (snapshotFilterFn && !snapshotFilterFn(*snapshot, stopTraversal)) {
+ return;
+ }
- 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 = getFactory().createLayerFE(legacyLayer->getName());
- layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
- layers.emplace_back(legacyLayer.get(), std::move(layerFE));
- }
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ Layer* legacyLayer = (it == mLegacyLayers.end()) ? nullptr : it->second.get();
+ sp<LayerFE> layerFE = getFactory().createLayerFE(snapshot->name);
+ layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
+ layers.emplace_back(legacyLayer, std::move(layerFE));
+ });
return layers;
};
@@ -7799,21 +8011,27 @@
SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t uid,
std::unordered_set<uint32_t> excludeLayerIds,
bool childrenOnly, const FloatRect& parentCrop) {
- return [this, excludeLayerIds = std::move(excludeLayerIds), uid, rootLayerId, childrenOnly,
+ return [&, rootLayerId, uid, excludeLayerIds = std::move(excludeLayerIds), childrenOnly,
parentCrop]() {
+ auto root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly);
frontend::LayerSnapshotBuilder::Args
- args{.root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly),
+ args{.root = root,
.layerLifecycleManager = mLayerLifecycleManager,
+ .forceUpdate = frontend::LayerSnapshotBuilder::ForceUpdateFlags::HIERARCHY,
.displays = mFrontEndDisplayInfos,
.displayChanges = true,
.globalShadowSettings = mDrawingState.globalShadowSettings,
.supportsBlur = mSupportsBlur,
.forceFullDamage = mForceFullDamage,
.parentCrop = {parentCrop},
- .excludeLayerIds = std::move(excludeLayerIds)};
+ .excludeLayerIds = std::move(excludeLayerIds),
+ .supportedLayerGenericMetadata =
+ getHwComposer().getSupportedLayerGenericMetadata(),
+ .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
mLayerSnapshotBuilder.update(args);
- auto getLayerSnapshotsFn = getLayerSnapshotsForScreenshots({}, uid);
+ auto getLayerSnapshotsFn =
+ getLayerSnapshotsForScreenshots({}, uid, /*snapshotFilterFn=*/nullptr);
std::vector<std::pair<Layer*, sp<LayerFE>>> layers = getLayerSnapshotsFn();
args.root = mLayerHierarchyBuilder.getHierarchy();
args.parentCrop.reset();
@@ -7823,8 +8041,8 @@
};
}
-SurfaceFlinger::LifecycleUpdate SurfaceFlinger::flushLifecycleUpdates() {
- LifecycleUpdate update;
+frontend::Update SurfaceFlinger::flushLifecycleUpdates() {
+ frontend::Update update;
ATRACE_NAME("TransactionHandler:flushTransactions");
// Locking:
// 1. to prevent onHandleDestroyed from being called while the state lock is held,
@@ -7841,12 +8059,28 @@
mCreatedLayers.clear();
update.newLayers = std::move(mNewLayers);
mNewLayers.clear();
+ update.layerCreationArgs = std::move(mNewLayerArgs);
+ mNewLayerArgs.clear();
update.destroyedHandles = std::move(mDestroyedHandles);
mDestroyedHandles.clear();
}
return update;
}
+void SurfaceFlinger::addToLayerTracing(bool visibleRegionDirty, int64_t time, int64_t vsyncId) {
+ const uint32_t tracingFlags = mLayerTracing.getFlags();
+ LayersProto layers(dumpDrawingStateProto(tracingFlags));
+ if (tracingFlags & LayerTracing::TRACE_EXTRA) {
+ dumpOffscreenLayersProto(layers);
+ }
+ std::string hwcDump;
+ if (tracingFlags & LayerTracing::TRACE_HWC) {
+ dumpHwc(hwcDump);
+ }
+ auto displays = dumpDisplayProto();
+ mLayerTracing.notify(visibleRegionDirty, time, vsyncId, &layers, std::move(hwcDump), &displays);
+}
+
// gui::ISurfaceComposer
binder::Status SurfaceComposerAIDL::bootFinished() {
@@ -8556,8 +8790,12 @@
binder::Status SurfaceComposerAIDL::addWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) {
status_t status;
+ const int pid = IPCThreadState::self()->getCallingPid();
const int uid = IPCThreadState::self()->getCallingUid();
- if (uid == AID_SYSTEM || uid == AID_GRAPHICS) {
+ // TODO(b/270566761) update permissions check so that only system_server and shell can add
+ // WindowInfosListeners
+ if (uid == AID_SYSTEM || uid == AID_GRAPHICS ||
+ checkPermission(sAccessSurfaceFlinger, pid, uid)) {
status = mFlinger->addWindowInfosListener(windowInfosListener);
} else {
status = PERMISSION_DENIED;
@@ -8568,8 +8806,10 @@
binder::Status SurfaceComposerAIDL::removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) {
status_t status;
+ const int pid = IPCThreadState::self()->getCallingPid();
const int uid = IPCThreadState::self()->getCallingUid();
- if (uid == AID_SYSTEM || uid == AID_GRAPHICS) {
+ if (uid == AID_SYSTEM || uid == AID_GRAPHICS ||
+ checkPermission(sAccessSurfaceFlinger, pid, uid)) {
status = mFlinger->removeWindowInfosListener(windowInfosListener);
} else {
status = PERMISSION_DENIED;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b41f414..42d5db7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -162,7 +162,8 @@
eDisplayTransactionNeeded = 0x04,
eTransformHintUpdateNeeded = 0x08,
eTransactionFlushNeeded = 0x10,
- eTransactionMask = 0x1f,
+ eInputInfoUpdateNeeded = 0x20,
+ eTransactionMask = 0x3f,
};
// Latch Unsignaled buffer behaviours
@@ -454,26 +455,6 @@
FINISHED,
};
- struct LayerCreatedState {
- LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot)
- : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
- wp<Layer> layer;
- // Indicates the initial parent of the created layer, only used for creating layer in
- // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
- wp<Layer> initialParent;
- // Indicates whether the layer getting created should be added at root if there's no parent
- // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
- // be added offscreen.
- bool addToRoot;
- };
-
- struct LifecycleUpdate {
- std::vector<TransactionState> transactions;
- std::vector<LayerCreatedState> layerCreatedStates;
- std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
- std::vector<uint32_t> destroyedHandles;
- };
-
template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
static Dumper dumper(F&& dump) {
using namespace std::placeholders;
@@ -618,7 +599,7 @@
status_t getMaxAcquiredBufferCount(int* buffers) const;
- status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener) const;
+ status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
status_t removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) const;
@@ -646,7 +627,7 @@
// Toggles hardware VSYNC by calling into HWC.
// TODO(b/241286146): Rename for self-explanatory API.
- void setVsyncEnabled(bool) override;
+ void setVsyncEnabled(PhysicalDisplayId, bool) override;
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
void triggerOnFrameRateOverridesChanged() override;
@@ -720,12 +701,13 @@
int64_t vsyncId);
void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
std::vector<std::pair<Layer*, LayerFE*>>& layers);
- bool updateLayerSnapshotsLegacy(VsyncId vsyncId, LifecycleUpdate& update,
+ bool updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
bool transactionsFlushed, bool& out)
REQUIRES(kMainThreadContext);
- bool updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update, bool transactionsFlushed,
+ bool updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
- LifecycleUpdate flushLifecycleUpdates() REQUIRES(kMainThreadContext);
+ void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
+ frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
void updateInputFlinger();
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -735,6 +717,8 @@
void updateCursorAsync();
void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
+
+ void resetPhaseConfiguration(Fps) REQUIRES(mStateLock, kMainThreadContext);
void updatePhaseConfiguration(Fps) REQUIRES(mStateLock);
/*
@@ -816,7 +800,7 @@
void markLayerPendingRemovalLocked(const sp<Layer>& layer);
// add a layer to SurfaceFlinger
- status_t addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle,
+ status_t addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
const sp<Layer>& layer, const wp<Layer>& parentLayer,
uint32_t* outTransformHint);
@@ -853,8 +837,7 @@
*/
// Called during boot, and restart after system_server death.
- void initializeDisplays();
- void onInitializeDisplays() REQUIRES(mStateLock, kMainThreadContext);
+ void initializeDisplays() REQUIRES(kMainThreadContext);
sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
REQUIRES(mStateLock) {
@@ -1083,7 +1066,9 @@
LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
void dumpOffscreenLayersProto(LayersProto& layersProto,
uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
- void dumpDisplayProto(LayersTraceProto& layersTraceProto) const;
+ google::protobuf::RepeatedPtrField<DisplayProto> dumpDisplayProto() const;
+ void addToLayerTracing(bool visibleRegionDirty, int64_t time, int64_t vsyncId)
+ REQUIRES(kMainThreadContext);
// Dumps state from HW Composer
void dumpHwc(std::string& result) const;
@@ -1119,20 +1104,26 @@
std::chrono::nanoseconds presentLatency);
int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
- void updateActiveDisplayVsyncLocked(const DisplayDevice& activeDisplay)
- REQUIRES(mStateLock, kMainThreadContext);
-
bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
REQUIRES(mStateLock);
+ void traverseLegacyLayers(const LayerVector::Visitor& visitor) const;
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
pid_t mPid;
std::future<void> mRenderEnginePrimeCacheFuture;
- // access must be protected by mStateLock
+ // mStateLock has conventions related to the current thread, because only
+ // the main thread should modify variables protected by mStateLock.
+ // - read access from a non-main thread must lock mStateLock, since the main
+ // thread may modify these variables.
+ // - write access from a non-main thread is not permitted.
+ // - read access from the main thread can use an ftl::FakeGuard, since other
+ // threads must not modify these variables.
+ // - write access from the main thread must lock mStateLock, since another
+ // thread may be reading these variables.
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
std::atomic<int32_t> mTransactionFlags = 0;
@@ -1231,7 +1222,7 @@
bool mLayerCachingEnabled = false;
bool mBackpressureGpuComposition = false;
- LayerTracing mLayerTracing{*this};
+ LayerTracing mLayerTracing;
bool mLayerTracingEnabled = false;
std::optional<TransactionTracing> mTransactionTracing;
@@ -1386,7 +1377,9 @@
[](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
}
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
- std::optional<ui::LayerStack> layerStack, uint32_t uid);
+ std::optional<ui::LayerStack> layerStack, uint32_t uid,
+ std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
+ snapshotFilterFn);
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
uint32_t rootLayerId, uint32_t uid, std::unordered_set<uint32_t> excludeLayerIds,
bool childrenOnly, const FloatRect& parentCrop);
@@ -1411,6 +1404,7 @@
std::vector<uint32_t> mDestroyedHandles;
std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers;
+ std::vector<LayerCreationArgs> mNewLayerArgs;
// These classes do not store any client state but help with managing transaction callbacks
// and stats.
std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers;
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 566d553..2918f7c 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -29,9 +29,8 @@
namespace android {
-LayerTracing::LayerTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {
- mBuffer = std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>();
-}
+LayerTracing::LayerTracing()
+ : mBuffer(std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>()) {}
LayerTracing::~LayerTracing() = default;
@@ -84,8 +83,11 @@
bool LayerTracing::flagIsSet(uint32_t flags) const {
return (mFlags & flags) == flags;
}
+uint32_t LayerTracing::getFlags() const {
+ return mFlags;
+}
-LayersTraceFileProto LayerTracing::createTraceFileProto() const {
+LayersTraceFileProto LayerTracing::createTraceFileProto() {
LayersTraceFileProto fileProto;
fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
@@ -101,7 +103,9 @@
mBuffer->dump(result);
}
-void LayerTracing::notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId) {
+void LayerTracing::notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId,
+ LayersProto* layers, std::string hwcDump,
+ google::protobuf::RepeatedPtrField<DisplayProto>* displays) {
std::scoped_lock lock(mTraceLock);
if (!mEnabled) {
return;
@@ -116,22 +120,15 @@
entry.set_elapsed_realtime_nanos(time);
const char* where = visibleRegionDirty ? "visibleRegionsDirty" : "bufferLatched";
entry.set_where(where);
- LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags));
-
- if (flagIsSet(LayerTracing::TRACE_EXTRA)) {
- mFlinger.dumpOffscreenLayersProto(layers);
- }
- entry.mutable_layers()->Swap(&layers);
+ entry.mutable_layers()->Swap(layers);
if (flagIsSet(LayerTracing::TRACE_HWC)) {
- std::string hwcDump;
- mFlinger.dumpHwc(hwcDump);
entry.set_hwc_blob(hwcDump);
}
if (!flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
entry.set_excludes_composition_state(true);
}
- mFlinger.dumpDisplayProto(entry);
+ entry.mutable_displays()->Swap(displays);
entry.set_vsync_id(vsyncId);
mBuffer->emplace(std::move(entry));
}
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index b32001c..11bb9f4 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -40,14 +40,15 @@
*/
class LayerTracing {
public:
- LayerTracing(SurfaceFlinger& flinger);
+ LayerTracing();
~LayerTracing();
bool enable();
bool disable(std::string filename = FILE_NAME);
bool isEnabled() const;
status_t writeToFile();
- LayersTraceFileProto createTraceFileProto() const;
- void notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId);
+ static LayersTraceFileProto createTraceFileProto();
+ void notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId, LayersProto* layers,
+ std::string hwcDump, google::protobuf::RepeatedPtrField<DisplayProto>* displays);
enum : uint32_t {
TRACE_INPUT = 1 << 1,
@@ -60,13 +61,12 @@
};
void setTraceFlags(uint32_t flags);
bool flagIsSet(uint32_t flags) const;
+ uint32_t getFlags() const;
void setBufferSize(size_t bufferSizeInBytes);
void dump(std::string&) const;
private:
static constexpr auto FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
-
- SurfaceFlinger& mFlinger;
uint32_t mFlags = TRACE_INPUT;
mutable std::mutex mTraceLock;
bool mEnabled GUARDED_BY(mTraceLock) = false;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 2f46487..8fd6538 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -18,11 +18,40 @@
#include <ui/Fence.h>
#include <ui/Rect.h>
+#include "FrontEnd/LayerCreationArgs.h"
#include "LayerProtoHelper.h"
#include "TransactionProtoParser.h"
+#include "TransactionState.h"
+#include "gui/LayerState.h"
namespace android::surfaceflinger {
+class FakeExternalTexture : public renderengine::ExternalTexture {
+ const sp<GraphicBuffer> mEmptyBuffer =
+ sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint64_t mId;
+ PixelFormat mPixelFormat;
+ uint64_t mUsage;
+
+public:
+ FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
+ uint64_t usage)
+ : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
+ const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; }
+ bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+ return getId() == other.getId();
+ }
+ uint32_t getWidth() const override { return mWidth; }
+ uint32_t getHeight() const override { return mHeight; }
+ uint64_t getId() const override { return mId; }
+ PixelFormat getPixelFormat() const override { return mPixelFormat; }
+ uint64_t getUsage() const override { return mUsage; }
+ ~FakeExternalTexture() = default;
+};
+
proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t) {
proto::TransactionState proto;
proto.set_pid(t.originPid);
@@ -34,7 +63,7 @@
proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(t.states.size()));
for (auto& layerState : t.states) {
- proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state)));
+ proto.mutable_layer_changes()->Add(std::move(toProto(layerState)));
}
proto.mutable_display_changes()->Reserve(static_cast<int32_t>(t.displays.size()));
@@ -45,40 +74,22 @@
}
proto::TransactionState TransactionProtoParser::toProto(
- const std::map<int32_t /* layerId */, TracingLayerState>& states) {
+ const std::map<uint32_t /* layerId */, TracingLayerState>& states) {
proto::TransactionState proto;
proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(states.size()));
for (auto& [layerId, state] : states) {
proto::LayerState layerProto = toProto(state);
- if (layerProto.has_buffer_data()) {
- proto::LayerState_BufferData* bufferProto = layerProto.mutable_buffer_data();
- bufferProto->set_buffer_id(state.bufferId);
- bufferProto->set_width(state.bufferWidth);
- bufferProto->set_height(state.bufferHeight);
- bufferProto->set_pixel_format(
- static_cast<proto::LayerState_BufferData_PixelFormat>(state.pixelFormat));
- bufferProto->set_usage(state.bufferUsage);
- }
layerProto.set_has_sideband_stream(state.hasSidebandStream);
- layerProto.set_layer_id(state.layerId);
- layerProto.set_parent_id(state.parentId);
- layerProto.set_relative_parent_id(state.relativeParentId);
- if (layerProto.has_window_info_handle()) {
- layerProto.mutable_window_info_handle()->set_crop_layer_id(state.inputCropId);
- }
proto.mutable_layer_changes()->Add(std::move(layerProto));
}
return proto;
}
-proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) {
+proto::LayerState TransactionProtoParser::toProto(
+ const ResolvedComposerState& resolvedComposerState) {
proto::LayerState proto;
- if (layer.surface) {
- proto.set_layer_id(mMapper->getLayerId(layer.surface));
- } else {
- proto.set_layer_id(layer.layerId);
- }
-
+ auto& layer = resolvedComposerState.state;
+ proto.set_layer_id(resolvedComposerState.layerId);
proto.set_what(layer.what);
if (layer.what & layer_state_t::ePositionChanged) {
@@ -134,27 +145,13 @@
}
if (layer.what & layer_state_t::eBufferChanged) {
proto::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
- if (layer.bufferData->hasBuffer()) {
- bufferProto->set_buffer_id(layer.bufferData->getId());
- bufferProto->set_width(layer.bufferData->getWidth());
- bufferProto->set_height(layer.bufferData->getHeight());
+ if (resolvedComposerState.externalTexture) {
+ bufferProto->set_buffer_id(resolvedComposerState.externalTexture->getId());
+ bufferProto->set_width(resolvedComposerState.externalTexture->getWidth());
+ bufferProto->set_height(resolvedComposerState.externalTexture->getHeight());
bufferProto->set_pixel_format(static_cast<proto::LayerState_BufferData_PixelFormat>(
- layer.bufferData->getPixelFormat()));
- bufferProto->set_usage(layer.bufferData->getUsage());
- } else {
- uint64_t bufferId;
- uint32_t width;
- uint32_t height;
- int32_t pixelFormat;
- uint64_t usage;
- mMapper->getGraphicBufferPropertiesFromCache(layer.bufferData->cachedBuffer, &bufferId,
- &width, &height, &pixelFormat, &usage);
- bufferProto->set_buffer_id(bufferId);
- bufferProto->set_width(width);
- bufferProto->set_height(height);
- bufferProto->set_pixel_format(
- static_cast<proto::LayerState_BufferData_PixelFormat>(pixelFormat));
- bufferProto->set_usage(usage);
+ resolvedComposerState.externalTexture->getPixelFormat()));
+ bufferProto->set_usage(resolvedComposerState.externalTexture->getUsage());
}
bufferProto->set_frame_number(layer.bufferData->frameNumber);
bufferProto->set_flags(layer.bufferData->flags.get());
@@ -178,16 +175,10 @@
}
if (layer.what & layer_state_t::eReparent) {
- int64_t layerId = layer.parentSurfaceControlForChild
- ? mMapper->getLayerId(layer.parentSurfaceControlForChild->getHandle())
- : -1;
- proto.set_parent_id(layerId);
+ proto.set_parent_id(resolvedComposerState.parentId);
}
if (layer.what & layer_state_t::eRelativeLayerChanged) {
- int64_t layerId = layer.relativeLayerSurfaceControl
- ? mMapper->getLayerId(layer.relativeLayerSurfaceControl->getHandle())
- : -1;
- proto.set_relative_parent_id(layerId);
+ proto.set_relative_parent_id(resolvedComposerState.relativeParentId);
proto.set_z(layer.z);
}
@@ -206,7 +197,7 @@
windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
- proto::LayerState_Transform* transformProto = windowInfoProto->mutable_transform();
+ proto::Transform* transformProto = windowInfoProto->mutable_transform();
transformProto->set_dsdx(inputInfo->transform.dsdx());
transformProto->set_dtdx(inputInfo->transform.dtdx());
transformProto->set_dtdy(inputInfo->transform.dtdy());
@@ -215,17 +206,16 @@
transformProto->set_ty(inputInfo->transform.ty());
windowInfoProto->set_replace_touchable_region_with_crop(
inputInfo->replaceTouchableRegionWithCrop);
- windowInfoProto->set_crop_layer_id(
- mMapper->getLayerId(inputInfo->touchableRegionCropHandle.promote()));
+ windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
}
}
if (layer.what & layer_state_t::eBackgroundColorChanged) {
- proto.set_bg_color_alpha(layer.bgColorAlpha);
+ proto.set_bg_color_alpha(layer.bgColor.a);
proto.set_bg_color_dataspace(static_cast<int32_t>(layer.bgColorDataspace));
proto::LayerState_Color3* colorProto = proto.mutable_color();
- colorProto->set_r(layer.color.r);
- colorProto->set_g(layer.color.g);
- colorProto->set_b(layer.color.b);
+ colorProto->set_r(layer.bgColor.r);
+ colorProto->set_g(layer.bgColor.g);
+ colorProto->set_b(layer.bgColor.b);
}
if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) {
proto.set_color_space_agnostic(layer.colorSpaceAgnostic);
@@ -288,13 +278,15 @@
return proto;
}
-proto::LayerCreationArgs TransactionProtoParser::toProto(const TracingLayerCreationArgs& args) {
+proto::LayerCreationArgs TransactionProtoParser::toProto(const LayerCreationArgs& args) {
proto::LayerCreationArgs proto;
- proto.set_layer_id(args.layerId);
+ proto.set_layer_id(args.sequence);
proto.set_name(args.name);
proto.set_flags(args.flags);
proto.set_parent_id(args.parentId);
- proto.set_mirror_from_id(args.mirrorFromId);
+ proto.set_mirror_from_id(args.layerIdToMirror);
+ proto.set_add_to_root(args.addToRoot);
+ proto.set_layer_stack_to_mirror(args.layerStackToMirror.id);
return proto;
}
@@ -312,7 +304,7 @@
for (int i = 0; i < layerCount; i++) {
ResolvedComposerState s;
s.state.what = 0;
- fromProto(proto.layer_changes(i), s.state);
+ fromProto(proto.layer_changes(i), s);
t.states.emplace_back(s);
}
@@ -325,46 +317,47 @@
}
void TransactionProtoParser::fromProto(const proto::LayerCreationArgs& proto,
- TracingLayerCreationArgs& outArgs) {
- outArgs.layerId = proto.layer_id();
+ LayerCreationArgs& outArgs) {
+ outArgs.sequence = proto.layer_id();
+
outArgs.name = proto.name();
outArgs.flags = proto.flags();
outArgs.parentId = proto.parent_id();
- outArgs.mirrorFromId = proto.mirror_from_id();
+ outArgs.layerIdToMirror = proto.mirror_from_id();
+ outArgs.addToRoot = proto.add_to_root();
+ outArgs.layerStackToMirror.id = proto.layer_stack_to_mirror();
}
void TransactionProtoParser::mergeFromProto(const proto::LayerState& proto,
TracingLayerState& outState) {
- layer_state_t state;
- fromProto(proto, state);
- outState.merge(state);
+ ResolvedComposerState resolvedComposerState;
+ fromProto(proto, resolvedComposerState);
+ layer_state_t& state = resolvedComposerState.state;
+ outState.state.merge(state);
+ outState.layerId = resolvedComposerState.layerId;
if (state.what & layer_state_t::eReparent) {
- outState.parentId = static_cast<int32_t>(proto.parent_id());
+ outState.parentId = resolvedComposerState.parentId;
}
if (state.what & layer_state_t::eRelativeLayerChanged) {
- outState.relativeParentId = static_cast<int32_t>(proto.relative_parent_id());
+ outState.relativeParentId = resolvedComposerState.relativeParentId;
}
if (state.what & layer_state_t::eInputInfoChanged) {
- outState.inputCropId = static_cast<int32_t>(proto.window_info_handle().crop_layer_id());
+ outState.touchCropId = resolvedComposerState.touchCropId;
}
if (state.what & layer_state_t::eBufferChanged) {
- const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
- outState.bufferId = bufferProto.buffer_id();
- outState.bufferWidth = bufferProto.width();
- outState.bufferHeight = bufferProto.height();
- outState.pixelFormat = bufferProto.pixel_format();
- outState.bufferUsage = bufferProto.usage();
+ outState.externalTexture = resolvedComposerState.externalTexture;
}
if (state.what & layer_state_t::eSidebandStreamChanged) {
outState.hasSidebandStream = proto.has_sideband_stream();
}
}
-void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_state_t& layer) {
- layer.layerId = (int32_t)proto.layer_id();
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+ ResolvedComposerState& resolvedComposerState) {
+ auto& layer = resolvedComposerState.state;
+ resolvedComposerState.layerId = proto.layer_id();
layer.what |= proto.what();
- layer.surface = mMapper->getLayerHandle(layer.layerId);
if (proto.what() & layer_state_t::ePositionChanged) {
layer.x = proto.x();
@@ -419,9 +412,15 @@
if (proto.what() & layer_state_t::eBufferChanged) {
const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
layer.bufferData =
- std::move(mMapper->getGraphicData(bufferProto.buffer_id(), bufferProto.width(),
- bufferProto.height(), bufferProto.pixel_format(),
- bufferProto.usage()));
+ std::make_shared<fake::BufferData>(bufferProto.buffer_id(), bufferProto.width(),
+ bufferProto.height(), bufferProto.pixel_format(),
+ bufferProto.usage());
+ resolvedComposerState.externalTexture =
+ std::make_shared<FakeExternalTexture>(layer.bufferData->getWidth(),
+ layer.bufferData->getHeight(),
+ layer.bufferData->getId(),
+ layer.bufferData->getPixelFormat(),
+ layer.bufferData->getUsage());
layer.bufferData->frameNumber = bufferProto.frame_number();
layer.bufferData->flags = ftl::Flags<BufferData::BufferDataChange>(bufferProto.flags());
layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
@@ -445,26 +444,10 @@
}
if (proto.what() & layer_state_t::eReparent) {
- int64_t layerId = proto.parent_id();
- if (layerId == -1) {
- layer.parentSurfaceControlForChild = nullptr;
- } else {
- layer.parentSurfaceControlForChild =
- sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
- mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
- static_cast<int32_t>(layerId), "");
- }
+ resolvedComposerState.parentId = proto.parent_id();
}
if (proto.what() & layer_state_t::eRelativeLayerChanged) {
- int64_t layerId = proto.relative_parent_id();
- if (layerId == -1) {
- layer.relativeLayerSurfaceControl = nullptr;
- } else {
- layer.relativeLayerSurfaceControl =
- sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
- mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
- static_cast<int32_t>(layerId), "");
- }
+ resolvedComposerState.relativeParentId = proto.relative_parent_id();
layer.z = proto.z();
}
@@ -484,29 +467,23 @@
inputInfo.setInputConfig(gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER,
windowInfoProto.has_wallpaper());
inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
- const proto::LayerState_Transform& transformProto = windowInfoProto.transform();
+ const proto::Transform& transformProto = windowInfoProto.transform();
inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
transformProto.dsdy());
inputInfo.transform.set(transformProto.tx(), transformProto.ty());
inputInfo.replaceTouchableRegionWithCrop =
windowInfoProto.replace_touchable_region_with_crop();
- int64_t layerId = windowInfoProto.crop_layer_id();
- if (layerId != -1) {
- inputInfo.touchableRegionCropHandle =
- mMapper->getLayerHandle(static_cast<int32_t>(layerId));
- } else {
- inputInfo.touchableRegionCropHandle = wp<IBinder>();
- }
+ resolvedComposerState.touchCropId = windowInfoProto.crop_layer_id();
layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
}
if (proto.what() & layer_state_t::eBackgroundColorChanged) {
- layer.bgColorAlpha = proto.bg_color_alpha();
+ layer.bgColor.a = proto.bg_color_alpha();
layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace());
const proto::LayerState_Color3& colorProto = proto.color();
- layer.color.r = colorProto.r();
- layer.color.g = colorProto.g();
- layer.color.b = colorProto.b();
+ layer.bgColor.r = colorProto.r();
+ layer.bgColor.g = colorProto.g();
+ layer.bgColor.b = colorProto.b();
}
if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) {
layer.colorSpaceAgnostic = proto.color_space_agnostic();
@@ -568,4 +545,62 @@
return display;
}
+void asProto(proto::Transform* proto, const ui::Transform& transform) {
+ proto->set_dsdx(transform.dsdx());
+ proto->set_dtdx(transform.dtdx());
+ proto->set_dtdy(transform.dtdy());
+ proto->set_dsdy(transform.dsdy());
+ proto->set_tx(transform.tx());
+ proto->set_ty(transform.ty());
+}
+
+proto::DisplayInfo TransactionProtoParser::toProto(const frontend::DisplayInfo& displayInfo,
+ uint32_t layerStack) {
+ proto::DisplayInfo proto;
+ proto.set_layer_stack(layerStack);
+ proto.set_display_id(displayInfo.info.displayId);
+ proto.set_logical_width(displayInfo.info.logicalWidth);
+ proto.set_logical_height(displayInfo.info.logicalHeight);
+ asProto(proto.mutable_transform_inverse(), displayInfo.info.transform);
+ asProto(proto.mutable_transform(), displayInfo.transform);
+ proto.set_receives_input(displayInfo.receivesInput);
+ proto.set_is_secure(displayInfo.isSecure);
+ proto.set_is_primary(displayInfo.isPrimary);
+ proto.set_is_virtual(displayInfo.isVirtual);
+ proto.set_rotation_flags((int)displayInfo.rotationFlags);
+ proto.set_transform_hint((int)displayInfo.transformHint);
+ return proto;
+}
+
+void fromProto2(ui::Transform& outTransform, const proto::Transform& proto) {
+ outTransform.set(proto.dsdx(), proto.dtdx(), proto.dtdy(), proto.dsdy());
+ outTransform.set(proto.tx(), proto.ty());
+}
+
+frontend::DisplayInfo TransactionProtoParser::fromProto(const proto::DisplayInfo& proto) {
+ frontend::DisplayInfo displayInfo;
+ displayInfo.info.displayId = proto.display_id();
+ displayInfo.info.logicalWidth = proto.logical_width();
+ displayInfo.info.logicalHeight = proto.logical_height();
+ fromProto2(displayInfo.info.transform, proto.transform_inverse());
+ fromProto2(displayInfo.transform, proto.transform());
+ displayInfo.receivesInput = proto.receives_input();
+ displayInfo.isSecure = proto.is_secure();
+ displayInfo.isPrimary = proto.is_primary();
+ displayInfo.isPrimary = proto.is_virtual();
+ displayInfo.rotationFlags = (ui::Transform::RotationFlags)proto.rotation_flags();
+ displayInfo.transformHint = (ui::Transform::RotationFlags)proto.transform_hint();
+ return displayInfo;
+}
+
+void TransactionProtoParser::fromProto(
+ const google::protobuf::RepeatedPtrField<proto::DisplayInfo>& proto,
+ display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> outDisplayInfos) {
+ outDisplayInfos.clear();
+ for (const proto::DisplayInfo& displayInfo : proto) {
+ outDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(displayInfo.layer_stack()),
+ fromProto(displayInfo));
+ }
+}
+
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 2232bb9..50944fc 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -18,30 +18,17 @@
#include <gui/fake/BufferData.h>
#include <layerproto/TransactionProto.h>
#include <utils/RefBase.h>
+#include "Display/DisplayMap.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "FrontEnd/LayerCreationArgs.h"
#include "TransactionState.h"
namespace android::surfaceflinger {
-struct TracingLayerCreationArgs {
- int32_t layerId;
- std::string name;
- uint32_t flags = 0;
- int32_t parentId = -1;
- int32_t mirrorFromId = -1;
-};
-
-struct TracingLayerState : layer_state_t {
- uint64_t bufferId;
- uint32_t bufferHeight;
- uint32_t bufferWidth;
- int32_t pixelFormat;
- uint64_t bufferUsage;
+struct TracingLayerState : ResolvedComposerState {
bool hasSidebandStream;
- int32_t parentId;
- int32_t relativeParentId;
- int32_t inputCropId;
- TracingLayerCreationArgs args;
+ LayerCreationArgs args;
};
class TransactionProtoParser {
@@ -51,40 +38,30 @@
class FlingerDataMapper {
public:
virtual ~FlingerDataMapper() = default;
- virtual sp<IBinder> getLayerHandle(int32_t /* layerId */) const { return nullptr; }
- virtual int64_t getLayerId(const sp<IBinder>& /* layerHandle */) const { return -1; }
- virtual int64_t getLayerId(BBinder* /* layerHandle */) const { return -1; }
virtual sp<IBinder> getDisplayHandle(int32_t /* displayId */) const { return nullptr; }
virtual int32_t getDisplayId(const sp<IBinder>& /* displayHandle */) const { return -1; }
- virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
- uint32_t height, int32_t pixelFormat,
- uint64_t usage) const {
- return std::make_shared<fake::BufferData>(bufferId, width, height, pixelFormat, usage);
- }
- virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */,
- uint64_t* /* outBufferId */,
- uint32_t* /* outWidth */,
- uint32_t* /* outHeight */,
- int32_t* /* outPixelFormat */,
- uint64_t* /* outUsage */) const {}
};
TransactionProtoParser(std::unique_ptr<FlingerDataMapper> provider)
: mMapper(std::move(provider)) {}
proto::TransactionState toProto(const TransactionState&);
- proto::TransactionState toProto(const std::map<int32_t /* layerId */, TracingLayerState>&);
- proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
+ proto::TransactionState toProto(const std::map<uint32_t /* layerId */, TracingLayerState>&);
+ proto::LayerCreationArgs toProto(const LayerCreationArgs& args);
+ proto::LayerState toProto(const ResolvedComposerState&);
+ proto::DisplayInfo toProto(const frontend::DisplayInfo&, uint32_t layerStack);
TransactionState fromProto(const proto::TransactionState&);
void mergeFromProto(const proto::LayerState&, TracingLayerState& outState);
- void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+ void fromProto(const proto::LayerCreationArgs&, LayerCreationArgs& outArgs);
std::unique_ptr<FlingerDataMapper> mMapper;
+ frontend::DisplayInfo fromProto(const proto::DisplayInfo&);
+ void fromProto(const google::protobuf::RepeatedPtrField<proto::DisplayInfo>&,
+ display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> outDisplayInfos);
private:
- proto::LayerState toProto(const layer_state_t&);
proto::DisplayState toProto(const DisplayState&);
- void fromProto(const proto::LayerState&, layer_state_t& out);
+ void fromProto(const proto::LayerState&, ResolvedComposerState& out);
DisplayState fromProto(const proto::DisplayState&);
};
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index cb5320b..26ed878 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -23,75 +23,14 @@
#include <utils/SystemClock.h>
#include <utils/Trace.h>
-#include "ClientCache.h"
+#include "Client.h"
+#include "FrontEnd/LayerCreationArgs.h"
#include "TransactionTracing.h"
-#include "renderengine/ExternalTexture.h"
namespace android {
-// Keeps the binder address as the layer id so we can avoid holding the tracing lock in the
-// binder thread.
-class FlatDataMapper : public TransactionProtoParser::FlingerDataMapper {
-public:
- virtual int64_t getLayerId(const sp<IBinder>& layerHandle) const {
- if (layerHandle == nullptr) {
- return -1;
- }
-
- return reinterpret_cast<int64_t>(layerHandle->localBinder());
- }
-
- void getGraphicBufferPropertiesFromCache(client_cache_t cachedBuffer, uint64_t* outBufferId,
- uint32_t* outWidth, uint32_t* outHeight,
- int32_t* outPixelFormat,
- uint64_t* outUsage) const override {
- std::shared_ptr<renderengine::ExternalTexture> buffer =
- ClientCache::getInstance().get(cachedBuffer);
- if (!buffer || !buffer->getBuffer()) {
- *outBufferId = 0;
- *outWidth = 0;
- *outHeight = 0;
- *outPixelFormat = 0;
- *outUsage = 0;
- return;
- }
-
- *outBufferId = buffer->getId();
- *outWidth = buffer->getWidth();
- *outHeight = buffer->getHeight();
- *outPixelFormat = buffer->getPixelFormat();
- *outUsage = buffer->getUsage();
- return;
- }
-};
-
-class FlingerDataMapper : public FlatDataMapper {
- std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
-
-public:
- FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
- : mLayerHandles(layerHandles) {}
-
- int64_t getLayerId(const sp<IBinder>& layerHandle) const override {
- if (layerHandle == nullptr) {
- return -1;
- }
- return getLayerId(layerHandle->localBinder());
- }
-
- int64_t getLayerId(BBinder* localBinder) const {
- auto it = mLayerHandles.find(localBinder);
- if (it == mLayerHandles.end()) {
- ALOGW("Could not find layer handle %p", localBinder);
- return -1;
- }
- return it->second;
- }
-};
-
TransactionTracing::TransactionTracing()
- : mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)),
- mLockfreeProtoParser(std::make_unique<FlatDataMapper>()) {
+ : mProtoParser(std::make_unique<TransactionProtoParser::FlingerDataMapper>()) {
std::scoped_lock lock(mTraceLock);
mBuffer.setSize(mBufferSizeInBytes);
@@ -137,84 +76,77 @@
auto timeOffsetNs = static_cast<std::uint64_t>(systemTime(SYSTEM_TIME_REALTIME) -
systemTime(SYSTEM_TIME_MONOTONIC));
proto.set_real_to_elapsed_time_offset_nanos(timeOffsetNs);
+ proto.set_version(TRACING_VERSION);
return proto;
}
void TransactionTracing::dump(std::string& result) const {
std::scoped_lock lock(mTraceLock);
- base::StringAppendF(&result,
- " queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
- mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
- mStartingStates.size());
+ base::StringAppendF(&result, " queued transactions=%zu created layers=%zu states=%zu\n",
+ mQueuedTransactions.size(), mCreatedLayers.size(), mStartingStates.size());
mBuffer.dump(result);
}
void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
- proto::TransactionState* state =
- new proto::TransactionState(mLockfreeProtoParser.toProto(transaction));
+ proto::TransactionState* state = new proto::TransactionState(mProtoParser.toProto(transaction));
mTransactionQueue.push(state);
}
-TransactionTracing::CommittedTransactions&
-TransactionTracing::findOrCreateCommittedTransactionRecord(int64_t vsyncId) {
- for (auto& pendingTransaction : mPendingTransactions) {
- if (pendingTransaction.vsyncId == vsyncId) {
- return pendingTransaction;
- }
+void TransactionTracing::addCommittedTransactions(
+ int64_t vsyncId, nsecs_t commitTime, frontend::Update& newUpdate,
+ const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
+ bool displayInfoChanged) {
+ CommittedUpdates update;
+ update.vsyncId = vsyncId;
+ update.timestamp = commitTime;
+ update.transactionIds.reserve(newUpdate.transactions.size());
+ for (const auto& transaction : newUpdate.transactions) {
+ update.transactionIds.emplace_back(transaction.id);
}
-
- CommittedTransactions committedTransactions;
- committedTransactions.vsyncId = vsyncId;
- committedTransactions.timestamp = systemTime();
- mPendingTransactions.emplace_back(committedTransactions);
- return mPendingTransactions.back();
-}
-
-void TransactionTracing::onLayerAddedToDrawingState(int layerId, int64_t vsyncId) {
- CommittedTransactions& committedTransactions = findOrCreateCommittedTransactionRecord(vsyncId);
- committedTransactions.createdLayerIds.emplace_back(layerId);
-}
-
-void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
- int64_t vsyncId) {
- CommittedTransactions& committedTransactions = findOrCreateCommittedTransactionRecord(vsyncId);
- committedTransactions.transactionIds.reserve(transactions.size());
- for (const auto& transaction : transactions) {
- committedTransactions.transactionIds.emplace_back(transaction.id);
+ update.displayInfoChanged = displayInfoChanged;
+ if (displayInfoChanged) {
+ update.displayInfos = displayInfos;
}
+ update.createdLayers = std::move(newUpdate.layerCreationArgs);
+ newUpdate.layerCreationArgs.clear();
+ update.destroyedLayerHandles.reserve(newUpdate.destroyedHandles.size());
+ for (uint32_t handle : newUpdate.destroyedHandles) {
+ update.destroyedLayerHandles.push_back(handle);
+ }
+ mPendingUpdates.emplace_back(update);
tryPushToTracingThread();
}
void TransactionTracing::loop() {
while (true) {
- std::vector<CommittedTransactions> committedTransactions;
- std::vector<int32_t> removedLayers;
+ std::vector<CommittedUpdates> committedUpdates;
+ std::vector<uint32_t> destroyedLayers;
{
std::unique_lock<std::mutex> lock(mMainThreadLock);
base::ScopedLockAssertion assumeLocked(mMainThreadLock);
mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
- return mDone || !mCommittedTransactions.empty();
+ return mDone || !mUpdates.empty();
});
if (mDone) {
- mCommittedTransactions.clear();
- mRemovedLayers.clear();
+ mUpdates.clear();
+ mDestroyedLayers.clear();
break;
}
- removedLayers = std::move(mRemovedLayers);
- mRemovedLayers.clear();
- committedTransactions = std::move(mCommittedTransactions);
- mCommittedTransactions.clear();
+ destroyedLayers = std::move(mDestroyedLayers);
+ mDestroyedLayers.clear();
+ committedUpdates = std::move(mUpdates);
+ mUpdates.clear();
} // unlock mMainThreadLock
- if (!committedTransactions.empty() || !removedLayers.empty()) {
- addEntry(committedTransactions, removedLayers);
+ if (!committedUpdates.empty() || !destroyedLayers.empty()) {
+ addEntry(committedUpdates, destroyedLayers);
}
}
}
-void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
- const std::vector<int32_t>& removedLayers) {
+void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committedUpdates,
+ const std::vector<uint32_t>& destroyedLayers) {
ATRACE_CALL();
std::scoped_lock lock(mTraceLock);
std::vector<std::string> removedEntries;
@@ -222,59 +154,27 @@
while (auto incomingTransaction = mTransactionQueue.pop()) {
auto transaction = *incomingTransaction;
- int32_t layerCount = transaction.layer_changes_size();
- for (int i = 0; i < layerCount; i++) {
- auto layer = transaction.mutable_layer_changes(i);
- layer->set_layer_id(
- mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(layer->layer_id())));
- if ((layer->what() & layer_state_t::eReparent) && layer->parent_id() != -1) {
- layer->set_parent_id(
- mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
- layer->parent_id())));
- }
-
- if ((layer->what() & layer_state_t::eRelativeLayerChanged) &&
- layer->relative_parent_id() != -1) {
- layer->set_relative_parent_id(
- mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
- layer->relative_parent_id())));
- }
-
- if (layer->has_window_info_handle() &&
- layer->window_info_handle().crop_layer_id() != -1) {
- auto input = layer->mutable_window_info_handle();
- input->set_crop_layer_id(
- mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
- input->crop_layer_id())));
- }
- }
mQueuedTransactions[incomingTransaction->transaction_id()] = transaction;
delete incomingTransaction;
}
- for (const CommittedTransactions& entry : committedTransactions) {
- entryProto.set_elapsed_realtime_nanos(entry.timestamp);
- entryProto.set_vsync_id(entry.vsyncId);
+ for (const CommittedUpdates& update : committedUpdates) {
+ entryProto.set_elapsed_realtime_nanos(update.timestamp);
+ entryProto.set_vsync_id(update.vsyncId);
entryProto.mutable_added_layers()->Reserve(
- static_cast<int32_t>(entry.createdLayerIds.size()));
+ static_cast<int32_t>(update.createdLayers.size()));
- for (const int32_t& id : entry.createdLayerIds) {
- auto it = mCreatedLayers.find(id);
- if (it != mCreatedLayers.end()) {
- entryProto.mutable_added_layers()->Add(std::move(it->second));
- mCreatedLayers.erase(it);
- } else {
- ALOGW("Could not created layer with id %d", id);
- }
+ for (const auto& args : update.createdLayers) {
+ entryProto.mutable_added_layers()->Add(std::move(mProtoParser.toProto(args)));
}
- entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
- for (auto& removedLayer : removedLayers) {
- entryProto.mutable_removed_layers()->Add(removedLayer);
- mCreatedLayers.erase(removedLayer);
+ entryProto.mutable_destroyed_layers()->Reserve(
+ static_cast<int32_t>(destroyedLayers.size()));
+ for (auto& destroyedLayer : destroyedLayers) {
+ entryProto.mutable_destroyed_layers()->Add(destroyedLayer);
}
entryProto.mutable_transactions()->Reserve(
- static_cast<int32_t>(entry.transactionIds.size()));
- for (const uint64_t& id : entry.transactionIds) {
+ static_cast<int32_t>(update.transactionIds.size()));
+ for (const uint64_t& id : update.transactionIds) {
auto it = mQueuedTransactions.find(id);
if (it != mQueuedTransactions.end()) {
entryProto.mutable_transactions()->Add(std::move(it->second));
@@ -284,13 +184,21 @@
}
}
- entryProto.mutable_removed_layer_handles()->Reserve(
- static_cast<int32_t>(mRemovedLayerHandles.size()));
- for (auto& [handle, layerId] : mRemovedLayerHandles) {
- entryProto.mutable_removed_layer_handles()->Add(layerId);
- mLayerHandles.erase(handle);
+ entryProto.mutable_destroyed_layer_handles()->Reserve(
+ static_cast<int32_t>(update.destroyedLayerHandles.size()));
+ for (auto layerId : update.destroyedLayerHandles) {
+ entryProto.mutable_destroyed_layer_handles()->Add(layerId);
}
- mRemovedLayerHandles.clear();
+
+ entryProto.set_displays_changed(update.displayInfoChanged);
+ if (update.displayInfoChanged) {
+ entryProto.mutable_displays()->Reserve(
+ static_cast<int32_t>(update.displayInfos.size()));
+ for (auto& [layerStack, displayInfo] : update.displayInfos) {
+ entryProto.mutable_displays()->Add(
+ std::move(mProtoParser.toProto(displayInfo, layerStack.id)));
+ }
+ }
std::string serializedProto;
entryProto.SerializeToString(&serializedProto);
@@ -311,7 +219,7 @@
}
void TransactionTracing::flush(int64_t vsyncId) {
- while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
+ while (!mPendingUpdates.empty() || !mPendingDestroyedLayers.empty()) {
tryPushToTracingThread();
}
std::unique_lock<std::mutex> lock(mTraceLock);
@@ -325,54 +233,21 @@
});
}
-void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
- uint32_t flags, int parentId) {
- std::scoped_lock lock(mTraceLock);
- TracingLayerCreationArgs args{layerId, name, flags, parentId, -1 /* mirrorFromId */};
- if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
- ALOGW("Duplicate handles found. %p", layerHandle);
- }
- mLayerHandles[layerHandle] = layerId;
- mCreatedLayers[layerId] = mProtoParser.toProto(args);
-}
-
-void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId,
- const std::string& name, int mirrorFromId) {
- std::scoped_lock lock(mTraceLock);
- TracingLayerCreationArgs args{layerId, name, 0 /* flags */, -1 /* parentId */, mirrorFromId};
- if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
- ALOGW("Duplicate handles found. %p", layerHandle);
- }
- mLayerHandles[layerHandle] = layerId;
- mCreatedLayers[layerId] = mProtoParser.toProto(args);
-}
-
void TransactionTracing::onLayerRemoved(int32_t layerId) {
- mPendingRemovedLayers.emplace_back(layerId);
+ mPendingDestroyedLayers.emplace_back(layerId);
tryPushToTracingThread();
}
-void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
- std::scoped_lock lock(mTraceLock);
- auto it = mLayerHandles.find(layerHandle);
- if (it == mLayerHandles.end()) {
- ALOGW("handle not found. %p", layerHandle);
- return;
- }
- mRemovedLayerHandles.emplace_back(layerHandle, it->second);
-}
-
void TransactionTracing::tryPushToTracingThread() {
// Try to acquire the lock from main thread.
if (mMainThreadLock.try_lock()) {
// We got the lock! Collect any pending transactions and continue.
- mCommittedTransactions.insert(mCommittedTransactions.end(),
- std::make_move_iterator(mPendingTransactions.begin()),
- std::make_move_iterator(mPendingTransactions.end()));
- mPendingTransactions.clear();
- mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
- mPendingRemovedLayers.end());
- mPendingRemovedLayers.clear();
+ 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();
mMainThreadLock.unlock();
} else {
@@ -394,24 +269,28 @@
// Merge layer states to starting transaction state.
for (const proto::TransactionState& transaction : removedEntry.transactions()) {
for (const proto::LayerState& layerState : transaction.layer_changes()) {
- auto it = mStartingStates.find((int32_t)layerState.layer_id());
+ auto it = mStartingStates.find(layerState.layer_id());
if (it == mStartingStates.end()) {
- ALOGW("Could not find layer id %d", (int32_t)layerState.layer_id());
+ ALOGW("Could not find layer id %d", layerState.layer_id());
continue;
}
mProtoParser.mergeFromProto(layerState, it->second);
}
}
- for (const int32_t removedLayerHandleId : removedEntry.removed_layer_handles()) {
- mRemovedLayerHandlesAtStart.insert(removedLayerHandleId);
+ for (const uint32_t destroyedLayerHandleId : removedEntry.destroyed_layer_handles()) {
+ mRemovedLayerHandlesAtStart.insert(destroyedLayerHandleId);
}
// Clean up stale starting states since the layer has been removed and the buffer does not
// contain any references to the layer.
- for (const int32_t removedLayerId : removedEntry.removed_layers()) {
- mStartingStates.erase(removedLayerId);
- mRemovedLayerHandlesAtStart.erase(removedLayerId);
+ for (const uint32_t destroyedLayerId : removedEntry.destroyed_layers()) {
+ mStartingStates.erase(destroyedLayerId);
+ mRemovedLayerHandlesAtStart.erase(destroyedLayerId);
+ }
+
+ if (removedEntry.displays_changed()) {
+ mProtoParser.fromProto(removedEntry.displays(), mStartingDisplayInfos);
}
}
@@ -434,10 +313,15 @@
transactionProto.set_post_time(mStartingTimestamp);
entryProto->mutable_transactions()->Add(std::move(transactionProto));
- entryProto->mutable_removed_layer_handles()->Reserve(
+ entryProto->mutable_destroyed_layer_handles()->Reserve(
static_cast<int32_t>(mRemovedLayerHandlesAtStart.size()));
- for (const int32_t removedLayerHandleId : mRemovedLayerHandlesAtStart) {
- entryProto->mutable_removed_layer_handles()->Add(removedLayerHandleId);
+ for (const uint32_t destroyedLayerHandleId : mRemovedLayerHandlesAtStart) {
+ entryProto->mutable_destroyed_layer_handles()->Add(destroyedLayerHandleId);
+ }
+
+ entryProto->mutable_displays()->Reserve(static_cast<int32_t>(mStartingDisplayInfos.size()));
+ for (auto& [layerStack, displayInfo] : mStartingDisplayInfos) {
+ entryProto->mutable_displays()->Add(mProtoParser.toProto(displayInfo, layerStack.id));
}
}
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index ae01d3c..f27e7a9 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -25,8 +25,12 @@
#include <mutex>
#include <thread>
-#include "RingBuffer.h"
+#include "Display/DisplayMap.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/Update.h"
#include "LocklessStack.h"
+#include "RingBuffer.h"
#include "TransactionProtoParser.h"
using namespace android::surfaceflinger;
@@ -55,22 +59,22 @@
~TransactionTracing();
void addQueuedTransaction(const TransactionState&);
- void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
+ void addCommittedTransactions(
+ int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
+ const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
+ bool displayInfoChanged);
status_t writeToFile(std::string filename = FILE_NAME);
void setBufferSize(size_t bufferSizeInBytes);
- void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
- int parentId);
- void onMirrorLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
- int mirrorFromId);
void onLayerRemoved(int layerId);
- void onHandleRemoved(BBinder* layerHandle);
- void onLayerAddedToDrawingState(int layerId, int64_t vsyncId);
void dump(std::string&) const;
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
+ static constexpr auto TRACING_VERSION = 1;
private:
friend class TransactionTracingTest;
+ friend class SurfaceFlinger;
static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
@@ -83,16 +87,12 @@
LocklessStack<proto::TransactionState> mTransactionQueue;
nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
std::unordered_map<int, proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
- std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
+ std::map<uint32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
+ display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mStartingDisplayInfos
GUARDED_BY(mTraceLock);
- std::vector<std::pair<BBinder* /* layerHandle */, int32_t /* layerId */>> mRemovedLayerHandles
- GUARDED_BY(mTraceLock);
- std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
- std::set<int32_t /* layerId */> mRemovedLayerHandlesAtStart GUARDED_BY(mTraceLock);
- TransactionProtoParser mProtoParser GUARDED_BY(mTraceLock);
- // Parses the transaction to proto without holding any tracing locks so we can generate proto
- // in the binder thread without any contention.
- TransactionProtoParser mLockfreeProtoParser;
+
+ std::set<uint32_t /* layerId */> mRemovedLayerHandlesAtStart GUARDED_BY(mTraceLock);
+ TransactionProtoParser mProtoParser;
// We do not want main thread to block so main thread will try to acquire mMainThreadLock,
// otherwise will push data to temporary container.
@@ -101,27 +101,29 @@
bool mDone GUARDED_BY(mMainThreadLock) = false;
std::condition_variable mTransactionsAvailableCv;
std::condition_variable mTransactionsAddedToBufferCv;
- struct CommittedTransactions {
+ struct CommittedUpdates {
std::vector<uint64_t> transactionIds;
- std::vector<int32_t> createdLayerIds;
+ std::vector<LayerCreationArgs> createdLayers;
+ std::vector<uint32_t> destroyedLayerHandles;
+ bool displayInfoChanged;
+ display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
int64_t vsyncId;
int64_t timestamp;
};
- std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock);
- std::vector<CommittedTransactions> mPendingTransactions; // only accessed by main thread
+ std::vector<CommittedUpdates> mUpdates GUARDED_BY(mMainThreadLock);
+ std::vector<CommittedUpdates> mPendingUpdates; // only accessed by main thread
- std::vector<int32_t /* layerId */> mRemovedLayers GUARDED_BY(mMainThreadLock);
- std::vector<int32_t /* layerId */> mPendingRemovedLayers; // only accessed by main thread
+ std::vector<uint32_t /* layerId */> mDestroyedLayers GUARDED_BY(mMainThreadLock);
+ std::vector<uint32_t /* layerId */> mPendingDestroyedLayers; // only accessed by main thread
proto::TransactionTraceFile createTraceFileProto() const;
void loop();
- void addEntry(const std::vector<CommittedTransactions>& committedTransactions,
- const std::vector<int32_t>& removedLayers) EXCLUDES(mTraceLock);
+ void addEntry(const std::vector<CommittedUpdates>& committedTransactions,
+ const std::vector<uint32_t>& removedLayers) EXCLUDES(mTraceLock);
int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
- CommittedTransactions& findOrCreateCommittedTransactionRecord(int64_t vsyncId);
// TEST
// Wait until all the committed transactions for the specified vsync id are added to the buffer.
void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index ab98dbf..0a7101c 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -14,175 +14,31 @@
* limitations under the License.
*/
+#include <memory>
+#include <vector>
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/RequestedLayerState.h"
+#include "Tracing/LayerTracing.h"
+#include "TransactionState.h"
+#include "cutils/properties.h"
#undef LOG_TAG
#define LOG_TAG "LayerTraceGenerator"
//#define LOG_NDEBUG 0
-#include <TestableSurfaceFlinger.h>
#include <Tracing/TransactionProtoParser.h>
-#include <binder/IPCThreadState.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
#include <gui/LayerState.h>
#include <log/log.h>
-#include <mock/MockEventThread.h>
#include <renderengine/ExternalTexture.h>
-#include <renderengine/mock/RenderEngine.h>
#include <utils/String16.h>
+#include <filesystem>
+#include <fstream>
#include <string>
+#include "LayerProtoHelper.h"
#include "LayerTraceGenerator.h"
namespace android {
-
-class Factory final : public surfaceflinger::Factory {
-public:
- ~Factory() = default;
-
- std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; }
-
- std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
- Fps /*currentRefreshRate*/) override {
- return std::make_unique<scheduler::FakePhaseOffsets>();
- }
-
- sp<StartPropertySetThread> createStartPropertySetThread(
- bool /* timestampPropertyValue */) override {
- return sp<StartPropertySetThread>();
- }
-
- sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& /* creationArgs */) override {
- return sp<DisplayDevice>();
- }
-
- sp<GraphicBuffer> createGraphicBuffer(uint32_t /* width */, uint32_t /* height */,
- PixelFormat /* format */, uint32_t /* layerCount */,
- uint64_t /* usage */,
- std::string /* requestorName */) override {
- return sp<GraphicBuffer>();
- }
-
- void createBufferQueue(sp<IGraphicBufferProducer>* /* outProducer */,
- sp<IGraphicBufferConsumer>* /* outConsumer */,
- bool /* consumerIsSurfaceFlinger */) override {}
-
- std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
- const sp<IGraphicBufferProducer>& /* producer */) override {
- return nullptr;
- }
-
- std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
- return compositionengine::impl::createCompositionEngine();
- }
-
- sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) {
- return sp<Layer>::make(args);
- }
-
- sp<Layer> createEffectLayer(const LayerCreationArgs& args) { return sp<Layer>::make(args); }
-
- sp<LayerFE> createLayerFE(const std::string& layerName) { return sp<LayerFE>::make(layerName); }
-
- std::unique_ptr<FrameTracer> createFrameTracer() override {
- return std::make_unique<testing::NiceMock<mock::FrameTracer>>();
- }
-
- std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
- return std::make_unique<testing::NiceMock<mock::FrameTimeline>>(timeStats,
- surfaceFlingerPid);
- }
-};
-
-class FakeExternalTexture : public renderengine::ExternalTexture {
- const sp<GraphicBuffer> mNullBuffer = nullptr;
- uint32_t mWidth;
- uint32_t mHeight;
- uint64_t mId;
- PixelFormat mPixelFormat;
- uint64_t mUsage;
-
-public:
- FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
- uint64_t usage)
- : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
- const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; }
- bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
- return getId() == other.getId();
- }
- uint32_t getWidth() const override { return mWidth; }
- uint32_t getHeight() const override { return mHeight; }
- uint64_t getId() const override { return mId; }
- PixelFormat getPixelFormat() const override { return mPixelFormat; }
- uint64_t getUsage() const override { return mUsage; }
- ~FakeExternalTexture() = default;
-};
-
-class MockSurfaceFlinger : public SurfaceFlinger {
-public:
- MockSurfaceFlinger(Factory& factory)
- : SurfaceFlinger(factory, SurfaceFlinger::SkipInitialization) {}
- std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
- BufferData& bufferData, const char* /* layerName */,
- uint64_t /* transactionId */) override {
- return std::make_shared<FakeExternalTexture>(bufferData.getWidth(), bufferData.getHeight(),
- bufferData.getId(),
- bufferData.getPixelFormat(),
- bufferData.getUsage());
- };
-
- // b/220017192 migrate from transact codes to ISurfaceComposer apis
- void setLayerTracingFlags(int32_t flags) {
- Parcel data;
- Parcel reply;
- data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
- data.writeInt32(flags);
- transact(1033, data, &reply, 0 /* flags */);
- }
-
- void setLayerTraceSize(int32_t sizeInKb) {
- Parcel data;
- Parcel reply;
- data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
- data.writeInt32(sizeInKb);
- transact(1029, data, &reply, 0 /* flags */);
- }
-
- void startLayerTracing(int64_t traceStartTime) {
- Parcel data;
- Parcel reply;
- data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
- data.writeInt32(1);
- data.writeInt64(traceStartTime);
- transact(1025, data, &reply, 0 /* flags */);
- }
-
- void stopLayerTracing(const char* tracePath) {
- Parcel data;
- Parcel reply;
- data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
- data.writeInt32(2);
- data.writeCString(tracePath);
- transact(1025, data, &reply, 0 /* flags */);
- }
-};
-
-class TraceGenFlingerDataMapper : public TransactionProtoParser::FlingerDataMapper {
-public:
- std::unordered_map<int32_t /*layerId*/, sp<IBinder> /* handle */> mLayerHandles;
- sp<IBinder> getLayerHandle(int32_t layerId) const override {
- if (layerId == -1) {
- ALOGE("Error: Called with layer=%d", layerId);
- return nullptr;
- }
- auto it = mLayerHandles.find(layerId);
- if (it == mLayerHandles.end()) {
- ALOGE("Error: Could not find handle for layer=%d", layerId);
- return nullptr;
- }
- return it->second;
- }
-};
+using namespace ftl::flag_operators;
bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile,
const char* outputLayersTracePath) {
@@ -191,91 +47,122 @@
return false;
}
- Factory mFactory;
- sp<MockSurfaceFlinger> flinger = sp<MockSurfaceFlinger>::make(mFactory);
- TestableSurfaceFlinger mFlinger(flinger);
- mFlinger.setupRenderEngine(
- std::make_unique<testing::NiceMock<renderengine::mock::RenderEngine>>());
- mock::VsyncController* mVsyncController = new testing::NiceMock<mock::VsyncController>();
- mock::VSyncTracker* mVSyncTracker = new testing::NiceMock<mock::VSyncTracker>();
- mock::EventThread* mEventThread = new testing::NiceMock<mock::EventThread>();
- mock::EventThread* mSFEventThread = new testing::NiceMock<mock::EventThread>();
- mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
- std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
- std::unique_ptr<EventThread>(mEventThread),
- std::unique_ptr<EventThread>(mSFEventThread),
- TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
- TestableSurfaceFlinger::kOneDisplayMode, true /* useNiceMock */);
+ TransactionProtoParser parser(std::make_unique<TransactionProtoParser::FlingerDataMapper>());
- Hwc2::mock::Composer* mComposer = new testing::NiceMock<Hwc2::mock::Composer>();
- mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
- mFlinger.mutableMaxRenderTargetSize() = 16384;
+ // frontend
+ frontend::LayerLifecycleManager lifecycleManager;
+ frontend::LayerHierarchyBuilder hierarchyBuilder{{}};
+ frontend::LayerSnapshotBuilder snapshotBuilder;
+ display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
- flinger->setLayerTracingFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS);
- flinger->setLayerTraceSize(512 * 1024); // 512MB buffer size
- flinger->startLayerTracing(traceFile.entry(0).elapsed_realtime_nanos());
- std::unique_ptr<TraceGenFlingerDataMapper> mapper =
- std::make_unique<TraceGenFlingerDataMapper>();
- TraceGenFlingerDataMapper* dataMapper = mapper.get();
- TransactionProtoParser parser(std::move(mapper));
+ renderengine::ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.surface_flinger.supports_background_blur", value, "0");
+ bool supportsBlur = atoi(value);
+
+ LayerTracing layerTracing;
+ layerTracing.setTraceFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS);
+ layerTracing.setBufferSize(512 * 1024 * 1024); // 512MB buffer size
+ layerTracing.enable();
ALOGD("Generating %d transactions...", traceFile.entry_size());
for (int i = 0; i < traceFile.entry_size(); i++) {
+ // parse proto
proto::TransactionTraceEntry entry = traceFile.entry(i);
ALOGV(" Entry %04d/%04d for time=%" PRId64 " vsyncid=%" PRId64
" layers +%d -%d handles -%d transactions=%d",
i, traceFile.entry_size(), entry.elapsed_realtime_nanos(), entry.vsync_id(),
- entry.added_layers_size(), entry.removed_layers_size(),
- entry.removed_layer_handles_size(), entry.transactions_size());
+ entry.added_layers_size(), entry.destroyed_layers_size(),
+ entry.destroyed_layer_handles_size(), entry.transactions_size());
+ std::vector<std::unique_ptr<frontend::RequestedLayerState>> addedLayers;
+ addedLayers.reserve((size_t)entry.added_layers_size());
for (int j = 0; j < entry.added_layers_size(); j++) {
- // create layers
- TracingLayerCreationArgs tracingArgs;
- parser.fromProto(entry.added_layers(j), tracingArgs);
-
- gui::CreateSurfaceResult outResult;
- LayerCreationArgs args(mFlinger.flinger(), nullptr /* client */, tracingArgs.name,
- tracingArgs.flags, LayerMetadata(),
- std::make_optional<int32_t>(tracingArgs.layerId));
-
- if (tracingArgs.mirrorFromId == -1) {
- sp<IBinder> parentHandle = nullptr;
- if ((tracingArgs.parentId != -1) &&
- (dataMapper->mLayerHandles.find(tracingArgs.parentId) ==
- dataMapper->mLayerHandles.end())) {
- args.addToRoot = false;
- } else if (tracingArgs.parentId != -1) {
- parentHandle = dataMapper->getLayerHandle(tracingArgs.parentId);
- }
- mFlinger.createLayer(args, parentHandle, outResult);
- } else {
- sp<IBinder> mirrorFromHandle = dataMapper->getLayerHandle(tracingArgs.mirrorFromId);
- mFlinger.mirrorLayer(args, mirrorFromHandle, outResult);
- }
- LOG_ALWAYS_FATAL_IF(outResult.layerId != tracingArgs.layerId,
- "Could not create layer expected:%d actual:%d", tracingArgs.layerId,
- outResult.layerId);
- dataMapper->mLayerHandles[tracingArgs.layerId] = outResult.handle;
+ LayerCreationArgs args;
+ parser.fromProto(entry.added_layers(j), args);
+ addedLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
}
+ std::vector<TransactionState> transactions;
+ transactions.reserve((size_t)entry.transactions_size());
for (int j = 0; j < entry.transactions_size(); j++) {
// apply transactions
TransactionState transaction = parser.fromProto(entry.transactions(j));
- mFlinger.setTransactionStateInternal(transaction);
+ transactions.emplace_back(std::move(transaction));
}
- const auto frameTime = TimePoint::fromNs(entry.elapsed_realtime_nanos());
- const auto vsyncId = VsyncId{entry.vsync_id()};
- mFlinger.commit(frameTime, vsyncId);
-
- for (int j = 0; j < entry.removed_layer_handles_size(); j++) {
- dataMapper->mLayerHandles.erase(entry.removed_layer_handles(j));
+ std::vector<uint32_t> destroyedHandles;
+ destroyedHandles.reserve((size_t)entry.destroyed_layer_handles_size());
+ for (int j = 0; j < entry.destroyed_layer_handles_size(); j++) {
+ destroyedHandles.push_back(entry.destroyed_layer_handles(j));
}
+
+ bool displayChanged = entry.displays_changed();
+ if (displayChanged) {
+ parser.fromProto(entry.displays(), displayInfos);
+ }
+
+ // apply updates
+ lifecycleManager.addLayers(std::move(addedLayers));
+ lifecycleManager.applyTransactions(transactions);
+ lifecycleManager.onHandlesDestroyed(destroyedHandles, /*ignoreUnknownHandles=*/true);
+
+ if (lifecycleManager.getGlobalChanges().test(
+ frontend::RequestedLayerState::Changes::Hierarchy)) {
+ hierarchyBuilder.update(lifecycleManager.getLayers(),
+ lifecycleManager.getDestroyedLayers());
+ }
+
+ frontend::LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = lifecycleManager,
+ .displays = displayInfos,
+ .displayChanges = displayChanged,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = supportsBlur,
+ .forceFullDamage = false,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ snapshotBuilder.update(args);
+
+ bool visibleRegionsDirty = lifecycleManager.getGlobalChanges().any(
+ frontend::RequestedLayerState::Changes::VisibleRegion |
+ frontend::RequestedLayerState::Changes::Hierarchy |
+ frontend::RequestedLayerState::Changes::Visibility);
+
+ ALOGV(" layers:%04zu snapshots:%04zu changes:%s", lifecycleManager.getLayers().size(),
+ snapshotBuilder.getSnapshots().size(),
+ lifecycleManager.getGlobalChanges().string().c_str());
+
+ lifecycleManager.commitChanges();
+ // write layers trace
+ auto tracingFlags = LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS;
+ std::unordered_set<uint64_t> stackIdsToSkip;
+ if ((tracingFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) {
+ for (const auto& displayInfo : displayInfos) {
+ if (displayInfo.second.isVirtual) {
+ stackIdsToSkip.insert(displayInfo.first.id);
+ }
+ }
+ }
+
+ const frontend::LayerHierarchy& root = hierarchyBuilder.getHierarchy();
+
+ LayersProto layersProto;
+ for (auto& [child, variant] : root.mChildren) {
+ if (variant != frontend::LayerHierarchy::Variant::Attached ||
+ stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
+ continue;
+ }
+ LayerProtoHelper::writeHierarchyToProto(layersProto, *child, snapshotBuilder, {},
+ tracingFlags);
+ }
+
+ auto displayProtos = LayerProtoHelper::writeDisplayInfoToProto(displayInfos);
+ layerTracing.notify(visibleRegionsDirty, entry.elapsed_realtime_nanos(), entry.vsync_id(),
+ &layersProto, {}, &displayProtos);
}
-
- flinger->stopLayerTracing(outputLayersTracePath);
+ layerTracing.disable(outputLayersTracePath);
ALOGD("End of generating trace file. File written to %s", outputLayersTracePath);
- dataMapper->mLayerHandles.clear();
return true;
}
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index 9f9ae48..c440c19 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -53,9 +53,6 @@
ALOGD("Generating %s...", outputLayersTracePath);
std::cout << "Generating " << outputLayersTracePath << "\n";
- // sink any log spam from the stubbed surfaceflinger
- __android_log_set_logger([](const struct __android_log_message* /* log_message */) {});
-
if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) {
std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
return -1;
diff --git a/services/surfaceflinger/Tracing/tools/run.sh b/services/surfaceflinger/Tracing/tools/run.sh
index baa93f1..307a4d8 100644
--- a/services/surfaceflinger/Tracing/tools/run.sh
+++ b/services/surfaceflinger/Tracing/tools/run.sh
@@ -5,7 +5,15 @@
# Build, push and run layertracegenerator
$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode layertracegenerator
adb wait-for-device && adb push $OUT/system/bin/layertracegenerator /data/layertracegenerator
-echo "Writing transaction trace to file"
-adb shell service call SurfaceFlinger 1041 i32 0
-adb shell /data/layertracegenerator
+
+if [ -z "$1" ]
+ then
+ echo "Writing transaction trace to file"
+ adb shell service call SurfaceFlinger 1041 i32 0
+ adb shell /data/layertracegenerator
+ else
+ echo "Pushing transaction trace to device"
+ adb push $1 /data/transaction_trace.winscope
+ adb shell /data/layertracegenerator /data/transaction_trace.winscope
+fi
adb pull /data/misc/wmtrace/layers_trace.winscope
\ No newline at end of file
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 6c5a8b2..2daea25 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -20,6 +20,7 @@
#include <memory>
#include <mutex>
#include <vector>
+#include "FrontEnd/LayerCreationArgs.h"
#include "renderengine/ExternalTexture.h"
#include <gui/LayerState.h>
@@ -39,6 +40,10 @@
ResolvedComposerState() = default;
ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); }
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+ uint32_t layerId = UNASSIGNED_LAYER_ID;
+ uint32_t parentId = UNASSIGNED_LAYER_ID;
+ uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
+ uint32_t touchCropId = UNASSIGNED_LAYER_ID;
};
struct TransactionState {
@@ -85,7 +90,7 @@
for (auto state = states.begin(); state != states.end();) {
if (state->state.hasBufferChanges() && state->state.hasValidBuffer() &&
state->state.surface) {
- int result = visitor(state->state);
+ int result = visitor(state->state, state->externalTexture);
if (result == STOP_TRAVERSAL) return;
if (result == DELETE_AND_CONTINUE_TRAVERSAL) {
state = states.erase(state);
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index a1313e3..292083b 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -17,7 +17,6 @@
#include <ftl/small_vector.h>
#include <gui/ISurfaceComposer.h>
-#include "SurfaceFlinger.h"
#include "WindowInfosListenerInvoker.h"
namespace android {
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index a1d66a1..d60a9c4 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -16,6 +16,8 @@
#pragma once
+#include <unordered_set>
+
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/IWindowInfosReportedListener.h>
@@ -49,8 +51,6 @@
static constexpr size_t kStaticCapacity = 3;
ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
mWindowInfosListeners GUARDED_BY(mListenersMutex);
-
- sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener;
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 609fd33..6074bb7 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -226,19 +226,19 @@
TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<android::mock::VsyncController>(),
- std::make_unique<android::mock::VSyncTracker>(), selectorPtr,
+ std::make_shared<android::mock::VSyncTracker>(), selectorPtr,
std::move(modulatorPtr), callback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
- std::unique_ptr<VSyncTracker> tracker,
+ VsyncSchedule::TrackerPtr tracker,
std::shared_ptr<RefreshRateSelector> selectorPtr,
sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
: Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
- mVsyncSchedule = std::unique_ptr<VsyncSchedule>(
- new VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
- registerDisplay(displayId, std::move(selectorPtr));
+ registerDisplayInternal(displayId, std::move(selectorPtr),
+ std::shared_ptr<VsyncSchedule>(
+ new VsyncSchedule(displayId, std::move(tracker), nullptr,
+ std::move(controller))));
}
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -247,7 +247,7 @@
auto &mutableLayerHistory() { return mLayerHistory; }
- auto refreshRateSelector() { return leaderSelectorPtr(); }
+ auto refreshRateSelector() { return pacesetterSelectorPtr(); }
void replaceTouchTimer(int64_t millis) {
if (mTouchTimer) {
@@ -402,9 +402,8 @@
SurfaceFlinger *flinger() { return mFlinger.get(); }
scheduler::TestableScheduler *scheduler() { return mScheduler; }
- // Allow reading display state without locking, as if called on the SF main thread.
- auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
- return mFlinger->onInitializeDisplays();
+ void initializeDisplays() {
+ FTL_FAKE_GUARD(kMainThreadContext, mFlinger->initializeDisplays());
}
void setGlobalShadowSettings(FuzzedDataProvider *fdp) {
@@ -453,8 +452,7 @@
LayersProto layersProto = mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>());
mFlinger->dumpOffscreenLayersProto(layersProto);
- LayersTraceProto layersTraceProto{};
- mFlinger->dumpDisplayProto(layersTraceProto);
+ mFlinger->dumpDisplayProto();
result = fdp->ConsumeRandomLengthString().c_str();
mFlinger->dumpHwc(result);
@@ -542,7 +540,7 @@
mFlinger->createDisplay(String8(fdp->ConsumeRandomLengthString().c_str()),
fdp->ConsumeBool());
- onInitializeDisplays();
+ initializeDisplays();
mFlinger->getPhysicalDisplayToken(physicalDisplayId);
mFlinger->mStartPropertySetThread =
@@ -647,10 +645,10 @@
// The ISchedulerCallback argument can be nullptr for a no-op implementation.
void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
- std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
+ std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
- scheduler::ISchedulerCallback *callback = nullptr,
+ scheduler::ISchedulerCallback* callback = nullptr,
bool hasMultipleModes = false) {
constexpr DisplayModeId kModeId60{0};
DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
@@ -789,7 +787,7 @@
}
private:
- void setVsyncEnabled(bool) override {}
+ void setVsyncEnabled(PhysicalDisplayId, bool) override {}
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 61fb29a..f17d2e1 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -48,6 +48,7 @@
constexpr uint16_t kRandomStringLength = 256;
constexpr std::chrono::duration kSyncPeriod(16ms);
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
template <typename T>
void dump(T* component, FuzzedDataProvider* fdp) {
@@ -76,7 +77,7 @@
FuzzedDataProvider mFdp;
- std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
+ std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
};
PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() {
@@ -90,12 +91,13 @@
}
void SchedulerFuzzer::fuzzEventThread() {
- mVsyncSchedule = std::unique_ptr<scheduler::VsyncSchedule>(
- new scheduler::VsyncSchedule(std::make_unique<mock::VSyncTracker>(),
- std::make_unique<mock::VSyncDispatch>(), nullptr));
+ mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>(
+ new scheduler::VsyncSchedule(getPhysicalDisplayId(),
+ std::make_shared<mock::VSyncTracker>(),
+ std::make_shared<mock::VSyncDispatch>(), nullptr));
const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
- android::impl::EventThread>("fuzzer", *mVsyncSchedule, nullptr, nullptr, getVsyncPeriod,
+ android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, nullptr, getVsyncPeriod,
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
@@ -109,8 +111,7 @@
thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
thread->registerDisplayEventConnection(connection);
- thread->onScreenAcquired();
- thread->onScreenReleased();
+ thread->enableSyntheticVsync(mFdp.ConsumeBool());
dump<android::impl::EventThread>(thread.get(), &mFdp);
}
@@ -132,7 +133,7 @@
}
void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() {
- FuzzImplVSyncTracker stubTracker{mFdp.ConsumeIntegral<nsecs_t>()};
+ auto stubTracker = std::make_shared<FuzzImplVSyncTracker>(mFdp.ConsumeIntegral<nsecs_t>());
scheduler::VSyncDispatchTimerQueue
mDispatch{std::make_unique<scheduler::ControllableClock>(), stubTracker,
mFdp.ConsumeIntegral<nsecs_t>() /*dispatchGroupThreshold*/,
@@ -145,17 +146,17 @@
scheduler::VSyncDispatchTimerQueueEntry entry(
"fuzz", [](auto, auto, auto) {},
mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/);
- entry.update(stubTracker, 0);
+ entry.update(*stubTracker, 0);
entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
- stubTracker, 0);
+ *stubTracker, 0);
entry.disarm();
entry.ensureNotRunning();
entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
- stubTracker, 0);
+ *stubTracker, 0);
auto const wakeup = entry.wakeupTime();
auto const ready = entry.readyTime();
entry.callback(entry.executing(), *wakeup, *ready);
@@ -169,7 +170,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{mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize,
+ scheduler::VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
+ mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize,
minimumSamplesForPrediction,
mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
@@ -218,9 +220,9 @@
sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger());
for (int i = 0; i < historySize; ++i) {
- historyV1.record(layer1.get(), time1, time1,
+ historyV1.record(layer1->getSequence(), layer1->getLayerProps(), time1, time1,
scheduler::LayerHistory::LayerUpdateType::Buffer);
- historyV1.record(layer2.get(), time2, time2,
+ historyV1.record(layer2->getSequence(), layer2->getLayerProps(), time2, time2,
scheduler::LayerHistory::LayerUpdateType::Buffer);
time1 += mFdp.PickValueInArray(kVsyncPeriods);
time2 += mFdp.PickValueInArray(kVsyncPeriods);
@@ -242,13 +244,15 @@
void SchedulerFuzzer::fuzzVSyncReactor() {
std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>();
- scheduler::VSyncReactor reactor(std::make_unique<ClockWrapper>(
+ scheduler::VSyncReactor reactor(DEFAULT_DISPLAY_ID,
+ std::make_unique<ClockWrapper>(
std::make_shared<FuzzImplClock>()),
*vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
false);
- reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>());
- bool periodFlushed = mFdp.ConsumeBool();
+ reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeBool());
+ bool periodFlushed = false; // Value does not matter, since this is an out
+ // param from addHwVsyncTimestamp.
reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt,
&periodFlushed);
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index 4c6a9cf..2c4eb10 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -40,6 +40,7 @@
/* offset between real-time clock and elapsed time clock in nanoseconds.
Calculated as: systemTime(SYSTEM_TIME_REALTIME) - systemTime(SYSTEM_TIME_MONOTONIC) */
fixed64 real_to_elapsed_time_offset_nanos = 3;
+ uint32 version = 4;
}
message TransactionTraceEntry {
@@ -47,18 +48,47 @@
int64 vsync_id = 2;
repeated TransactionState transactions = 3;
repeated LayerCreationArgs added_layers = 4;
- repeated int32 removed_layers = 5;
+ repeated uint32 destroyed_layers = 5;
repeated DisplayState added_displays = 6;
repeated int32 removed_displays = 7;
- repeated int32 removed_layer_handles = 8;
+ repeated uint32 destroyed_layer_handles = 8;
+ bool displays_changed = 9;
+ repeated DisplayInfo displays = 10;
+}
+
+message DisplayInfo {
+ uint32 layer_stack = 1;
+ int32 display_id = 2;
+ int32 logical_width = 3;
+ int32 logical_height = 4;
+ Transform transform_inverse = 5;
+ Transform transform = 6;
+ bool receives_input = 7;
+ bool is_secure = 8;
+ bool is_primary = 9;
+ bool is_virtual = 10;
+ int32 rotation_flags = 11;
+ int32 transform_hint = 12;
+
}
message LayerCreationArgs {
- int32 layer_id = 1;
+ uint32 layer_id = 1;
string name = 2;
uint32 flags = 3;
- int32 parent_id = 4;
- int32 mirror_from_id = 5;
+ uint32 parent_id = 4;
+ uint32 mirror_from_id = 5;
+ bool add_to_root = 6;
+ uint32 layer_stack_to_mirror = 7;
+}
+
+message Transform {
+ float dsdx = 1;
+ float dtdx = 2;
+ float dtdy = 3;
+ float dsdy = 4;
+ float tx = 5;
+ float ty = 6;
}
message TransactionState {
@@ -74,7 +104,7 @@
// Keep insync with layer_state_t
message LayerState {
- int64 layer_id = 1;
+ uint32 layer_id = 1;
// Changes are split into ChangesLsb and ChangesMsb. First 32 bits are in ChangesLsb
// and the next 32 bits are in ChangesMsb. This is needed because enums have to be
// 32 bits and there's no nice way to put 64bit constants into .proto files.
@@ -164,8 +194,8 @@
Matrix22 matrix = 11;
float corner_radius = 12;
uint32 background_blur_radius = 13;
- int64 parent_id = 14;
- int64 relative_parent_id = 15;
+ uint32 parent_id = 14;
+ uint32 relative_parent_id = 15;
float alpha = 16;
message Color3 {
@@ -220,14 +250,6 @@
ColorTransformProto color_transform = 25;
repeated BlurRegion blur_regions = 26;
- message Transform {
- float dsdx = 1;
- float dtdx = 2;
- float dtdy = 3;
- float dsdy = 4;
- float tx = 5;
- float ty = 6;
- }
message WindowInfo {
uint32 layout_params_flags = 1;
int32 layout_params_type = 2;
@@ -236,7 +258,7 @@
bool focusable = 5;
bool has_wallpaper = 6;
float global_scale_factor = 7;
- int64 crop_layer_id = 8;
+ uint32 crop_layer_id = 8;
bool replace_touchable_region_with_crop = 9;
RectProto touchable_region_crop = 10;
Transform transform = 11;
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index bf7cae9..0b8c51e 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -544,6 +544,7 @@
.apply();
{
+ SCOPED_TRACE("final color");
auto shot = screenshot();
shot->expectColor(Rect(0, 0, width, height), finalColor);
shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index e69db7c..0ea0824 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -18,6 +18,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <android-base/properties.h>
#include <private/android_filesystem_config.h>
#include "LayerTransactionTest.h"
#include "utils/TransactionUtils.h"
@@ -119,15 +120,20 @@
shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
}
- // Remove child layer
+ if (base::GetBoolProperty("debug.sf.enable_legacy_frontend", true)) {
+ GTEST_SKIP() << "Skipping test because mirroring behavior changes with legacy frontend";
+ }
+
+ // Remove child layer and verify we can still mirror the layer when
+ // its offscreen.
Transaction().reparent(mChildLayer, nullptr).apply();
{
SCOPED_TRACE("Removed Child Layer");
auto shot = screenshot();
// Grandchild mirror
- shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+ shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK);
// Child mirror
- shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+ shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
}
// Add grandchild layer to offscreen layer
@@ -136,9 +142,9 @@
SCOPED_TRACE("Added Grandchild Layer");
auto shot = screenshot();
// Grandchild mirror
- shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+ shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
// Child mirror
- shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+ shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
}
// Add child layer
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index e9b1fbb..d0ab105 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -187,8 +187,6 @@
// Expect no filtering because the output source crop and output buffer are the same size.
TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
- // Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
-
gui::DisplayCaptureArgs captureArgs;
captureArgs.displayToken = mDisplay;
captureArgs.width = 50;
@@ -224,4 +222,17 @@
mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
}
+// Expect no filtering because parent's position transform shouldn't scale the layer.
+TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) {
+ Transaction().setPosition(mParent, 100, 100).apply();
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mParent->getHandle();
+ captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+ mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index 53c3c39..d71486f 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -18,61 +18,61 @@
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
#include <future>
-#include "utils/TransactionUtils.h"
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
using gui::DisplayInfo;
using gui::WindowInfo;
+using WindowInfosPredicate = std::function<bool(const std::vector<WindowInfo>&)>;
+
class WindowInfosListenerTest : public ::testing::Test {
protected:
void SetUp() override {
seteuid(AID_SYSTEM);
mClient = sp<SurfaceComposerClient>::make();
- mWindowInfosListener = sp<SyncWindowInfosListener>::make();
- mClient->addWindowInfosListener(mWindowInfosListener);
}
- void TearDown() override {
- mClient->removeWindowInfosListener(mWindowInfosListener);
- seteuid(AID_ROOT);
- }
+ void TearDown() override { seteuid(AID_ROOT); }
- struct SyncWindowInfosListener : public gui::WindowInfosListener {
+ struct WindowInfosListener : public gui::WindowInfosListener {
public:
+ WindowInfosListener(WindowInfosPredicate predicate, std::promise<void>& promise)
+ : mPredicate(std::move(predicate)), mPromise(promise) {}
+
void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
const std::vector<DisplayInfo>&) override {
- windowInfosPromise.set_value(windowInfos);
- }
-
- std::vector<WindowInfo> waitForWindowInfos() {
- std::future<std::vector<WindowInfo>> windowInfosFuture =
- windowInfosPromise.get_future();
- std::vector<WindowInfo> windowInfos = windowInfosFuture.get();
- windowInfosPromise = std::promise<std::vector<WindowInfo>>();
- return windowInfos;
+ if (mPredicate(windowInfos)) {
+ mPromise.set_value();
+ }
}
private:
- std::promise<std::vector<WindowInfo>> windowInfosPromise;
+ WindowInfosPredicate mPredicate;
+ std::promise<void>& mPromise;
};
sp<SurfaceComposerClient> mClient;
- sp<SyncWindowInfosListener> mWindowInfosListener;
+
+ bool waitForWindowInfosPredicate(WindowInfosPredicate predicate) {
+ std::promise<void> promise;
+ auto listener = sp<WindowInfosListener>::make(std::move(predicate), promise);
+ mClient->addWindowInfosListener(listener);
+ auto future = promise.get_future();
+ bool satisfied = future.wait_for(std::chrono::seconds{1}) == std::future_status::ready;
+ mClient->removeWindowInfosListener(listener);
+ return satisfied;
+ }
};
std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo,
std::vector<WindowInfo> windowInfos) {
- std::optional<WindowInfo> foundWindowInfo = std::nullopt;
for (WindowInfo windowInfo : windowInfos) {
if (windowInfo.token == targetWindowInfo.token) {
- foundWindowInfo = std::make_optional<>(windowInfo);
- break;
+ return windowInfo;
}
}
-
- return foundWindowInfo;
+ return std::nullopt;
}
TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
@@ -92,15 +92,17 @@
.setInputWindowInfo(surfaceControl, windowInfo)
.apply();
- std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
- std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
- ASSERT_NE(std::nullopt, foundWindowInfo);
+ auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) {
+ return findMatchingWindowInfo(windowInfo, windowInfos).has_value();
+ };
+ ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent));
Transaction().reparent(surfaceControl, nullptr).apply();
- windowInfos = mWindowInfosListener->waitForWindowInfos();
- foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
- ASSERT_EQ(std::nullopt, foundWindowInfo);
+ auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) {
+ return !findMatchingWindowInfo(windowInfo, windowInfos).has_value();
+ };
+ ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent));
}
TEST_F(WindowInfosListenerTest, WindowInfoChanged) {
@@ -121,19 +123,28 @@
.setInputWindowInfo(surfaceControl, windowInfo)
.apply();
- std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
- std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
- ASSERT_NE(std::nullopt, foundWindowInfo);
- ASSERT_TRUE(foundWindowInfo->touchableRegion.isEmpty());
+ auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) {
+ auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+ if (!foundWindowInfo) {
+ return false;
+ }
+ return foundWindowInfo->touchableRegion.isEmpty();
+ };
+ ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty));
Rect touchableRegions(0, 0, 50, 50);
windowInfo.addTouchableRegion(Rect(0, 0, 50, 50));
Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
- windowInfos = mWindowInfosListener->waitForWindowInfos();
- foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
- ASSERT_NE(std::nullopt, foundWindowInfo);
- ASSERT_TRUE(foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion));
+ auto windowIsPresentAndTouchableRegionMatches =
+ [&](const std::vector<WindowInfo>& windowInfos) {
+ auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+ if (!foundWindowInfo) {
+ return false;
+ }
+ return foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion);
+ };
+ ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches));
}
} // namespace android
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 0e214af..5f9214c 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -91,6 +91,8 @@
uint64_t curr_frame;
float x;
float y;
+ uint32_t bufferWidth;
+ uint32_t bufferHeight;
};
bool operator==(const LayerInfo& lh, const LayerInfo& rh) {
@@ -105,7 +107,8 @@
inline void PrintTo(const LayerInfo& info, ::std::ostream* os) {
*os << "Layer [" << info.id << "] name=" << info.name << " parent=" << info.parent
<< " z=" << info.z << " curr_frame=" << info.curr_frame << " x=" << info.x
- << " y=" << info.y;
+ << " y=" << info.y << " bufferWidth=" << info.bufferWidth
+ << " bufferHeight=" << info.bufferHeight;
}
struct find_id : std::unary_function<LayerInfo, bool> {
@@ -114,6 +117,18 @@
bool operator()(LayerInfo const& m) const { return m.id == id; }
};
+static LayerInfo getLayerInfoFromProto(::android::surfaceflinger::LayerProto& proto) {
+ return {proto.id(),
+ proto.name(),
+ proto.parent(),
+ proto.z(),
+ proto.curr_frame(),
+ proto.has_position() ? proto.position().x() : -1,
+ proto.has_position() ? proto.position().y() : -1,
+ proto.has_active_buffer() ? proto.active_buffer().width() : 0,
+ proto.has_active_buffer() ? proto.active_buffer().height() : 0};
+}
+
TEST_P(TransactionTraceTestSuite, validateEndState) {
ASSERT_GT(mActualLayersTraceProto.entry_size(), 0);
ASSERT_GT(mExpectedLayersTraceProto.entry_size(), 0);
@@ -128,10 +143,7 @@
expectedLayers.reserve(static_cast<size_t>(expectedLastEntry.layers().layers_size()));
for (int i = 0; i < expectedLastEntry.layers().layers_size(); i++) {
auto layer = expectedLastEntry.layers().layers(i);
- expectedLayers.push_back({layer.id(), layer.name(), layer.parent(), layer.z(),
- layer.curr_frame(),
- layer.has_position() ? layer.position().x() : -1,
- layer.has_position() ? layer.position().y() : -1});
+ expectedLayers.push_back(getLayerInfoFromProto(layer));
}
std::sort(expectedLayers.begin(), expectedLayers.end(), compareById);
@@ -139,10 +151,7 @@
actualLayers.reserve(static_cast<size_t>(actualLastEntry.layers().layers_size()));
for (int i = 0; i < actualLastEntry.layers().layers_size(); i++) {
auto layer = actualLastEntry.layers().layers(i);
- actualLayers.push_back({layer.id(), layer.name(), layer.parent(), layer.z(),
- layer.curr_frame(),
- layer.has_position() ? layer.position().x() : -1,
- layer.has_position() ? layer.position().y() : -1});
+ actualLayers.push_back(getLayerInfoFromProto(layer));
}
std::sort(actualLayers.begin(), actualLayers.end(), compareById);
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
index 9e4005c..296d2fd 100644
--- a/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope
index 16a91ee..ae54415 100644
--- a/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
index 8356ae7..8d03df4 100644
--- a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope
index cd62ab8..022861c 100644
--- a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 87c3c65..df3ffd2 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -103,16 +103,18 @@
"SurfaceFlinger_DestroyDisplayTest.cpp",
"SurfaceFlinger_DisplayModeSwitching.cpp",
"SurfaceFlinger_DisplayTransactionCommitTest.cpp",
+ "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+ "SurfaceFlinger_HdrOutputControlTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
+ "SurfaceFlinger_InitializeDisplaysTest.cpp",
+ "SurfaceFlinger_MultiDisplayPacesetterTest.cpp",
"SurfaceFlinger_NotifyPowerBoostTest.cpp",
- "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
"SurfaceFlinger_PowerHintTest.cpp",
"SurfaceFlinger_SetDisplayStateTest.cpp",
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
"SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
- "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SchedulerTest.cpp",
"SetFrameRateTest.cpp",
"RefreshRateSelectorTest.cpp",
@@ -133,6 +135,7 @@
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
"VsyncConfigurationTest.cpp",
+ "VsyncScheduleTest.cpp",
],
}
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 0416e93..19a93e1 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -99,7 +99,7 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- setupScheduler();
+ mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
@@ -122,36 +122,6 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
- void setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread),
- TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
- TestableSurfaceFlinger::kTwoDisplayModes);
- }
-
void setupForceGeometryDirty() {
// TODO: This requires the visible region and other related
// state to be set, and is problematic for BufferLayers since they are
@@ -176,7 +146,6 @@
bool mDisplayOff = false;
TestableSurfaceFlinger mFlinger;
sp<DisplayDevice> mDisplay;
- sp<DisplayDevice> mExternalDisplay;
sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
sp<compositionengine::mock::DisplaySurface>::make();
sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
@@ -315,13 +284,16 @@
constexpr auto kDisplayConnectionType = ui::DisplayConnectionType::Internal;
constexpr bool kIsPrimary = true;
- test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
- .setDisplaySurface(test->mDisplaySurface)
- .setNativeWindow(test->mNativeWindow)
- .setSecure(Derived::IS_SECURE)
- .setPowerMode(Derived::INIT_POWER_MODE)
- .inject();
+ test->mDisplay =
+ FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+ kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
+ .setDisplaySurface(test->mDisplaySurface)
+ .setNativeWindow(test->mNativeWindow)
+ .setSecure(Derived::IS_SECURE)
+ .setPowerMode(Derived::INIT_POWER_MODE)
+ .setRefreshRateSelector(test->mFlinger.scheduler()->refreshRateSelector())
+ .skipRegisterDisplay()
+ .inject();
Mock::VerifyAndClear(test->mNativeWindow.get());
constexpr bool kIsInternal = kDisplayConnectionType == ui::DisplayConnectionType::Internal;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index e0b508a..e32cf88 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -29,9 +29,7 @@
using android::hardware::graphics::composer::hal::HWDisplayId;
-using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-
-DisplayTransactionTest::DisplayTransactionTest() {
+DisplayTransactionTest::DisplayTransactionTest(bool withMockScheduler) {
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());
@@ -48,7 +46,10 @@
return nullptr;
});
- injectMockScheduler();
+ if (withMockScheduler) {
+ injectMockScheduler(PhysicalDisplayId::fromPort(0));
+ }
+
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
injectMockComposer(0);
@@ -61,7 +62,9 @@
mFlinger.resetScheduler(nullptr);
}
-void DisplayTransactionTest::injectMockScheduler() {
+void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) {
+ LOG_ALWAYS_FATAL_IF(mFlinger.scheduler());
+
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(mEventThread,
@@ -74,10 +77,11 @@
mock::EventThread::kCallingUid,
ResyncCallback())));
- mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
- std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
+ mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(),
+ std::make_shared<mock::VSyncTracker>(),
std::unique_ptr<EventThread>(mEventThread),
std::unique_ptr<EventThread>(mSFEventThread),
+ TestableSurfaceFlinger::DefaultDisplayMode{displayId},
TestableSurfaceFlinger::SchedulerCallbackImpl::kMock);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 223f4db..e64cb38 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -85,7 +85,7 @@
// --------------------------------------------------------------------
// Mock/Fake injection
- void injectMockScheduler();
+ void injectMockScheduler(PhysicalDisplayId);
void injectMockComposer(int virtualDisplayCount);
void injectFakeBufferQueueFactory();
void injectFakeNativeWindowSurfaceFactory();
@@ -128,8 +128,6 @@
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
Hwc2::mock::Composer* mComposer = nullptr;
- mock::VsyncController* mVsyncController = new mock::VsyncController;
- mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
mock::EventThread* mEventThread = new mock::EventThread;
mock::EventThread* mSFEventThread = new mock::EventThread;
@@ -139,7 +137,7 @@
surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
protected:
- DisplayTransactionTest();
+ DisplayTransactionTest(bool withMockScheduler = true);
};
constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667;
@@ -158,7 +156,6 @@
#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
BOOL_SUBSTITUTE(Async);
-BOOL_SUBSTITUTE(Critical);
BOOL_SUBSTITUTE(Primary);
BOOL_SUBSTITUTE(Secure);
BOOL_SUBSTITUTE(Virtual);
@@ -238,8 +235,8 @@
// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
// 3) GpuVirtualDisplayIdType for virtual display without HWC backing.
-template <typename DisplayIdType, int width, int height, Critical critical, Async async,
- Secure secure, Primary primary, int grallocUsage, int displayFlags>
+template <typename DisplayIdType, int width, int height, Async async, Secure secure,
+ Primary primary, int grallocUsage, int displayFlags>
struct DisplayVariant {
using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
@@ -255,9 +252,6 @@
static constexpr Virtual VIRTUAL =
IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
- // When creating native window surfaces for the framebuffer, whether those should be critical
- static constexpr Critical CRITICAL = critical;
-
// When creating native window surfaces for the framebuffer, whether those should be async
static constexpr Async ASYNC = async;
@@ -486,17 +480,16 @@
constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1;
-template <typename PhysicalDisplay, int width, int height, Critical critical>
+template <typename PhysicalDisplay, int width, int height>
struct PhysicalDisplayVariant
- : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
- Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
- GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
- HwcDisplayVariant<
- PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
- DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
- Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
- GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
- PhysicalDisplay> {};
+ : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE,
+ Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY,
+ PHYSICAL_DISPLAY_FLAGS>,
+ HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+ DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
+ Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
+ PhysicalDisplay> {};
template <bool hasIdentificationData>
struct PrimaryDisplay {
@@ -508,14 +501,16 @@
static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
};
-template <bool hasIdentificationData>
-struct ExternalDisplay {
- static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External;
+template <ui::DisplayConnectionType connectionType, bool hasIdentificationData>
+struct SecondaryDisplay {
+ static constexpr auto CONNECTION_TYPE = connectionType;
static constexpr Primary PRIMARY = Primary::FALSE;
static constexpr uint8_t PORT = 254;
static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
- static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+ static constexpr auto GET_IDENTIFICATION_DATA =
+ connectionType == ui::DisplayConnectionType::Internal ? getInternalEdid
+ : getExternalEdid;
};
struct TertiaryDisplay {
@@ -525,15 +520,18 @@
static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
};
-// A primary display is a physical display that is critical
-using PrimaryDisplayVariant =
- PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+using PrimaryDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160>;
-// An external display is physical display that is not critical.
+using InnerDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<true>, 1840, 2208>;
+using OuterDisplayVariant =
+ PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal, true>, 1080,
+ 2092>;
+
using ExternalDisplayVariant =
- PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+ PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External, false>, 1920,
+ 1280>;
-using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200>;
// A virtual display not supported by the HWC.
constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
@@ -542,12 +540,11 @@
template <int width, int height, Secure secure>
struct NonHwcVirtualDisplayVariant
- : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY,
- VIRTUAL_DISPLAY_FLAGS> {
- using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE,
- Async::TRUE, secure, Primary::FALSE,
- GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>;
+ : DisplayVariant<GpuVirtualDisplayIdType, width, height, Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS> {
+ using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY,
+ VIRTUAL_DISPLAY_FLAGS>;
static void injectHwcDisplay(DisplayTransactionTest*) {}
@@ -589,17 +586,14 @@
template <int width, int height, Secure secure>
struct HwcVirtualDisplayVariant
- : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY,
- VIRTUAL_DISPLAY_FLAGS>,
- HwcDisplayVariant<
- HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
- DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
- Async::TRUE, secure, Primary::FALSE,
- GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>> {
- using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
- Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER,
- VIRTUAL_DISPLAY_FLAGS>;
+ : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>,
+ HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+ DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY,
+ VIRTUAL_DISPLAY_FLAGS>> {
+ using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER, VIRTUAL_DISPLAY_FLAGS>;
using Self = HwcVirtualDisplayVariant<width, height, secure>;
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index f6bcadc..f1cdca3 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -125,7 +125,7 @@
ConnectionEventRecorder mConnectionEventCallRecorder{0};
ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
- std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
+ std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
sp<MockEventThreadConnection> mThrottledConnection;
@@ -140,12 +140,12 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mVsyncSchedule = std::unique_ptr<scheduler::VsyncSchedule>(
- new scheduler::VsyncSchedule(std::make_unique<mock::VSyncTracker>(),
- std::make_unique<mock::VSyncDispatch>(), nullptr));
-
- mock::VSyncDispatch& mockDispatch =
- *static_cast<mock::VSyncDispatch*>(&mVsyncSchedule->getDispatch());
+ auto mockDispatchPtr = std::make_shared<mock::VSyncDispatch>();
+ mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>(
+ new scheduler::VsyncSchedule(INTERNAL_DISPLAY_ID,
+ std::make_shared<mock::VSyncTracker>(), mockDispatchPtr,
+ nullptr));
+ mock::VSyncDispatch& mockDispatch = *mockDispatchPtr;
EXPECT_CALL(mockDispatch, registerCallback(_, _))
.WillRepeatedly(Invoke(mVSyncCallbackRegisterRecorder.getInvocable()));
EXPECT_CALL(mockDispatch, schedule(_, _))
@@ -189,10 +189,9 @@
};
mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
- mThread =
- std::make_unique<impl::EventThread>(/*std::move(source), */ "EventThreadTest",
- *mVsyncSchedule, mTokenManager.get(), throttleVsync,
- getVsyncPeriod, kWorkDuration, kReadyDuration);
+ mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule,
+ mTokenManager.get(), throttleVsync,
+ getVsyncPeriod, kWorkDuration, kReadyDuration);
// EventThread should register itself as VSyncSource callback.
EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index 1cd9e49..f695b09 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -29,9 +29,7 @@
#include "TestableSurfaceFlinger.h"
#include "fake/FakeClock.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
#include "mock/MockFrameTimeline.h"
-#include "mock/MockVsyncController.h"
namespace android {
@@ -47,7 +45,6 @@
using android::Hwc2::IComposer;
using android::Hwc2::IComposerClient;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
using gui::LayerMetadata;
struct TestableFpsListener : public gui::BnFpsListener {
@@ -77,7 +74,6 @@
static constexpr uint32_t LAYER_FLAGS = 0;
static constexpr int32_t PRIORITY_UNSET = -1;
- void setupScheduler();
sp<Layer> createBufferStateLayer(LayerMetadata metadata);
TestableSurfaceFlinger mFlinger;
@@ -102,7 +98,7 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- setupScheduler();
+ mFlinger.setupMockScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFpsListener = sp<TestableFpsListener>::make();
@@ -120,33 +116,6 @@
return sp<Layer>::make(args);
}
-void FpsReporterTest::setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread));
-}
-
namespace {
TEST_F(FpsReporterTest, callsListeners) {
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
index ac63a0e..1c9aee7 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
@@ -24,8 +24,6 @@
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
namespace android {
@@ -38,8 +36,6 @@
using android::Hwc2::IComposer;
using android::Hwc2::IComposerClient;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
/**
* This class covers all the test that are related to refresh rate selection.
*/
@@ -56,7 +52,6 @@
static constexpr uint32_t LAYER_FLAGS = 0;
static constexpr int32_t PRIORITY_UNSET = -1;
- void setupScheduler();
sp<Layer> createBufferStateLayer();
sp<Layer> createEffectLayer();
@@ -76,7 +71,7 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- setupScheduler();
+ mFlinger.setupMockScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
}
@@ -108,37 +103,8 @@
layer->commitTransaction(c);
}
-void RefreshRateSelectionTest::setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread));
-}
-
namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
mParent = createBufferStateLayer();
mChild = createBufferStateLayer();
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index abd7789..d26ef3c 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -489,7 +489,7 @@
auto displayFrame0 = getDisplayFrame(0);
EXPECT_EQ(displayFrame0->getActuals().presentTime, 59);
- EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown);
+ EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown | JankType::DisplayHAL);
EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
}
@@ -2259,6 +2259,7 @@
mFrameTimeline->setSfWakeUp(sfToken3, 72, Fps::fromPeriodNsecs(11));
mFrameTimeline->setSfPresent(80, validPresentFence);
+ erroneousPresentFence2->signalForTest(2);
validPresentFence->signalForTest(80);
addEmptyDisplayFrame();
@@ -2268,14 +2269,14 @@
EXPECT_EQ(displayFrame->getActuals().presentTime, 26);
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
- EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL);
}
{
auto displayFrame = getDisplayFrame(1);
EXPECT_EQ(displayFrame->getActuals().presentTime, 60);
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
- EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL);
}
{
auto displayFrame = getDisplayFrame(2);
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
index 29aa717..1b5c6e7 100644
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -25,15 +25,13 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
namespace android {
using testing::_;
using testing::Mock;
using testing::Return;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
using gui::GameMode;
using gui::LayerMetadata;
@@ -43,7 +41,7 @@
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());
- setupScheduler();
+ mFlinger.setupMockScheduler();
setupComposer();
}
@@ -59,33 +57,6 @@
return sp<Layer>::make(args);
}
- void setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread));
- }
-
void setupComposer() {
mComposer = new Hwc2::mock::Composer();
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 763426a..ddf3363 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -17,12 +17,9 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "FrontEnd/LayerHandle.h"
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
-#include "Layer.h"
#include "LayerHierarchyTest.h"
-#include "gui/SurfaceComposerClient.h"
#define UPDATE_AND_VERIFY(HIERARCHY) \
({ \
@@ -207,7 +204,8 @@
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
- reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ // This calls setLayer
+ removeRelativeZ(11);
UPDATE_AND_VERIFY(hierarchyBuilder);
std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
@@ -263,6 +261,37 @@
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
}
+TEST_F(LayerHierarchyTest, reparentRelativeLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ reparentLayer(11, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ setZ(11, 0);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
// mirror tests
TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
@@ -387,7 +416,7 @@
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
// remove relative parent so layer becomes onscreen again
- reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ removeRelativeZ(11);
UPDATE_AND_VERIFY(hierarchyBuilder);
expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
@@ -437,10 +466,11 @@
updateBackgroundColor(1, 0.5);
UPDATE_AND_VERIFY(hierarchyBuilder);
-
- std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2};
+ auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ std::vector<uint32_t> expectedTraversalPath = {1, bgLayerId, 11, 111, 12,
+ 121, 122, 1221, 13, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
- expectedTraversalPath = {1222, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ expectedTraversalPath = {bgLayerId, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
expectedTraversalPath = {};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 852cb91..5b3c7ef 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -17,11 +17,10 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "FrontEnd/LayerHandle.h"
+#include "Client.h" // temporarily needed for LayerCreationArgs
+#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
-#include "Layer.h"
-#include "gui/SurfaceComposerClient.h"
namespace android::surfaceflinger::frontend {
@@ -51,20 +50,21 @@
createLayer(1221, 122);
}
- LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent,
- wp<IBinder> mirror) {
- LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
+ uint32_t layerIdToMirror) {
+ LayerCreationArgs args(std::make_optional(id));
+ args.name = "testlayer";
args.addToRoot = canBeRoot;
- args.parentHandle = parent;
- args.mirrorLayerHandle = mirror;
+ args.parentId = parentId;
+ args.layerIdToMirror = layerIdToMirror;
return args;
}
- LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStack) {
- LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStackToMirror) {
+ LayerCreationArgs args(std::make_optional(id));
+ args.name = "testlayer";
args.addToRoot = true;
- args.parentHandle.clear();
- args.layerStackToMirror = layerStack;
+ args.layerStackToMirror = layerStackToMirror;
return args;
}
@@ -90,17 +90,14 @@
}
virtual void createRootLayer(uint32_t id) {
- sp<LayerHandle> handle = sp<LayerHandle>::make(id);
- mHandles[id] = handle;
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(std::make_unique<RequestedLayerState>(
- createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)));
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+ /*mirror=*/UNASSIGNED_LAYER_ID)));
mLifecycleManager.addLayers(std::move(layers));
}
void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
- sp<LayerHandle> handle = sp<LayerHandle>::make(id);
- mHandles[id] = handle;
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(std::make_unique<RequestedLayerState>(
createDisplayMirrorArgs(/*id=*/id, layerStack)));
@@ -108,62 +105,56 @@
}
virtual void createLayer(uint32_t id, uint32_t parentId) {
- sp<LayerHandle> handle = sp<LayerHandle>::make(id);
- mHandles[id] = handle;
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(std::make_unique<RequestedLayerState>(
- createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId],
- /*mirror=*/nullptr)));
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+ /*mirror=*/UNASSIGNED_LAYER_ID)));
mLifecycleManager.addLayers(std::move(layers));
}
- void reparentLayer(uint32_t id, uint32_t newParentId) {
+ std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
-
- if (newParentId == UNASSIGNED_LAYER_ID) {
- transactions.back().states.front().state.parentSurfaceControlForChild = nullptr;
- } else {
- auto parentHandle = mHandles[newParentId];
- transactions.back().states.front().state.parentSurfaceControlForChild =
- sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
- static_cast<int32_t>(newParentId), "Test");
- }
+ transactions.back().states.front().parentId = newParentId;
transactions.back().states.front().state.what = layer_state_t::eReparent;
- transactions.back().states.front().state.surface = mHandles[id];
- mLifecycleManager.applyTransactions(transactions);
+ transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID;
+ transactions.back().states.front().layerId = id;
+ return transactions;
+ }
+
+ void reparentLayer(uint32_t id, uint32_t newParentId) {
+ mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
+ }
+
+ std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().relativeParentId = relativeParentId;
+ transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+ transactions.back().states.front().layerId = id;
+ return transactions;
}
void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+ mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId));
+ }
+
+ void removeRelativeZ(uint32_t id) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
-
- if (relativeParentId == UNASSIGNED_LAYER_ID) {
- transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
- } else {
- auto parentHandle = mHandles[relativeParentId];
- transactions.back().states.front().state.relativeLayerSurfaceControl =
- sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
- static_cast<int32_t>(relativeParentId), "test");
- transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
- }
- transactions.back().states.front().state.surface = mHandles[id];
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ transactions.back().states.front().layerId = id;
mLifecycleManager.applyTransactions(transactions);
}
- virtual void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) {
- auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent];
- auto mirrorHandle =
- (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror];
-
- sp<LayerHandle> handle = sp<LayerHandle>::make(id);
- mHandles[id] = handle;
+ virtual void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) {
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(std::make_unique<RequestedLayerState>(
- createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle,
- /*mirror=*/mHandles[layerToMirror])));
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+ /*mirror=*/layerIdToMirror)));
mLifecycleManager.addLayers(std::move(layers));
}
@@ -172,8 +163,8 @@
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
- transactions.back().states.front().state.bgColorAlpha = alpha;
- transactions.back().states.front().state.surface = mHandles[id];
+ transactions.back().states.front().state.bgColor.a = alpha;
+ transactions.back().states.front().layerId = id;
mLifecycleManager.applyTransactions(transactions);
}
@@ -196,16 +187,19 @@
mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
}
- void setZ(uint32_t id, int32_t z) {
+ std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
- transactions.back().states.front().state.surface = mHandles[id];
- transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().layerId = id;
transactions.back().states.front().state.z = z;
- mLifecycleManager.applyTransactions(transactions);
+ return transactions;
+ }
+
+ void setZ(uint32_t id, int32_t z) {
+ mLifecycleManager.applyTransactions(setZTransaction(id, z));
}
void setCrop(uint32_t id, const Rect& crop) {
@@ -214,8 +208,7 @@
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eCropChanged;
- transactions.back().states.front().state.surface = mHandles[id];
- transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().layerId = id;
transactions.back().states.front().state.crop = crop;
mLifecycleManager.applyTransactions(transactions);
}
@@ -228,8 +221,7 @@
transactions.back().states.front().state.what = layer_state_t::eFlagsChanged;
transactions.back().states.front().state.flags = flags;
transactions.back().states.front().state.mask = mask;
- transactions.back().states.front().state.surface = mHandles[id];
- transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().layerId = id;
mLifecycleManager.applyTransactions(transactions);
}
@@ -239,8 +231,7 @@
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eAlphaChanged;
- transactions.back().states.front().state.surface = mHandles[id];
- transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().layerId = id;
transactions.back().states.front().state.color.a = static_cast<half>(alpha);
mLifecycleManager.applyTransactions(transactions);
}
@@ -257,8 +248,7 @@
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eColorChanged;
transactions.back().states.front().state.color.rgb = rgb;
- transactions.back().states.front().state.surface = mHandles[id];
- transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().layerId = id;
mLifecycleManager.applyTransactions(transactions);
}
@@ -268,14 +258,27 @@
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
- transactions.back().states.front().state.surface = mHandles[id];
- transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().layerId = id;
transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
mLifecycleManager.applyTransactions(transactions);
}
+ void setTouchableRegion(uint32_t id, Region region) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.windowInfoHandle =
+ sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ inputInfo->touchableRegion = region;
+ inputInfo->token = sp<BBinder>::make();
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
- std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 8397f8d..b767276 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -112,7 +112,8 @@
Fps desiredRefreshRate, int numFrames) {
LayerHistory::Summary summary;
for (int i = 0; i < numFrames; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += frameRate.getPeriodNsecs();
summary = summarizeLayerHistory(time);
@@ -155,7 +156,8 @@
EXPECT_TRUE(summarizeLayerHistory(time).empty());
EXPECT_EQ(0, activeLayerCount());
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -177,7 +179,8 @@
EXPECT_TRUE(summarizeLayerHistory(time).empty());
EXPECT_EQ(0, activeLayerCount());
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
auto summary = summarizeLayerHistory(time);
@@ -205,7 +208,8 @@
// Max returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -214,7 +218,8 @@
// Max is returned since we have enough history but there is no timestamp votes.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -232,7 +237,8 @@
nsecs_t time = systemTime();
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
auto summary = summarizeLayerHistory(time);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
// Layer is still considered inactive so we expect to get Min
@@ -240,7 +246,8 @@
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
summary = summarizeLayerHistory(time);
EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -257,7 +264,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -280,7 +288,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -307,7 +316,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -335,7 +345,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -363,7 +374,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -395,7 +407,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -441,7 +454,8 @@
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
summary = summarizeLayerHistory(time);
}
@@ -453,13 +467,15 @@
// layer2 is frequent and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
// layer1 is still active but infrequent.
- history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(2, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
@@ -472,7 +488,8 @@
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
@@ -488,10 +505,12 @@
constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
if (i % RATIO == 0) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
}
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
@@ -504,7 +523,8 @@
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
summary = summarizeLayerHistory(time);
ASSERT_EQ(2, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
@@ -530,7 +550,8 @@
// layer2 still has low refresh rate.
// layer3 becomes inactive.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
@@ -551,7 +572,8 @@
// layer3 becomes active and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
@@ -582,7 +604,8 @@
// the very first updates makes the layer frequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
@@ -593,7 +616,8 @@
}
// the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
@@ -607,7 +631,8 @@
// Now even if we post a quick few frame we should stay infrequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
EXPECT_EQ(1, layerCount());
@@ -618,7 +643,8 @@
}
// More quick frames will get us to frequent again
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
EXPECT_EQ(1, layerCount());
@@ -645,9 +671,10 @@
nsecs_t time = systemTime();
// Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(explicitInvisiblelayer.get(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
+ time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
+ time, time, LayerHistory::LayerUpdateType::Buffer);
EXPECT_EQ(2, layerCount());
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -673,7 +700,8 @@
// layer is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -684,7 +712,8 @@
EXPECT_EQ(0, animatingLayerCount(time));
// another update with the same cadence keep in infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -694,7 +723,8 @@
EXPECT_EQ(0, animatingLayerCount(time));
// an update as animation will immediately vote for Max
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::AnimationTX);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -719,7 +749,8 @@
// Fill up the window with frequent updates
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += (60_Hz).getPeriodNsecs();
EXPECT_EQ(1, layerCount());
@@ -731,7 +762,8 @@
// posting a buffer after long inactivity should retain the layer as active
time += std::chrono::nanoseconds(3s).count();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
@@ -741,9 +773,11 @@
// posting more infrequent buffer should make the layer infrequent
time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -751,7 +785,8 @@
EXPECT_EQ(0, animatingLayerCount(time));
// posting another buffer should keep the layer infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -759,8 +794,10 @@
EXPECT_EQ(0, animatingLayerCount(time));
// posting more buffers would mean starting of an animation, so making the layer frequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -769,7 +806,8 @@
// posting a buffer after long inactivity should retain the layer as active
time += std::chrono::nanoseconds(3s).count();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -778,7 +816,8 @@
// posting another buffer should keep the layer frequent
time += (60_Hz).getPeriodNsecs();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -801,7 +840,8 @@
// layer is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -869,10 +909,10 @@
const nsecs_t startTime = systemTime();
const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
- history().record(heuristicLayer.get(), startTime, startTime,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(infrequentLayer.get(), startTime, startTime,
- LayerHistory::LayerUpdateType::Buffer);
+ history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
+ startTime, LayerHistory::LayerUpdateType::Buffer);
+ history().record(infrequentLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
+ startTime, LayerHistory::LayerUpdateType::Buffer);
nsecs_t time = startTime;
nsecs_t lastInfrequentUpdate = startTime;
@@ -880,14 +920,15 @@
int infrequentLayerUpdates = 0;
while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
time += heuristicUpdateDelta.count();
- history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
totalInfrequentLayerUpdates);
lastInfrequentUpdate = time;
- history().record(infrequentLayer.get(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
+ history().record(infrequentLayer->getSequence(), infrequentLayer->getLayerProps(), time,
+ time, LayerHistory::LayerUpdateType::Buffer);
infrequentLayerUpdates++;
}
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 89440a6..14b8e4b 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -17,25 +17,14 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "FrontEnd/LayerHandle.h"
#include "FrontEnd/LayerLifecycleManager.h"
-#include "Layer.h"
-#include "gui/SurfaceComposerClient.h"
+#include "LayerHierarchyTest.h"
+#include "TransactionState.h"
using namespace android::surfaceflinger;
namespace android::surfaceflinger::frontend {
-namespace {
-LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
- LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
- args.addToRoot = canBeRoot;
- args.parentHandle = parent;
- args.mirrorLayerHandle = mirror;
- return args;
-}
-} // namespace
-
// To run test:
/**
mp :libsurfaceflinger_unittest && adb sync; adb shell \
@@ -66,69 +55,24 @@
std::unordered_set<uint32_t> mActualLayersDestroyed;
};
-class LayerLifecycleManagerTest : public testing::Test {
+class LayerLifecycleManagerTest : public LayerHierarchyTestBase {
protected:
std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
- return std::make_unique<RequestedLayerState>(
- createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr));
+ return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true,
+ /*parent=*/UNASSIGNED_LAYER_ID,
+ /*mirror=*/UNASSIGNED_LAYER_ID));
}
std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
- mHandles[parentId] = sp<LayerHandle>::make(parentId);
return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
- /*parent=*/mHandles[parentId],
- /*mirror=*/nullptr));
- }
-
- TransactionState reparentLayer(uint32_t id, uint32_t newParentId) {
- TransactionState transaction;
- transaction.states.push_back({});
-
- if (newParentId == UNASSIGNED_LAYER_ID) {
- transaction.states.front().state.parentSurfaceControlForChild = nullptr;
- } else {
- transaction.states.front().state.parentSurfaceControlForChild =
- sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
- sp<LayerHandle>::make(newParentId),
- static_cast<int32_t>(newParentId), "Test");
- }
- transaction.states.front().state.what = layer_state_t::eReparent;
- transaction.states.front().state.surface = sp<LayerHandle>::make(id);
- return transaction;
- }
-
- TransactionState setLayer(uint32_t id, int32_t z) {
- TransactionState transaction;
- transaction.states.push_back({});
- transaction.states.front().state.z = z;
- transaction.states.front().state.what = layer_state_t::eLayerChanged;
- transaction.states.front().state.surface = sp<LayerHandle>::make(id);
- return transaction;
- }
-
- TransactionState makeRelative(uint32_t id, uint32_t relativeParentId) {
- TransactionState transaction;
- transaction.states.push_back({});
-
- if (relativeParentId == UNASSIGNED_LAYER_ID) {
- transaction.states.front().state.relativeLayerSurfaceControl = nullptr;
- } else {
- transaction.states.front().state.relativeLayerSurfaceControl =
- sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
- sp<LayerHandle>::make(relativeParentId),
- static_cast<int32_t>(relativeParentId), "Test");
- }
- transaction.states.front().state.what = layer_state_t::eRelativeLayerChanged;
- transaction.states.front().state.surface = sp<LayerHandle>::make(id);
- return transaction;
+ parentId,
+ /*mirror=*/UNASSIGNED_LAYER_ID));
}
RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager,
uint32_t layerId) {
return lifecycleManager.getLayerFromId(layerId);
}
-
- std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
};
TEST_F(LayerLifecycleManagerTest, addLayers) {
@@ -153,16 +97,7 @@
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
-
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().state.z = 2;
- transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
- sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
- transactions.back().states.front().state.surface = handle;
- lifecycleManager.applyTransactions(transactions);
- transactions.clear();
+ lifecycleManager.applyTransactions(setZTransaction(1, 2));
auto& managedLayers = lifecycleManager.getLayers();
ASSERT_EQ(managedLayers.size(), 1u);
@@ -177,11 +112,12 @@
EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
// apply transactions that do not affect the hierarchy
+ std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.backgroundBlurRadius = 22;
transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
- transactions.back().states.front().state.surface = handle;
+ transactions.back().states.front().layerId = 1;
lifecycleManager.applyTransactions(transactions);
EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
@@ -232,7 +168,7 @@
listener->expectLayersAdded({1, 2, 3});
listener->expectLayersDestroyed({});
- lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
lifecycleManager.commitChanges();
listener->expectLayersAdded({});
listener->expectLayersDestroyed({});
@@ -257,7 +193,7 @@
listener->expectLayersAdded({1, 2, 3, 4});
listener->expectLayersDestroyed({});
- lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
lifecycleManager.onHandlesDestroyed({3});
lifecycleManager.commitChanges();
listener->expectLayersAdded({});
@@ -278,7 +214,7 @@
listener->expectLayersAdded({1, 2, 3, 4});
listener->expectLayersDestroyed({});
- lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
lifecycleManager.onHandlesDestroyed({3, 4});
lifecycleManager.commitChanges();
listener->expectLayersAdded({});
@@ -300,9 +236,9 @@
listener->expectLayersAdded({1, 2, 3, 4});
listener->expectLayersDestroyed({});
- lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
- lifecycleManager.applyTransactions({reparentLayer(4, 2)});
+ lifecycleManager.applyTransactions(reparentLayerTransaction(4, 2));
EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
lifecycleManager.commitChanges();
@@ -325,9 +261,9 @@
listener->expectLayersAdded({1, 2, 3, 4});
listener->expectLayersDestroyed({});
- lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
- lifecycleManager.applyTransactions({reparentLayer(4, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.applyTransactions(reparentLayerTransaction(4, UNASSIGNED_LAYER_ID));
EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
lifecycleManager.commitChanges();
@@ -350,9 +286,9 @@
listener->expectLayersAdded({1, 2, 3, 4});
listener->expectLayersDestroyed({});
- lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
- lifecycleManager.applyTransactions({setLayer(4, 1)});
+ lifecycleManager.applyTransactions(setZTransaction(4, 1));
EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
lifecycleManager.commitChanges();
@@ -372,10 +308,9 @@
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.bgColor.a = 0.5;
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
- sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
- transactions.back().states.front().state.surface = handle;
+ transactions.back().states.front().layerId = 1;
lifecycleManager.applyTransactions(transactions);
auto& managedLayers = lifecycleManager.getLayers();
@@ -383,9 +318,10 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- listener->expectLayersAdded({1, 2});
+ auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ listener->expectLayersAdded({1, bgLayerId});
listener->expectLayersDestroyed({});
- EXPECT_EQ(getRequestedLayerState(lifecycleManager, 2)->color.a, 0.5_hf);
+ EXPECT_EQ(getRequestedLayerState(lifecycleManager, bgLayerId)->color.a, 0.5_hf);
}
TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) {
@@ -400,15 +336,14 @@
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.bgColor.a = 0.5;
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
- sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
- transactions.back().states.front().state.surface = handle;
+ transactions.back().states.front().layerId = 1;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.bgColorAlpha = 0;
+ transactions.back().states.front().state.bgColor.a = 0;
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
- transactions.back().states.front().state.surface = handle;
+ transactions.back().states.front().layerId = 1;
lifecycleManager.applyTransactions(transactions);
@@ -417,8 +352,9 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- listener->expectLayersAdded({1, 2});
- listener->expectLayersDestroyed({2});
+ auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ listener->expectLayersAdded({1, bgLayerId});
+ listener->expectLayersDestroyed({bgLayerId});
}
TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) {
@@ -433,10 +369,9 @@
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.bgColor.a = 0.5;
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
- sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
- transactions.back().states.front().state.surface = handle;
+ transactions.back().states.front().layerId = 1;
transactions.emplace_back();
lifecycleManager.applyTransactions(transactions);
lifecycleManager.onHandlesDestroyed({1});
@@ -446,8 +381,9 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- listener->expectLayersAdded({1, 2});
- listener->expectLayersDestroyed({1, 2});
+ auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ listener->expectLayersAdded({1, bgLayerId});
+ listener->expectLayersDestroyed({1, bgLayerId});
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index aa6a14e..b8c4781 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -17,11 +17,9 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "FrontEnd/LayerHandle.h"
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
#include "FrontEnd/LayerSnapshotBuilder.h"
-#include "Layer.h"
#include "LayerHierarchyTest.h"
#define UPDATE_AND_VERIFY(BUILDER, ...) \
@@ -75,14 +73,14 @@
mHierarchyBuilder.update(mLifecycleManager.getLayers(),
mLifecycleManager.getDestroyedLayers());
}
- LayerSnapshotBuilder::Args args{
- .root = mHierarchyBuilder.getHierarchy(),
- .layerLifecycleManager = mLifecycleManager,
- .includeMetadata = false,
- .displays = mFrontEndDisplayInfos,
- .displayChanges = hasDisplayChanges,
- .globalShadowSettings = globalShadowSettings,
- };
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = hasDisplayChanges,
+ .globalShadowSettings = globalShadowSettings,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
actualBuilder.update(args);
// rebuild layer snapshots from scratch and verify that it matches the updated state.
@@ -100,6 +98,9 @@
}
LayerSnapshot* getSnapshot(uint32_t layerId) { return mSnapshotBuilder.getSnapshot(layerId); }
+ LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath path) {
+ return mSnapshotBuilder.getSnapshot(path);
+ }
LayerHierarchyBuilder mHierarchyBuilder{{}};
LayerSnapshotBuilder mSnapshotBuilder;
@@ -111,23 +112,25 @@
122, 1221, 13, 2};
TEST_F(LayerSnapshotTest, buildSnapshot) {
- LayerSnapshotBuilder::Args args{
- .root = mHierarchyBuilder.getHierarchy(),
- .layerLifecycleManager = mLifecycleManager,
- .includeMetadata = false,
- .displays = mFrontEndDisplayInfos,
- .globalShadowSettings = globalShadowSettings,
- };
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
LayerSnapshotBuilder builder(args);
}
TEST_F(LayerSnapshotTest, updateSnapshot) {
- LayerSnapshotBuilder::Args args{
- .root = mHierarchyBuilder.getHierarchy(),
- .layerLifecycleManager = mLifecycleManager,
- .includeMetadata = false,
- .displays = mFrontEndDisplayInfos,
- .globalShadowSettings = globalShadowSettings,
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}
+
};
LayerSnapshotBuilder builder;
@@ -263,7 +266,7 @@
transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
transactions.back().states.front().state.metadata = LayerMetadata();
transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, 42);
- transactions.back().states.front().state.surface = mHandles[1];
+ transactions.back().states.front().layerId = 1;
transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
mLifecycleManager.applyTransactions(transactions);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -292,8 +295,7 @@
ANATIVEWINDOW_FRAME_RATE_EXACT;
transactions.back().states.front().state.changeFrameRateStrategy =
ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS;
- transactions.back().states.front().state.surface = mHandles[11];
- transactions.back().states.front().state.layerId = static_cast<int32_t>(11);
+ transactions.back().states.front().layerId = 11;
mLifecycleManager.applyTransactions(transactions);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -320,7 +322,7 @@
// └── 2
// ROOT (DISPLAY 1)
// └── 3 (mirrors display 0)
-TEST_F(LayerSnapshotTest, displayMirrorRespects) {
+TEST_F(LayerSnapshotTest, displayMirrorRespectsLayerSkipScreenshotFlag) {
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
@@ -329,4 +331,96 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
}
+// ROOT (DISPLAY 0)
+// ├── 1
+// │ ├── 11
+// │ │ └── 111
+// │ └── 13
+// └── 2
+// ROOT (DISPLAY 3)
+// └── 3 (mirrors display 0)
+TEST_F(LayerSnapshotTest, mirrorLayerGetsCorrectLayerStack) {
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 3);
+ createDisplayMirrorLayer(4, ui::LayerStack::fromValue(0));
+ setLayerStack(4, 4);
+
+ std::vector<uint32_t> expected = {4, 1, 11, 111, 13, 2, 3, 1, 11,
+ 111, 13, 2, 1, 11, 111, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 3})->outputFilter.layerStack.id, 3u);
+ EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 4})->outputFilter.layerStack.id, 4u);
+}
+
+// ROOT (DISPLAY 0)
+// ├── 1 (crop 50x50)
+// │ ├── 11
+// │ │ └── 111
+// │ └── 13
+// └── 2
+// ROOT (DISPLAY 3)
+// └── 3 (mirrors display 0) (crop 100x100)
+TEST_F(LayerSnapshotTest, mirrorLayerTouchIsCroppedByMirrorRoot) {
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 3);
+ setCrop(1, Rect{50, 50});
+ setCrop(3, Rect{100, 100});
+ setCrop(111, Rect{200, 200});
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegion(111, touch);
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ EXPECT_TRUE(getSnapshot({.id = 111})->inputInfo.touchableRegion.hasSameRects(touch));
+ Region touchCroppedByMirrorRoot{Rect{0, 0, 50, 50}};
+ EXPECT_TRUE(getSnapshot({.id = 111, .mirrorRootId = 3})
+ ->inputInfo.touchableRegion.hasSameRects(touchCroppedByMirrorRoot));
+}
+
+TEST_F(LayerSnapshotTest, canRemoveDisplayMirror) {
+ setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 1);
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ destroyLayerHandle(3);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+}
+
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterMirroring) {
+ size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 1);
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2,
+ 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ destroyLayerHandle(3);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(startingNumSnapshots, mSnapshotBuilder.getSnapshots().size());
+}
+
+// Rel z doesn't create duplicate snapshots but this is for completeness
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterRelZ) {
+ size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+ reparentRelativeLayer(13, 11);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 13, 111, 12, 121, 122, 1221, 2});
+ setZ(13, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(startingNumSnapshots, mSnapshotBuilder.getSnapshots().size());
+}
+
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterLayerDestruction) {
+ size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+ destroyLayerHandle(2);
+ destroyLayerHandle(122);
+
+ std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+ EXPECT_LE(startingNumSnapshots - 2, mSnapshotBuilder.getSnapshots().size());
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
index ee42e19..803e807 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -16,15 +16,8 @@
#include "LayerTestUtils.h"
-#include "mock/MockEventThread.h"
-
namespace android {
-using testing::_;
-using testing::Return;
-
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
sp<Layer> BufferStateLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
sp<Client> client;
LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
@@ -44,36 +37,7 @@
}
BaseLayerTest::BaseLayerTest() {
- setupScheduler();
-}
-
-void BaseLayerTest::setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread),
- TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
- TestableSurfaceFlinger::kTwoDisplayModes);
+ mFlinger.setupMockScheduler();
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
index ab446fa..0773d90 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.h
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -63,8 +63,6 @@
protected:
BaseLayerTest();
- void setupScheduler();
-
TestableSurfaceFlinger mFlinger;
};
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 7aa5201..8f1b450 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -67,12 +67,12 @@
struct MessageQueueTest : testing::Test {
void SetUp() override {
- EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, kDuration));
- EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
}
- mock::VSyncDispatch mVSyncDispatch;
+ std::shared_ptr<mock::VSyncDispatch> mVSyncDispatch = std::make_shared<mock::VSyncDispatch>();
MockTokenManager mTokenManager;
TestableMessageQueue mEventQueue;
@@ -90,7 +90,7 @@
.earliestVsync = 0};
EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
@@ -103,13 +103,13 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
@@ -122,7 +122,7 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
@@ -149,7 +149,7 @@
.readyDuration = 0,
.earliestVsync = kPresentTime.ns()};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
}
@@ -161,7 +161,7 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
}
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index aafc323..d08e12c 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -224,7 +224,8 @@
EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
-TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_noCallbacksAfterStopAndResetTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index f4d052d..63ed87b 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -1455,6 +1455,24 @@
lr.name = "ExplicitExactOrMultiple 29.97 Hz";
EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
}
+
+ // Test that 29.97 will choose 30 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());
+ }
+
+ // Test that 59.94 will choose 60 if 59.94 is not supported
+ {
+ auto selector = createSelector(makeModes(kMode60, kMode30Frac, kMode30), kModeId60);
+
+ lr.desiredRefreshRate = 59.94_Hz;
+ lr.name = "ExplicitExactOrMultiple 59.94 Hz";
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+ }
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) {
@@ -2981,6 +2999,12 @@
layers[0].name = "Test layer";
layers[0].vote = LayerVoteType::Min;
EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+
+ constexpr FpsRanges kCappedAt60 = {{30_Hz, 90_Hz}, {30_Hz, 60_Hz}};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {DisplayModeId(kModeId60), kCappedAt60, kCappedAt60}));
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
}
TEST_P(RefreshRateSelectorTest, frameRateIsCappedByPolicy) {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 4b15385..dc76b4c 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <ftl/fake_guard.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -122,12 +123,6 @@
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
mScheduler->onHotplugReceived(handle, kDisplayId1, false);
- EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- mScheduler->onScreenAcquired(handle);
-
- EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- mScheduler->onScreenReleased(handle);
-
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
mScheduler->dump(handle, output);
@@ -147,12 +142,6 @@
EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1);
mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false);
- EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- mScheduler->onScreenAcquired(mConnectionHandle);
-
- EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- mScheduler->onScreenReleased(mConnectionHandle);
-
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
mScheduler->dump(mConnectionHandle, output);
@@ -172,11 +161,12 @@
// recordLayerHistory should be a noop
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
- mScheduler->setDisplayPowerMode(kPowerModeOn);
+ FTL_FAKE_GUARD(kMainThreadContext, mScheduler->setDisplayPowerMode(kDisplayId1, kPowerModeOn));
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
@@ -196,7 +186,8 @@
kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
@@ -245,10 +236,11 @@
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
- mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ LayerHistory::LayerUpdateType::Buffer);
constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
- mScheduler->setDisplayPowerMode(kPowerModeOn);
+ FTL_FAKE_GUARD(kMainThreadContext, mScheduler->setDisplayPowerMode(kDisplayId1, kPowerModeOn));
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 6adcd52..44ab569 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -380,8 +380,10 @@
commitTransaction();
auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
- history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
- history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+ history.record(parent->getSequence(), parent->getLayerProps(), 0, 0,
+ LayerHistory::LayerUpdateType::Buffer);
+ history.record(child->getSequence(), child->getLayerProps(), 0, 0,
+ LayerHistory::LayerUpdateType::Buffer);
const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
const auto summary = history.summarize(*selectorPtr, 0);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 43af595..e176546 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -50,7 +50,7 @@
mFlinger.configureAndCommit();
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
- .setDisplayModes(kModes, kModeId60, std::move(selectorPtr))
+ .setRefreshRateSelector(std::move(selectorPtr))
.inject();
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
@@ -100,7 +100,7 @@
ResyncCallback())));
auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+ auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
EXPECT_CALL(*vsyncTracker, currentPeriod())
@@ -109,8 +109,8 @@
EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
std::move(eventThread), std::move(sfEventThread),
- TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
- std::move(selectorPtr));
+ std::move(selectorPtr),
+ TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp);
}
TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
@@ -119,7 +119,7 @@
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
- mFlinger.onActiveDisplayChanged(*mDisplay);
+ mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
@@ -159,7 +159,7 @@
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- mFlinger.onActiveDisplayChanged(*mDisplay);
+ mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90.value(), true, 0,
@@ -195,7 +195,7 @@
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
- mFlinger.onActiveDisplayChanged(*mDisplay);
+ mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
@@ -238,7 +238,7 @@
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
- mFlinger.onActiveDisplayChanged(*mDisplay);
+ mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0,
@@ -284,9 +284,43 @@
ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
}
-TEST_F(DisplayModeSwitchingTest, multiDisplay) {
+MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
+ if (!arg->getDesiredActiveMode()) {
+ *result_listener << "No desired active mode";
+ return false;
+ }
+
+ if (arg->getDesiredActiveMode()->modeOpt->modePtr->getId() != modeId) {
+ *result_listener << "Unexpected desired active mode " << modeId;
+ return false;
+ }
+
+ if (!flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) {
+ *result_listener << "VsyncModulator did not shift to early phase";
+ return false;
+ }
+
+ return true;
+}
+
+MATCHER_P(ModeSettledTo, modeId, "") {
+ if (const auto desiredOpt = arg->getDesiredActiveMode()) {
+ *result_listener << "Unsettled desired active mode "
+ << desiredOpt->modeOpt->modePtr->getId();
+ return false;
+ }
+
ftl::FakeGuard guard(kMainThreadContext);
+ if (arg->getActiveMode().modePtr->getId() != modeId) {
+ *result_listener << "Settled to unexpected active mode " << modeId;
+ return false;
+ }
+
+ return true;
+}
+
+TEST_F(DisplayModeSwitchingTest, multiDisplay) {
constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
@@ -309,13 +343,13 @@
const auto& innerDisplay = mDisplay;
- EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
- EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
- EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId60);
- EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+ mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
- mFlinger.onActiveDisplayChanged(*innerDisplay);
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -327,12 +361,8 @@
mock::createDisplayModeSpecs(kModeId60.value(),
false, 0.f, 120.f)));
- // Transition on the inner display.
- ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
- EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
-
- // No transition on the outer display.
- EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_CALL(*mComposer,
@@ -342,31 +372,18 @@
mFlinger.commit();
- // Transition on the inner display.
- ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
- EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
-
- // No transition on the outer display.
- EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
mFlinger.commit();
- // Transition on the inner display.
- EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
- EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
- // No transition on the outer display.
- EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
- EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+ mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
- mFlinger.onActiveDisplayChanged(*outerDisplay);
-
- // No transition on the inner display.
- EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-
- // Transition on the outer display.
- ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
- EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60);
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
EXPECT_CALL(*mComposer,
setActiveConfigWithConstraints(kOuterDisplayHwcId,
@@ -375,22 +392,13 @@
mFlinger.commit();
- // No transition on the inner display.
- EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-
- // Transition on the outer display.
- ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
- EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60);
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
mFlinger.commit();
- // No transition on the inner display.
- EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
- EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
-
- // Transition on the outer display.
- EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
- EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
new file mode 100644
index 0000000..a2c54ac
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gtest/gtest.h>
+#include <gui/AidlStatusUtil.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
+using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
+using gui::aidl_utils::statusTFromBinderStatus;
+
+TEST(HdrOutputControlTest, testGetHdrOutputConversionSupport) {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+ bool hdrOutputConversionSupport;
+ binder::Status status = sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+}
+
+TEST(HdrOutputControlTest, testGetHdrConversionCapabilities) {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+ bool hdrOutputConversionSupport;
+ binder::Status getSupportStatus =
+ sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
+
+ std::vector<gui::HdrConversionCapability> capabilities;
+ binder::Status status = sf->getHdrConversionCapabilities(&capabilities);
+
+ if (hdrOutputConversionSupport) {
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ } else {
+ ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status));
+ }
+}
+
+TEST(HdrOutputControlTest, testSetHdrConversionStrategy) {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+ bool hdrOutputConversionSupport;
+ binder::Status getSupportStatus =
+ sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
+
+ std::vector<HdrConversionStrategy> strategies =
+ {HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+ GuiHdrConversionStrategyTag::passthrough)>),
+ HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+ GuiHdrConversionStrategyTag::autoAllowedHdrTypes)>),
+ HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+ GuiHdrConversionStrategyTag::forceHdrConversion)>)};
+ int32_t outPreferredHdrOutputType = 0;
+
+ for (HdrConversionStrategy strategy : strategies) {
+ binder::Status status = sf->setHdrConversionStrategy(&strategy, &outPreferredHdrOutputType);
+
+ if (hdrOutputConversionSupport) {
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ } else {
+ ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status));
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
similarity index 86%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index f553a23..fc5f2b0 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -22,9 +22,9 @@
namespace android {
namespace {
-class OnInitializeDisplaysTest : public DisplayTransactionTest {};
+class InitializeDisplaysTest : public DisplayTransactionTest {};
-TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
+TEST_F(InitializeDisplaysTest, commitsPrimaryDisplay) {
using Case = SimplePrimaryDisplayCase;
// --------------------------------------------------------------------
@@ -44,12 +44,15 @@
// We expect a scheduled commit for the display transaction.
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+ mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+ nextAnticipatedVSyncTimeFrom(_))
+ .WillRepeatedly(Return(0));
// --------------------------------------------------------------------
// Invocation
- mFlinger.onInitializeDisplays();
+ FTL_FAKE_GUARD(kMainThreadContext, mFlinger.initializeDisplays());
// --------------------------------------------------------------------
// Postconditions
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
new file mode 100644
index 0000000..e38f56e
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+struct MultiDisplayPacesetterTest : DisplayTransactionTest {
+ static constexpr bool kWithMockScheduler = false;
+ MultiDisplayPacesetterTest() : DisplayTransactionTest(kWithMockScheduler) {}
+};
+
+TEST_F(MultiDisplayPacesetterTest, foldable) {
+ injectMockScheduler(InnerDisplayVariant::DISPLAY_ID::get());
+
+ // Inject inner and outer displays with uninitialized power modes.
+ sp<DisplayDevice> innerDisplay, outerDisplay;
+ constexpr bool kInitPowerMode = false;
+ {
+ InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
+ innerDisplay = injector.inject();
+ }
+ {
+ OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ outerDisplay = injector.inject();
+ }
+
+ // When the device boots, the inner display should be the pacesetter.
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
+
+ // ...and should still be after powering on.
+ mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
+
+ // The outer display should become the pacesetter after folding.
+ mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
+ mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
+
+ // The inner display should become the pacesetter after unfolding.
+ mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
+ mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
+
+ // The inner display should stay the pacesetter if both are powered on.
+ // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
+ mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
+
+ // The outer display should become the pacesetter if designated.
+ mFlinger.scheduler()->setPacesetterDisplay(outerDisplay->getPhysicalId());
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index 622717f..7839ef0 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -28,9 +28,7 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockEventThread.h"
#include "mock/MockTimeStats.h"
-#include "mock/MockVsyncController.h"
#include "mock/system/window/MockNativeWindow.h"
using namespace android;
@@ -53,8 +51,6 @@
public:
void SetUp() override;
- void setupScheduler();
-
protected:
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
@@ -68,7 +64,7 @@
};
void SurfaceFlingerPowerHintTest::SetUp() {
- setupScheduler();
+ mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
mComposer = new Hwc2::mock::Composer();
mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
@@ -96,39 +92,11 @@
.setDisplaySurface(mDisplaySurface)
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
+ .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector())
+ .skipRegisterDisplay()
.inject();
}
-void SurfaceFlingerPowerHintTest::setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread),
- TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
- TestableSurfaceFlinger::kTwoDisplayModes);
-}
-
TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) {
ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true));
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index a0aaa68..7754c21 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -59,54 +59,45 @@
};
struct EventThreadBaseSupportedVariant {
- static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
- // The callback should not be notified to toggle VSYNC.
- EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_)).Times(0);
-
- // The event thread should not be notified.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+ static void setupVsyncNoCallExpectations(DisplayTransactionTest* test) {
+ // Expect no change to hardware nor synthetic VSYNC.
+ EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, _)).Times(0);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
}
};
struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupVsyncAndEventThreadNoCallExpectations(test);
+ static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ setupVsyncNoCallExpectations(test);
}
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupVsyncAndEventThreadNoCallExpectations(test);
+ static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ setupVsyncNoCallExpectations(test);
}
};
struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The callback should be notified to enable VSYNC.
- EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(true)).Times(1);
-
- // The event thread should be notified that the screen was acquired.
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+ static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // Expect to enable hardware VSYNC and disable synthetic VSYNC.
+ EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, true)).Times(1);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1);
}
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The callback should be notified to disable VSYNC.
- EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(false)).Times(1);
-
- // The event thread should not be notified that the screen was released.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+ static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // Expect to disable hardware VSYNC and enable synthetic VSYNC.
+ EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, false)).Times(1);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1);
}
};
struct DispSyncIsSupportedVariant {
static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_VSYNC_PERIOD)).Times(1);
- EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1);
+ auto vsyncSchedule = test->mFlinger.scheduler()->getVsyncSchedule();
+ EXPECT_CALL(static_cast<mock::VsyncController&>(vsyncSchedule->getController()),
+ startPeriodTransition(DEFAULT_VSYNC_PERIOD, false))
+ .Times(1);
+ EXPECT_CALL(static_cast<mock::VSyncTracker&>(vsyncSchedule->getTracker()), resetModel())
+ .Times(1);
}
};
@@ -133,7 +124,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::EventThread::setupEnableVsyncCallExpectations(test);
Case::DispSync::setupResetModelCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -148,7 +139,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -160,7 +151,7 @@
struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::EventThread::setupDisableVsyncCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
}
@@ -173,7 +164,7 @@
: public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
}
@@ -185,7 +176,7 @@
struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
};
@@ -194,7 +185,7 @@
: public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::EventThread::setupEnableVsyncCallExpectations(test);
Case::DispSync::setupResetModelCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
@@ -203,7 +194,7 @@
struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
};
@@ -212,7 +203,7 @@
: public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::EventThread::setupEnableVsyncCallExpectations(test);
Case::DispSync::setupResetModelCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
@@ -222,7 +213,7 @@
: public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::EventThread::setupDisableVsyncCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
}
};
@@ -231,7 +222,7 @@
: public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupNoComposerPowerModeCallExpectations(test);
}
};
@@ -262,8 +253,9 @@
return display;
}
- static void setInitialHwVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
- test->mFlinger.scheduler()->setInitialHwVsyncEnabled(enabled);
+ static void setInitialHwVsyncEnabled(DisplayTransactionTest* test, PhysicalDisplayId id,
+ bool enabled) {
+ test->mFlinger.scheduler()->setInitialHwVsyncEnabled(id, enabled);
}
static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
@@ -329,9 +321,12 @@
Case::Doze::setupComposerCallExpectations(this);
auto display =
Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
- Case::setInitialHwVsyncEnabled(this,
- PowerModeInitialVSyncEnabled<
- Case::Transition::INITIAL_POWER_MODE>::value);
+ auto displayId = display->getId();
+ if (auto physicalDisplayId = PhysicalDisplayId::tryCast(displayId)) {
+ Case::setInitialHwVsyncEnabled(this, *physicalDisplayId,
+ PowerModeInitialVSyncEnabled<
+ Case::Transition::INITIAL_POWER_MODE>::value);
+ }
// --------------------------------------------------------------------
// Call Expectations
@@ -484,38 +479,5 @@
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
-// TODO(b/262417075)
-TEST_F(SetPowerModeInternalTest, DISABLED_designatesLeaderDisplay) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Inject a primary display with uninitialized power mode.
- constexpr bool kInitPowerMode = false;
- Case::Display::injectHwcDisplay<kInitPowerMode>(this);
- auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
- injector.setPowerMode(std::nullopt);
- const auto display = injector.inject();
-
- // --------------------------------------------------------------------
- // Invocation
-
- // FakeDisplayDeviceInjector registers the display with Scheduler, so it has already been
- // designated as the leader. Set an arbitrary leader to verify that `setPowerModeInternal`
- // designates a leader regardless of any preceding `Scheduler::registerDisplay` call(s).
- constexpr PhysicalDisplayId kPlaceholderId = PhysicalDisplayId::fromPort(42);
- ASSERT_NE(display->getPhysicalId(), kPlaceholderId);
- mFlinger.scheduler()->setLeaderDisplay(kPlaceholderId);
-
- mFlinger.setPowerModeInternal(display, PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The primary display should be designated as the leader.
- EXPECT_EQ(mFlinger.scheduler()->leaderDisplayId(), display->getPhysicalId());
-}
-
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
index fed6a1a..0e5f1ea 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
@@ -3,47 +3,17 @@
#include <gui/LayerMetadata.h>
#include "TestableSurfaceFlinger.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
namespace android {
using testing::_;
using testing::Return;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test {
public:
- SurfaceFlingerUpdateLayerMetadataSnapshotTest() { setupScheduler(); }
+ SurfaceFlingerUpdateLayerMetadataSnapshotTest() { mFlinger.setupMockScheduler(); }
protected:
- void setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread));
- }
-
sp<Layer> createLayer(const char* name, LayerMetadata& inOutlayerMetadata) {
LayerCreationArgs args =
LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, inOutlayerMetadata};
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index bd3f3ca..f1a5fc4 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -37,19 +37,16 @@
public:
TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
- std::make_unique<mock::VSyncTracker>(), std::move(selectorPtr),
+ std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
/* modulatorPtr */ nullptr, callback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
- std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
+ std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
: Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
- mVsyncSchedule = std::unique_ptr<VsyncSchedule>(
- new VsyncSchedule(std::move(tracker), std::make_unique<mock::VSyncDispatch>(),
- std::move(controller)));
-
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
- registerDisplay(displayId, std::move(selectorPtr));
+ registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
+ std::move(tracker));
ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
// Execute task to prevent broken promise exception on destruction.
@@ -66,15 +63,28 @@
return Scheduler::createConnection(std::move(eventThread));
}
- auto refreshRateSelector() { return leaderSelectorPtr(); }
+ auto refreshRateSelector() { return pacesetterSelectorPtr(); }
const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
return mRefreshRateSelectors;
}
void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
+ registerDisplay(displayId, std::move(selectorPtr),
+ std::make_unique<mock::VsyncController>(),
+ std::make_shared<mock::VSyncTracker>());
+ }
+
+ void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ std::unique_ptr<VsyncController> controller,
+ std::shared_ptr<VSyncTracker> tracker) {
ftl::FakeGuard guard(kMainThreadContext);
- Scheduler::registerDisplay(displayId, std::move(selectorPtr));
+ Scheduler::registerDisplayInternal(displayId, std::move(selectorPtr),
+ std::shared_ptr<VsyncSchedule>(
+ new VsyncSchedule(displayId, std::move(tracker),
+ std::make_shared<
+ mock::VSyncDispatch>(),
+ std::move(controller))));
}
void unregisterDisplay(PhysicalDisplayId displayId) {
@@ -82,16 +92,16 @@
Scheduler::unregisterDisplay(displayId);
}
- std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
- return mLeaderDisplayId;
+ std::optional<PhysicalDisplayId> pacesetterDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+ return mPacesetterDisplayId;
}
- void setLeaderDisplay(PhysicalDisplayId displayId) {
+ void setPacesetterDisplay(PhysicalDisplayId displayId) {
ftl::FakeGuard guard(kMainThreadContext);
- Scheduler::setLeaderDisplay(displayId);
+ Scheduler::setPacesetterDisplay(displayId);
}
- auto& mutableVsyncModulator() { return *mVsyncModulator; }
+ auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
auto& mutableLayerHistory() { return mLayerHistory; }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
@@ -150,10 +160,11 @@
Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
- void setInitialHwVsyncEnabled(bool enabled) {
- std::lock_guard<std::mutex> lock(mVsyncSchedule->mHwVsyncLock);
- mVsyncSchedule->mHwVsyncState = enabled ? VsyncSchedule::HwVsyncState::Enabled
- : VsyncSchedule::HwVsyncState::Disabled;
+ void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) {
+ auto schedule = getVsyncSchedule(id);
+ std::lock_guard<std::mutex> lock(schedule->mHwVsyncLock);
+ schedule->mHwVsyncState = enabled ? VsyncSchedule::HwVsyncState::Enabled
+ : VsyncSchedule::HwVsyncState::Disabled;
}
private:
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 89a661f..fc9e653 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -28,6 +28,7 @@
#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>
@@ -47,14 +48,12 @@
#include "TestableScheduler.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockEventThread.h"
#include "mock/MockFrameTimeline.h"
#include "mock/MockFrameTracer.h"
#include "mock/MockSchedulerCallback.h"
namespace android {
-
-class EventThread;
-
namespace renderengine {
class RenderEngine;
@@ -151,6 +150,11 @@
CreateCompositionEngineFunction mCreateCompositionEngine;
};
+struct MockSchedulerOptions {
+ PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0);
+ bool useNiceMock = false;
+};
+
} // namespace surfaceflinger::test
class TestableSurfaceFlinger {
@@ -189,38 +193,31 @@
enum class SchedulerCallbackImpl { kNoOp, kMock };
- static constexpr struct OneDisplayMode {
- } kOneDisplayMode;
+ struct DefaultDisplayMode {
+ // The ID of the injected RefreshRateSelector and its default display mode.
+ PhysicalDisplayId displayId;
+ };
- static constexpr struct TwoDisplayModes {
- } kTwoDisplayModes;
+ using RefreshRateSelectorPtr = scheduler::Scheduler::RefreshRateSelectorPtr;
- using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>;
-
- using DisplayModesVariant =
- std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateSelectorPtr>;
+ using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>;
void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
- std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
+ std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
+ DisplayModesVariant modesVariant,
SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
- DisplayModesVariant modesVariant = kOneDisplayMode,
bool useNiceMock = false) {
- RefreshRateSelectorPtr selectorPtr;
- if (std::holds_alternative<RefreshRateSelectorPtr>(modesVariant)) {
- selectorPtr = std::move(std::get<RefreshRateSelectorPtr>(modesVariant));
- } else {
- constexpr DisplayModeId kModeId60{0};
- DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
-
- if (std::holds_alternative<TwoDisplayModes>(modesVariant)) {
- constexpr DisplayModeId kModeId90{1};
- modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
- }
-
- selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
- }
+ RefreshRateSelectorPtr selectorPtr = ftl::match(
+ modesVariant,
+ [](DefaultDisplayMode arg) {
+ constexpr DisplayModeId kModeId60{0};
+ return std::make_shared<scheduler::RefreshRateSelector>(
+ makeModes(mock::createDisplayMode(arg.displayId, kModeId60, 60_Hz)),
+ kModeId60);
+ },
+ [](RefreshRateSelectorPtr selectorPtr) { return selectorPtr; });
const auto fps = selectorPtr->getActiveMode().fps;
mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
@@ -253,13 +250,47 @@
std::move(modulatorPtr), callback);
}
- mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), *mTokenManager, 0ms);
+ mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms);
- mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
+ mScheduler->mutableAppConnectionHandle() =
+ mScheduler->createConnection(std::move(appEventThread));
+
+ mFlinger->mAppConnectionHandle = mScheduler->mutableAppConnectionHandle();
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
resetScheduler(mScheduler);
}
+ void setupMockScheduler(test::MockSchedulerOptions options = {}) {
+ using testing::_;
+ using testing::Return;
+
+ auto eventThread = makeMock<mock::EventThread>(options.useNiceMock);
+ auto sfEventThread = makeMock<mock::EventThread>(options.useNiceMock);
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
+ mock::EventThread::kCallingUid,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
+ mock::EventThread::kCallingUid,
+ ResyncCallback())));
+
+ auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock);
+ auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock);
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread),
+ std::move(sfEventThread), DefaultDisplayMode{options.displayId},
+ SchedulerCallbackImpl::kNoOp, options.useNiceMock);
+ }
+
void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
scheduler::TestableScheduler& mutableScheduler() { return *mScheduler; }
@@ -388,10 +419,7 @@
return mFlinger->setDisplayStateLocked(s);
}
- // Allow reading display state without locking, as if called on the SF main thread.
- auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
- return mFlinger->onInitializeDisplays();
- }
+ void initializeDisplays() FTL_FAKE_GUARD(kMainThreadContext) { mFlinger->initializeDisplays(); }
auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
@@ -474,10 +502,11 @@
return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
}
- void onActiveDisplayChanged(const DisplayDevice& activeDisplay) {
+ void onActiveDisplayChanged(const DisplayDevice* inactiveDisplayPtr,
+ const DisplayDevice& activeDisplay) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
- mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay);
+ mFlinger->onActiveDisplayChangedLocked(inactiveDisplayPtr, activeDisplay);
}
auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
@@ -773,16 +802,16 @@
return mFlinger.mutableDisplays().get(mDisplayToken)->get();
}
- // If `selectorPtr` is nullptr, the injector creates RefreshRateSelector from the `modes`.
- // Otherwise, it uses `selectorPtr`, which the caller must create using the same `modes`.
- //
- // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateSelector, remove
- // the `selectorPtr` parameter in favor of an alternative setRefreshRateSelector API.
- auto& setDisplayModes(
- DisplayModes modes, DisplayModeId activeModeId,
- std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr = nullptr) {
+ auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
mDisplayModes = std::move(modes);
mCreationArgs.activeModeId = activeModeId;
+ mCreationArgs.refreshRateSelector = nullptr;
+ return *this;
+ }
+
+ auto& setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
+ mDisplayModes = selectorPtr->displayModes();
+ mCreationArgs.activeModeId = selectorPtr->getActiveMode().modePtr->getId();
mCreationArgs.refreshRateSelector = std::move(selectorPtr);
return *this;
}
@@ -824,6 +853,11 @@
return *this;
}
+ auto& skipRegisterDisplay() {
+ mRegisterDisplay = false;
+ return *this;
+ }
+
sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
@@ -887,7 +921,7 @@
ui::ColorModes(),
std::nullopt);
- if (mFlinger.scheduler()) {
+ if (mFlinger.scheduler() && mRegisterDisplay) {
mFlinger.scheduler()->registerDisplay(physicalId,
display->holdRefreshRateSelector());
}
@@ -906,11 +940,22 @@
sp<BBinder> mDisplayToken = sp<BBinder>::make();
DisplayDeviceCreationArgs mCreationArgs;
DisplayModes mDisplayModes;
+ bool mRegisterDisplay = true;
const std::optional<ui::DisplayConnectionType> mConnectionType;
const std::optional<hal::HWDisplayId> mHwcDisplayId;
};
private:
+ template <typename T>
+ static std::unique_ptr<T> makeMock(bool useNiceMock) {
+ return useNiceMock ? std::make_unique<testing::NiceMock<T>>() : std::make_unique<T>();
+ }
+
+ template <typename T>
+ static std::shared_ptr<T> makeSharedMock(bool useNiceMock) {
+ return useNiceMock ? std::make_shared<testing::NiceMock<T>>() : std::make_shared<T>();
+ }
+
static constexpr VsyncId kVsyncId{123};
surfaceflinger::test::Factory mFactory;
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 859f702..d4e2357 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -33,15 +33,12 @@
#include "FrontEnd/TransactionHandler.h"
#include "TestableSurfaceFlinger.h"
#include "TransactionState.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
namespace android {
using testing::_;
using testing::Return;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
using frontend::TransactionHandler;
constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5);
@@ -52,7 +49,9 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- setupScheduler();
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.setupMockScheduler();
+ mFlinger.flinger()->addTransactionReadyFilters();
}
~TransactionApplicationTest() {
@@ -61,38 +60,8 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
- void setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*mVSyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
- mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
- std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
- std::move(eventThread), std::move(sfEventThread));
- mFlinger.flinger()->addTransactionReadyFilters();
- }
-
TestableSurfaceFlinger mFlinger;
- mock::VsyncController* mVsyncController = new mock::VsyncController();
- mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
-
struct TransactionInfo {
Vector<ComposerState> states;
Vector<DisplayState> displays;
@@ -230,7 +199,7 @@
void modulateVsync() {
static_cast<void>(
- mFlinger.mutableScheduler().mutableVsyncModulator().onRefreshRateChangeInitiated());
+ mFlinger.mutableScheduler().vsyncModulator().onRefreshRateChangeInitiated());
}
bool mHasListenerCallbacks = false;
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 1173d1c..764d19b 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -28,15 +28,13 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
namespace android {
using testing::_;
using testing::Mock;
using testing::Return;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
using PresentState = frametimeline::SurfaceFrame::PresentState;
class TransactionFrameTracerTest : public testing::Test {
@@ -45,7 +43,7 @@
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());
- setupScheduler();
+ mFlinger.setupMockScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
}
@@ -68,33 +66,6 @@
layer->commitTransaction(c);
}
- void setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread));
- }
-
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index b6427c0..3dea189 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -19,6 +19,7 @@
#include <limits> // std::numeric_limits
#include <gui/SurfaceComposerClient.h>
+#include "LayerProtoHelper.h"
#include "Tracing/TransactionProtoParser.h"
@@ -27,7 +28,6 @@
namespace android {
TEST(TransactionProtoParserTest, parse) {
- const sp<IBinder> layerHandle = sp<BBinder>::make();
const sp<IBinder> displayHandle = sp<BBinder>::make();
TransactionState t1;
t1.originPid = 1;
@@ -37,7 +37,6 @@
t1.postTime = 5;
layer_state_t layer;
- layer.layerId = 6;
layer.what = std::numeric_limits<uint64_t>::max();
layer.what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
layer.x = 7;
@@ -48,10 +47,9 @@
for (uint32_t i = 0; i < layerCount; i++) {
ResolvedComposerState s;
if (i == 1) {
- layer.parentSurfaceControlForChild =
- sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), layerHandle, 42,
- "#42");
+ s.parentId = 42;
}
+ s.layerId = 6 + i;
s.state = layer;
t1.states.emplace_back(s);
}
@@ -72,18 +70,10 @@
class TestMapper : public TransactionProtoParser::FlingerDataMapper {
public:
- sp<IBinder> layerHandle;
sp<IBinder> displayHandle;
- TestMapper(sp<IBinder> layerHandle, sp<IBinder> displayHandle)
- : layerHandle(layerHandle), displayHandle(displayHandle) {}
+ TestMapper(sp<IBinder> displayHandle) : displayHandle(displayHandle) {}
- sp<IBinder> getLayerHandle(int32_t id) const override {
- return (id == 42) ? layerHandle : nullptr;
- }
- int64_t getLayerId(const sp<IBinder>& handle) const override {
- return (handle == layerHandle) ? 42 : -1;
- }
sp<IBinder> getDisplayHandle(int32_t id) const {
return (id == 43) ? displayHandle : nullptr;
}
@@ -92,7 +82,7 @@
}
};
- TransactionProtoParser parser(std::make_unique<TestMapper>(layerHandle, displayHandle));
+ TransactionProtoParser parser(std::make_unique<TestMapper>(displayHandle));
proto::TransactionState proto = parser.toProto(t1);
TransactionState t2 = parser.fromProto(proto);
@@ -105,8 +95,8 @@
ASSERT_EQ(t1.states.size(), t2.states.size());
ASSERT_EQ(t1.states[0].state.x, t2.states[0].state.x);
ASSERT_EQ(t1.states[0].state.matrix.dsdx, t2.states[0].state.matrix.dsdx);
- ASSERT_EQ(t1.states[1].state.parentSurfaceControlForChild->getHandle(),
- t2.states[1].state.parentSurfaceControlForChild->getHandle());
+ ASSERT_EQ(t1.states[1].layerId, t2.states[1].layerId);
+ ASSERT_EQ(t1.states[1].parentId, t2.states[1].parentId);
ASSERT_EQ(t1.displays.size(), t2.displays.size());
ASSERT_EQ(t1.displays[1].width, t2.displays[1].width);
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index ae03db4..e2c6491 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -28,15 +28,13 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
namespace android {
using testing::_;
using testing::Mock;
using testing::Return;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
using PresentState = frametimeline::SurfaceFrame::PresentState;
class TransactionSurfaceFrameTest : public testing::Test {
@@ -45,7 +43,7 @@
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());
- setupScheduler();
+ mFlinger.setupMockScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
}
@@ -67,33 +65,6 @@
layer->commitTransaction(c);
}
- void setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread));
- }
-
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 482c3a8..74541ac 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -18,7 +18,11 @@
#include <gtest/gtest.h>
#include <gui/SurfaceComposerClient.h>
+#include <cstdint>
+#include "Client.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/Update.h"
#include "Tracing/RingBuffer.h"
#include "Tracing/TransactionTracing.h"
@@ -42,14 +46,15 @@
}
void queueAndCommitTransaction(int64_t vsyncId) {
+ frontend::Update update;
TransactionState transaction;
transaction.id = static_cast<uint64_t>(vsyncId * 3);
transaction.originUid = 1;
transaction.originPid = 2;
mTracing.addQueuedTransaction(transaction);
std::vector<TransactionState> transactions;
- transactions.emplace_back(transaction);
- mTracing.addCommittedTransactions(transactions, vsyncId);
+ update.transactions.emplace_back(transaction);
+ mTracing.addCommittedTransactions(vsyncId, 0, update, {}, false);
flush(vsyncId);
}
@@ -57,13 +62,25 @@
const std::vector<TransactionState>& expectedTransactions,
int64_t expectedVsyncId) {
EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
- EXPECT_EQ(actualProto.transactions().size(),
+ ASSERT_EQ(actualProto.transactions().size(),
static_cast<int32_t>(expectedTransactions.size()));
for (uint32_t i = 0; i < expectedTransactions.size(); i++) {
EXPECT_EQ(actualProto.transactions(static_cast<int32_t>(i)).pid(),
expectedTransactions[i].originPid);
}
}
+
+ LayerCreationArgs getLayerCreationArgs(uint32_t layerId, uint32_t parentId,
+ uint32_t layerIdToMirror, uint32_t flags,
+ bool addToRoot) {
+ LayerCreationArgs args;
+ args.sequence = layerId;
+ args.parentId = parentId;
+ args.layerIdToMirror = layerIdToMirror;
+ args.flags = flags;
+ args.addToRoot = addToRoot;
+ return args;
+ }
};
TEST_F(TransactionTracingTest, addTransactions) {
@@ -79,57 +96,59 @@
// Split incoming transactions into two and commit them in reverse order to test out of order
// commits.
- std::vector<TransactionState> firstTransactionSet =
- std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
int64_t firstTransactionSetVsyncId = 42;
- mTracing.addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
+ frontend::Update firstUpdate;
+ firstUpdate.transactions =
+ std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
+ mTracing.addCommittedTransactions(firstTransactionSetVsyncId, 0, firstUpdate, {}, false);
int64_t secondTransactionSetVsyncId = 43;
- std::vector<TransactionState> secondTransactionSet =
+ frontend::Update secondUpdate;
+ secondUpdate.transactions =
std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
- mTracing.addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
+ mTracing.addCommittedTransactions(secondTransactionSetVsyncId, 0, secondUpdate, {}, false);
flush(secondTransactionSetVsyncId);
proto::TransactionTraceFile proto = writeToProto();
- EXPECT_EQ(proto.entry().size(), 2);
- verifyEntry(proto.entry(0), firstTransactionSet, firstTransactionSetVsyncId);
- verifyEntry(proto.entry(1), secondTransactionSet, secondTransactionSetVsyncId);
+ ASSERT_EQ(proto.entry().size(), 2);
+ verifyEntry(proto.entry(0), firstUpdate.transactions, firstTransactionSetVsyncId);
+ verifyEntry(proto.entry(1), secondUpdate.transactions, secondTransactionSetVsyncId);
}
class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
protected:
void SetUp() override {
- // add layers
mTracing.setBufferSize(SMALL_BUFFER_SIZE);
- const sp<IBinder> fakeLayerHandle = sp<BBinder>::make();
- mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
- 123 /* flags */, -1 /* parentId */);
- const sp<IBinder> fakeChildLayerHandle = sp<BBinder>::make();
- mTracing.onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
- 456 /* flags */, mParentLayerId);
- // add some layer transaction
+ // add layers and add some layer transaction
{
+ frontend::Update update;
+ update.layerCreationArgs.emplace_back(std::move(
+ getLayerCreationArgs(mParentLayerId, /*parentId=*/UNASSIGNED_LAYER_ID,
+ /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123,
+ /*addToRoot=*/true)));
+ update.layerCreationArgs.emplace_back(std::move(
+ getLayerCreationArgs(mChildLayerId, mParentLayerId,
+ /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/456,
+ /*addToRoot=*/true)));
TransactionState transaction;
transaction.id = 50;
ResolvedComposerState layerState;
- layerState.state.surface = fakeLayerHandle;
+ layerState.layerId = mParentLayerId;
layerState.state.what = layer_state_t::eLayerChanged;
layerState.state.z = 42;
transaction.states.emplace_back(layerState);
ResolvedComposerState childState;
- childState.state.surface = fakeChildLayerHandle;
+ childState.layerId = mChildLayerId;
childState.state.what = layer_state_t::eLayerChanged;
childState.state.z = 43;
transaction.states.emplace_back(childState);
mTracing.addQueuedTransaction(transaction);
- std::vector<TransactionState> transactions;
- transactions.emplace_back(transaction);
+ update.transactions.emplace_back(transaction);
VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
- mTracing.onLayerAddedToDrawingState(mParentLayerId, VSYNC_ID_FIRST_LAYER_CHANGE);
- mTracing.onLayerAddedToDrawingState(mChildLayerId, VSYNC_ID_FIRST_LAYER_CHANGE);
- mTracing.addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+ mTracing.addCommittedTransactions(VSYNC_ID_FIRST_LAYER_CHANGE, 0, update, {}, false);
+
flush(VSYNC_ID_FIRST_LAYER_CHANGE);
}
@@ -139,17 +158,17 @@
TransactionState transaction;
transaction.id = 51;
ResolvedComposerState layerState;
- layerState.state.surface = fakeLayerHandle;
+ layerState.layerId = mParentLayerId;
layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
layerState.state.z = 41;
layerState.state.x = 22;
transaction.states.emplace_back(layerState);
mTracing.addQueuedTransaction(transaction);
- std::vector<TransactionState> transactions;
- transactions.emplace_back(transaction);
+ frontend::Update update;
+ update.transactions.emplace_back(transaction);
VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
- mTracing.addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+ mTracing.addCommittedTransactions(VSYNC_ID_SECOND_LAYER_CHANGE, 0, update, {}, false);
flush(VSYNC_ID_SECOND_LAYER_CHANGE);
}
@@ -163,8 +182,8 @@
queueAndCommitTransaction(++mVsyncId);
}
- int mParentLayerId = 1;
- int mChildLayerId = 2;
+ uint32_t mParentLayerId = 1;
+ uint32_t mChildLayerId = 2;
int64_t mVsyncId = 0;
int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
@@ -232,42 +251,42 @@
class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
protected:
void SetUp() override {
- // add layers
mTracing.setBufferSize(SMALL_BUFFER_SIZE);
- const sp<IBinder> fakeLayerHandle = sp<BBinder>::make();
- mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
- 123 /* flags */, -1 /* parentId */);
- const sp<IBinder> fakeMirrorLayerHandle = sp<BBinder>::make();
- mTracing.onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
- mLayerId);
- mTracing.onLayerAddedToDrawingState(mLayerId, mVsyncId);
- mTracing.onLayerAddedToDrawingState(mMirrorLayerId, mVsyncId);
- // add some layer transaction
+ // add layers and some layer transaction
{
+ frontend::Update update;
+ update.layerCreationArgs.emplace_back(
+ getLayerCreationArgs(mLayerId, /*parentId=*/UNASSIGNED_LAYER_ID,
+ /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123,
+ /*addToRoot=*/true));
+ update.layerCreationArgs.emplace_back(
+ getLayerCreationArgs(mMirrorLayerId, UNASSIGNED_LAYER_ID,
+ /*layerIdToMirror=*/mLayerId, /*flags=*/0,
+ /*addToRoot=*/false));
+
TransactionState transaction;
transaction.id = 50;
ResolvedComposerState layerState;
- layerState.state.surface = fakeLayerHandle;
+ layerState.layerId = mLayerId;
layerState.state.what = layer_state_t::eLayerChanged;
layerState.state.z = 42;
transaction.states.emplace_back(layerState);
ResolvedComposerState mirrorState;
- mirrorState.state.surface = fakeMirrorLayerHandle;
+ mirrorState.layerId = mMirrorLayerId;
mirrorState.state.what = layer_state_t::eLayerChanged;
mirrorState.state.z = 43;
transaction.states.emplace_back(mirrorState);
mTracing.addQueuedTransaction(transaction);
- std::vector<TransactionState> transactions;
- transactions.emplace_back(transaction);
- mTracing.addCommittedTransactions(transactions, mVsyncId);
+ update.transactions.emplace_back(transaction);
+ mTracing.addCommittedTransactions(mVsyncId, 0, update, {}, false);
flush(mVsyncId);
}
}
- int mLayerId = 5;
- int mMirrorLayerId = 55;
+ uint32_t mLayerId = 5;
+ uint32_t mMirrorLayerId = 55;
int64_t mVsyncId = 0;
int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index da87f1d..108151e 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -25,7 +25,6 @@
#include "TestableSurfaceFlinger.h"
#include "TunnelModeEnabledReporter.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
namespace android {
@@ -36,8 +35,6 @@
using android::Hwc2::IComposer;
using android::Hwc2::IComposerClient;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
constexpr int DEFAULT_SIDEBAND_STREAM = 51;
struct TestableTunnelModeEnabledListener : public gui::BnTunnelModeEnabledListener {
@@ -61,8 +58,6 @@
static constexpr uint32_t HEIGHT = 100;
static constexpr uint32_t LAYER_FLAGS = 0;
- void setupScheduler();
- void setupComposer(uint32_t virtualDisplayCount);
sp<Layer> createBufferStateLayer(LayerMetadata metadata);
TestableSurfaceFlinger mFlinger;
@@ -80,7 +75,7 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- setupScheduler();
+ mFlinger.setupMockScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter;
mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false);
@@ -100,33 +95,6 @@
return sp<Layer>::make(args);
}
-void TunnelModeEnabledReporterTest::setupScheduler() {
- auto eventThread = std::make_unique<mock::EventThread>();
- auto sfEventThread = std::make_unique<mock::EventThread>();
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
-
- auto vsyncController = std::make_unique<mock::VsyncController>();
- auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- EXPECT_CALL(*vsyncTracker, currentPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
- std::move(eventThread), std::move(sfEventThread));
-}
-
namespace {
TEST_F(TunnelModeEnabledReporterTest, callsAddedListeners) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 2908834..41866a1 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -109,7 +109,8 @@
class RepeatingCallbackReceiver {
public:
- RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t workload, nsecs_t readyDuration)
+ RepeatingCallbackReceiver(std::shared_ptr<VSyncDispatch> dispatch, nsecs_t workload,
+ nsecs_t readyDuration)
: mWorkload(workload),
mReadyDuration(readyDuration),
mCallback(
@@ -166,9 +167,10 @@
};
TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
- FixedRateIdealStubTracker tracker;
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
- mVsyncMoveThreshold);
+ auto tracker = std::make_shared<FixedRateIdealStubTracker>();
+ auto dispatch =
+ std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+ mDispatchGroupThreshold, mVsyncMoveThreshold);
static size_t constexpr num_clients = 3;
std::array<RepeatingCallbackReceiver, num_clients>
@@ -195,14 +197,15 @@
// starts at 333hz, slides down to 43hz
TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
auto next_vsync_interval = toNs(3ms);
- VRRStubTracker tracker(next_vsync_interval);
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
- mVsyncMoveThreshold);
+ auto tracker = std::make_shared<VRRStubTracker>(next_vsync_interval);
+ auto dispatch =
+ std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+ mDispatchGroupThreshold, mVsyncMoveThreshold);
RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
auto const on_each_frame = [&](nsecs_t last_known) {
- tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
+ tracker->set_interval(next_vsync_interval += toNs(1ms), last_known);
};
std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
@@ -213,9 +216,10 @@
// starts at 333hz, jumps to 200hz at frame 10
TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
- VRRStubTracker tracker(toNs(3ms));
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
- mVsyncMoveThreshold);
+ auto tracker = std::make_shared<VRRStubTracker>(toNs(3ms));
+ auto dispatch =
+ std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+ mDispatchGroupThreshold, mVsyncMoveThreshold);
RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
@@ -223,7 +227,7 @@
auto constexpr jump_frame_at = 10u;
auto const on_each_frame = [&](nsecs_t last_known) {
if (jump_frame_counter++ == jump_frame_at) {
- tracker.set_interval(toNs(5ms), last_known);
+ tracker->set_interval(toNs(5ms), last_known);
}
};
std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index f143b49..7af1da6 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -116,13 +116,14 @@
class CountingCallback {
public:
- CountingCallback(VSyncDispatch& dispatch)
- : mDispatch(dispatch),
- mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
- std::placeholders::_1, std::placeholders::_2,
- std::placeholders::_3),
- "test")) {}
- ~CountingCallback() { mDispatch.unregisterCallback(mToken); }
+ CountingCallback(std::shared_ptr<VSyncDispatch> dispatch)
+ : mDispatch(std::move(dispatch)),
+ mToken(mDispatch->registerCallback(std::bind(&CountingCallback::counter, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3),
+ "test")) {}
+ ~CountingCallback() { mDispatch->unregisterCallback(mToken); }
operator VSyncDispatch::CallbackToken() const { return mToken; }
@@ -132,7 +133,7 @@
mReadyTime.push_back(readyTime);
}
- VSyncDispatch& mDispatch;
+ std::shared_ptr<VSyncDispatch> mDispatch;
VSyncDispatch::CallbackToken mToken;
std::vector<nsecs_t> mCalls;
std::vector<nsecs_t> mWakeupTime;
@@ -141,12 +142,12 @@
class PausingCallback {
public:
- PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount)
- : mDispatch(dispatch),
- mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this,
- std::placeholders::_1,
- std::placeholders::_2),
- "test")),
+ PausingCallback(std::shared_ptr<VSyncDispatch> dispatch, std::chrono::milliseconds pauseAmount)
+ : mDispatch(std::move(dispatch)),
+ mToken(mDispatch->registerCallback(std::bind(&PausingCallback::pause, this,
+ std::placeholders::_1,
+ std::placeholders::_2),
+ "test")),
mRegistered(true),
mPauseAmount(pauseAmount) {}
~PausingCallback() { unregister(); }
@@ -181,12 +182,12 @@
void unregister() {
if (mRegistered) {
- mDispatch.unregisterCallback(mToken);
+ mDispatch->unregisterCallback(mToken);
mRegistered = false;
}
}
- VSyncDispatch& mDispatch;
+ std::shared_ptr<VSyncDispatch> mDispatch;
VSyncDispatch::CallbackToken mToken;
bool mRegistered = true;
@@ -231,22 +232,26 @@
static nsecs_t constexpr mDispatchGroupThreshold = 5;
nsecs_t const mPeriod = 1000;
nsecs_t const mVsyncMoveThreshold = 300;
- NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
- VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
- mVsyncMoveThreshold};
+ std::shared_ptr<NiceMock<MockVSyncTracker>> mStubTracker =
+ std::make_shared<NiceMock<MockVSyncTracker>>(mPeriod);
+ std::shared_ptr<VSyncDispatch> mDispatch =
+ std::make_shared<VSyncDispatchTimerQueue>(createTimeKeeper(), mStubTracker,
+ mDispatchGroupThreshold, mVsyncMoveThreshold);
};
TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
{
- VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
- mVsyncMoveThreshold};
+ std::shared_ptr<VSyncDispatch> mDispatch =
+ std::make_shared<VSyncDispatchTimerQueue>(createTimeKeeper(), mStubTracker,
+ mDispatchGroupThreshold,
+ mVsyncMoveThreshold);
CountingCallback cb(mDispatch);
- const auto result = mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 1000});
+ const auto result = mDispatch->schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -257,10 +262,10 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- const auto result = mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = intended});
+ const auto result = mDispatch->schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
@@ -277,14 +282,15 @@
EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
CountingCallback cb(mDispatch);
- auto result = mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = intended});
+ auto result = mDispatch->schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
- result = mDispatch.update(cb,
+ result =
+ mDispatch->update(cb,
{.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(700, *result);
@@ -302,17 +308,17 @@
CountingCallback cb(mDispatch);
const auto result =
- mDispatch.update(cb,
- {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+ mDispatch->update(cb,
+ {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
EXPECT_FALSE(result.has_value());
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
EXPECT_CALL(mMockClock, alarmAt(_, 1050));
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -323,15 +329,15 @@
auto const now = 234;
mMockClock.advanceBy(234);
auto const workDuration = 10 * mPeriod;
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + workDuration))
.WillOnce(Return(mPeriod * 11));
EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
CountingCallback cb(mDispatch);
- const auto result = mDispatch.schedule(cb,
- {.workDuration = workDuration,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result = mDispatch->schedule(cb,
+ {.workDuration = workDuration,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod, *result);
}
@@ -341,12 +347,13 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- const auto result =
- mDispatch.schedule(cb,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ const auto result = mDispatch->schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
- EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
+ EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
@@ -354,13 +361,14 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- const auto result =
- mDispatch.schedule(cb,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ const auto result = mDispatch->schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
mMockClock.advanceBy(950);
- EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+ EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
@@ -368,15 +376,16 @@
EXPECT_CALL(mMockClock, alarmCancel());
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
- const auto result =
- mDispatch.schedule(cb,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ const auto result = mDispatch->schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
- EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+ EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate);
cb.unpause();
pausingThread.join();
}
@@ -389,9 +398,10 @@
PausingCallback cb(mDispatch, 50ms);
cb.stashResource(resource);
- const auto result =
- mDispatch.schedule(cb,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ const auto result = mDispatch->schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
@@ -408,7 +418,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
.Times(4)
.WillOnce(Return(1055))
.WillOnce(Return(1063))
@@ -423,8 +433,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
- mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
advanceToNextCallback();
advanceToNextCallback();
@@ -436,7 +446,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) {
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
.Times(4)
.WillOnce(Return(1000))
.WillOnce(Return(2000))
@@ -450,21 +460,21 @@
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
EXPECT_THAT(cb.mCalls[0], Eq(1000));
- mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(2));
EXPECT_THAT(cb.mCalls[1], Eq(2000));
- mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
advanceToNextCallback();
@@ -473,7 +483,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
.Times(4)
.WillOnce(Return(10000))
.WillOnce(Return(1000))
@@ -488,10 +498,10 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
- mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
- mDispatch.cancel(cb1);
+ mDispatch->schedule(cb0,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
+ mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->cancel(cb1);
}
TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
@@ -502,9 +512,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
@@ -517,9 +527,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
@@ -537,10 +547,10 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1,
- {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1,
+ {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
ASSERT_THAT(cb0.mCalls.size(), Eq(1));
@@ -548,9 +558,11 @@
ASSERT_THAT(cb1.mCalls.size(), Eq(1));
EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
- mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
- mDispatch.schedule(cb1,
- {.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb1,
+ {.workDuration = notCloseOffset,
+ .readyDuration = 0,
+ .earliestVsync = 2000});
advanceToNextCallback();
ASSERT_THAT(cb1.mCalls.size(), Eq(2));
EXPECT_THAT(cb1.mCalls[1], Eq(2000));
@@ -570,32 +582,32 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
- EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
+ EXPECT_EQ(mDispatch->cancel(cb0), CancelResult::Cancelled);
}
TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
.Times(3)
.WillOnce(Return(950))
.WillOnce(Return(1975))
.WillOnce(Return(2950));
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
mMockClock.advanceBy(850);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
- mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
mMockClock.advanceBy(900);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
mMockClock.advanceBy(125);
EXPECT_THAT(cb.mCalls.size(), Eq(2));
- mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
mMockClock.advanceBy(975);
EXPECT_THAT(cb.mCalls.size(), Eq(3));
}
@@ -606,48 +618,48 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
VSyncDispatch::CallbackToken tmp;
- tmp = mDispatch.registerCallback(
+ tmp = mDispatch->registerCallback(
[&](auto, auto, auto) {
- mDispatch.schedule(tmp,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 2000});
+ mDispatch->schedule(tmp,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 2000});
},
"o.o");
- mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
VSyncDispatch::CallbackToken tmp;
std::optional<nsecs_t> lastTarget;
- tmp = mDispatch.registerCallback(
+ tmp = mDispatch->registerCallback(
[&](auto timestamp, auto, auto) {
auto result =
- mDispatch.schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp - mVsyncMoveThreshold});
- EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod + timestamp - 400, *result);
- result = mDispatch.schedule(tmp,
+ mDispatch->schedule(tmp,
{.workDuration = 400,
.readyDuration = 0,
- .earliestVsync = timestamp});
+ .earliestVsync = timestamp - mVsyncMoveThreshold});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod + timestamp - 400, *result);
- result = mDispatch.schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp + mVsyncMoveThreshold});
+ result = mDispatch->schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod + timestamp - 400, *result);
+ result = mDispatch->schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp + mVsyncMoveThreshold});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod + timestamp - 400, *result);
lastTarget = timestamp;
},
"oo");
- mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
EXPECT_THAT(lastTarget, Eq(1000));
@@ -663,16 +675,16 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
mMockClock.advanceBy(750);
- mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
- mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
mMockClock.advanceBy(800);
- mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
}
TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
@@ -685,12 +697,12 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
- mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
- mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
advanceToNextCallback();
@@ -702,8 +714,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
}
TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
@@ -713,29 +725,30 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
CountingCallback cb0(mDispatch);
- mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.cancel(cb0);
- mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->cancel(cb0);
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
}
TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
VSyncDispatch::CallbackToken token(100);
- EXPECT_FALSE(mDispatch
- .schedule(token,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
- .has_value());
- EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
+ EXPECT_FALSE(
+ mDispatch
+ ->schedule(token,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+ .has_value());
+ EXPECT_THAT(mDispatch->cancel(token), Eq(CancelResult::Error));
}
TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
auto result =
- mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
- result = mDispatch.schedule(cb0,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb0,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -745,14 +758,14 @@
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
auto result =
- mDispatch.schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- result = mDispatch.schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1200, *result);
advanceToNextCallback();
@@ -760,19 +773,19 @@
}
TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
.Times(2)
.WillOnce(Return(1000))
.WillOnce(Return(1002));
CountingCallback cb(mDispatch);
auto result =
- mDispatch.schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- result = mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(602, *result);
}
@@ -780,13 +793,13 @@
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
CountingCallback cb0(mDispatch);
auto result =
- mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
advanceToNextCallback();
- result = mDispatch.schedule(cb0,
- {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb0,
+ {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -797,13 +810,13 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
CountingCallback cb0(mDispatch);
auto result =
- mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
advanceToNextCallback();
- result = mDispatch.schedule(cb0,
- {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb0,
+ {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1100, *result);
}
@@ -813,13 +826,13 @@
CountingCallback cb(mDispatch);
auto result =
- mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch.schedule(cb,
- {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb,
+ {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
@@ -865,16 +878,16 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- result = mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
mMockClock.advanceBy(80);
@@ -893,16 +906,16 @@
CountingCallback cb(mDispatch);
auto result =
- mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- result = mDispatch.schedule(cb,
- {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb,
+ {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1630, *result);
mMockClock.advanceBy(80);
@@ -919,19 +932,19 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled);
+ EXPECT_EQ(mDispatch->cancel(cb2), CancelResult::Cancelled);
mMockClock.advanceBy(80);
@@ -948,19 +961,19 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled);
+ EXPECT_EQ(mDispatch->cancel(cb1), CancelResult::Cancelled);
EXPECT_THAT(cb1.mCalls.size(), Eq(0));
EXPECT_THAT(cb2.mCalls.size(), Eq(0));
@@ -975,21 +988,21 @@
CountingCallback cb2(mDispatch);
Sequence seq;
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
.InSequence(seq)
.WillOnce(Return(1000));
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
.InSequence(seq)
.WillOnce(Return(1000));
auto result =
- mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch.schedule(cb2,
- {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb2,
+ {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(610, *result);
@@ -1011,10 +1024,10 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- const auto result = mDispatch.schedule(cb,
- {.workDuration = 70,
- .readyDuration = 30,
- .earliestVsync = intended});
+ const auto result = mDispatch->schedule(cb,
+ {.workDuration = 70,
+ .readyDuration = 30,
+ .earliestVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
advanceToNextCallback();
@@ -1033,8 +1046,8 @@
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch.schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
@@ -1052,7 +1065,8 @@
protected:
nsecs_t const mPeriod = 1000;
nsecs_t const mVsyncMoveThreshold = 200;
- NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+ std::shared_ptr<NiceMock<MockVSyncTracker>> mStubTracker =
+ std::make_shared<NiceMock<MockVSyncTracker>>(mPeriod);
};
TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
@@ -1070,7 +1084,7 @@
EXPECT_FALSE(entry.wakeupTime());
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
@@ -1084,7 +1098,7 @@
auto const duration = 500;
auto const now = 8750;
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + duration))
.Times(1)
.WillOnce(Return(10000));
VSyncDispatchTimerQueueEntry entry(
@@ -1092,7 +1106,7 @@
EXPECT_FALSE(entry.wakeupTime());
EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
- mStubTracker, now)
+ *mStubTracker.get(), now)
.has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
@@ -1115,7 +1129,7 @@
mVsyncMoveThreshold);
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
@@ -1137,7 +1151,7 @@
}
TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
.Times(2)
.WillOnce(Return(1000))
.WillOnce(Return(1020));
@@ -1146,17 +1160,17 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- entry.update(mStubTracker, 0);
+ entry.update(*mStubTracker.get(), 0);
EXPECT_FALSE(entry.wakeupTime());
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
auto wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(wakeup, Eq(900));
- entry.update(mStubTracker, 0);
+ entry.update(*mStubTracker.get(), 0);
wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(920));
@@ -1166,9 +1180,9 @@
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
- entry.update(mStubTracker, 0);
+ entry.update(*mStubTracker.get(), 0);
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
@@ -1179,24 +1193,24 @@
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
@@ -1208,24 +1222,24 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
Sequence seq;
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500))
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500))
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
.InSequence(seq)
.WillOnce(Return(2000));
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
entry.executing(); // 1000 is executing
EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
}
@@ -1233,16 +1247,16 @@
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
}
@@ -1255,7 +1269,7 @@
entry.addPendingWorkloadUpdate(
{.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400});
EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
- entry.update(mStubTracker, 0);
+ entry.update(*mStubTracker.get(), 0);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
}
@@ -1276,7 +1290,7 @@
mVsyncMoveThreshold);
EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
- mStubTracker, 0)
+ *mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index db531bf..43d683d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -47,6 +47,8 @@
return vsyncs;
}
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+
struct VSyncPredictorTest : testing::Test {
nsecs_t mNow = 0;
nsecs_t mPeriod = 1000;
@@ -55,7 +57,7 @@
static constexpr size_t kOutlierTolerancePercent = 25;
static constexpr nsecs_t mMaxRoundingError = 100;
- VSyncPredictor tracker{mPeriod, kHistorySize, kMinimumSamplesForPrediction,
+ VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, kHistorySize, kMinimumSamplesForPrediction,
kOutlierTolerancePercent};
};
@@ -376,7 +378,8 @@
// See b/151146131
TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
- VSyncPredictor tracker{mPeriod, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, 20, kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent};
std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
840923581635, 840940161584, 840956868096,
840973702473, 840990256277, 841007116851,
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index fb8d989..122192b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -91,13 +91,15 @@
return ft;
}
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+
class VSyncReactorTest : public testing::Test {
protected:
VSyncReactorTest()
: mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
- mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit,
- false /* supportKernelIdleTimer */) {
+ mReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
+ kPendingLimit, false /* supportKernelIdleTimer */) {
ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
}
@@ -192,7 +194,7 @@
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -205,7 +207,7 @@
TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
nsecs_t const newPeriod = 5000;
EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
@@ -224,7 +226,7 @@
TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
nsecs_t sampleTime = 0;
nsecs_t const newPeriod = 5000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -232,7 +234,7 @@
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.startPeriodTransition(period);
+ mReactor.startPeriodTransition(period, false);
EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -242,13 +244,13 @@
nsecs_t const secondPeriod = 5000;
nsecs_t const thirdPeriod = 2000;
- mReactor.startPeriodTransition(secondPeriod);
+ mReactor.startPeriodTransition(secondPeriod, false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.startPeriodTransition(thirdPeriod);
+ mReactor.startPeriodTransition(thirdPeriod, false);
EXPECT_TRUE(
mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -289,14 +291,14 @@
TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
nsecs_t const newPeriod = 5000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
nsecs_t const newPeriod = 5000;
EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed));
@@ -321,7 +323,7 @@
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
auto time = 0;
auto constexpr numTimestampSubmissions = 10;
@@ -346,7 +348,7 @@
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
auto time = 0;
// If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps.
@@ -363,7 +365,7 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
time += period;
mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
@@ -379,7 +381,7 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
static auto constexpr numSamplesWithNewPeriod = 4;
Sequence seq;
@@ -406,7 +408,7 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
Sequence seq;
EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -426,7 +428,7 @@
nsecs_t const newPeriod1 = 4000;
nsecs_t const newPeriod2 = 7000;
- mReactor.startPeriodTransition(newPeriod1);
+ mReactor.startPeriodTransition(newPeriod1, false);
Sequence seq;
EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -445,7 +447,7 @@
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
- mReactor.startPeriodTransition(newPeriod2);
+ mReactor.startPeriodTransition(newPeriod2, false);
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
@@ -458,7 +460,7 @@
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
- mReactor.startPeriodTransition(newPeriod);
+ mReactor.startPeriodTransition(newPeriod, false);
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -472,8 +474,9 @@
TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
// Create a reactor which supports the kernel idle timer
- auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
- kPendingLimit, true /* supportKernelIdleTimer */);
+ auto idleReactor =
+ VSyncReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock),
+ *mMockTracker, kPendingLimit, true /* supportKernelIdleTimer */);
bool periodFlushed = true;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
@@ -481,7 +484,7 @@
// First, set the same period, which should only be confirmed when we receive two
// matching callbacks
- idleReactor.startPeriodTransition(10000);
+ idleReactor.startPeriodTransition(10000, false);
EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Correct period but incorrect timestamp delta
@@ -494,7 +497,7 @@
// Then, set a new period, which should be confirmed as soon as we receive a callback
// reporting the new period
nsecs_t const newPeriod = 5000;
- idleReactor.startPeriodTransition(newPeriod);
+ idleReactor.startPeriodTransition(newPeriod, false);
// Incorrect timestamp delta and period
EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed));
EXPECT_FALSE(periodFlushed);
diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
new file mode 100644
index 0000000..4010fa6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include <scheduler/Fps.h>
+#include "Scheduler/VsyncSchedule.h"
+#include "ThreadContext.h"
+#include "mock/MockSchedulerCallback.h"
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
+
+using testing::_;
+
+namespace android {
+
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+
+class VsyncScheduleTest : public testing::Test {
+protected:
+ VsyncScheduleTest();
+ ~VsyncScheduleTest() override;
+
+ scheduler::mock::SchedulerCallback mCallback;
+ const std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule =
+ std::unique_ptr<scheduler::VsyncSchedule>(
+ new scheduler::VsyncSchedule(DEFAULT_DISPLAY_ID,
+ std::make_shared<mock::VSyncTracker>(),
+ std::make_shared<mock::VSyncDispatch>(),
+ std::make_unique<mock::VsyncController>()));
+
+ mock::VsyncController& getController() {
+ return *static_cast<mock::VsyncController*>(&mVsyncSchedule->getController());
+ }
+};
+
+VsyncScheduleTest::VsyncScheduleTest() {
+ 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());
+}
+
+VsyncScheduleTest::~VsyncScheduleTest() {
+ 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());
+}
+
+namespace {
+
+using namespace testing;
+
+TEST_F(VsyncScheduleTest, InitiallyDisallowed) {
+ ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, EnableDoesNothingWhenDisallowed) {
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed) {
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed2) {
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+ mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, MakeAllowed) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled2) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+ mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, EnableWorksWhenDisabled) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, EnableWorksOnce) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, AllowedIsSticky) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, EnableDisable) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, EnableDisable2) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+ mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, StartPeriodTransition) {
+ // Note: startPeriodTransition is only called when hardware vsyncs are
+ // allowed.
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+
+ const Period period = (60_Hz).getPeriod();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+ EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false));
+
+ mVsyncSchedule->startPeriodTransition(mCallback, period, false);
+}
+
+TEST_F(VsyncScheduleTest, StartPeriodTransitionAlreadyEnabled) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ const Period period = (60_Hz).getPeriod();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+ EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false));
+
+ mVsyncSchedule->startPeriodTransition(mCallback, period, false);
+}
+
+TEST_F(VsyncScheduleTest, StartPeriodTransitionForce) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+
+ const Period period = (60_Hz).getPeriod();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+ EXPECT_CALL(getController(), startPeriodTransition(period.ns(), true));
+
+ mVsyncSchedule->startPeriodTransition(mCallback, period, true);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleDisallowed) {
+ const Period period = (60_Hz).getPeriod();
+ const auto timestamp = TimePoint::now();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+ EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0);
+
+ mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleDisabled) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ const Period period = (60_Hz).getPeriod();
+ const auto timestamp = TimePoint::now();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+ EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0);
+
+ mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleReturnsTrue) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ const Period period = (60_Hz).getPeriod();
+ const auto timestamp = TimePoint::now();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+ EXPECT_CALL(getController(),
+ addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _))
+ .WillOnce(Return(true));
+
+ mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleReturnsFalse) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ const Period period = (60_Hz).getPeriod();
+ const auto timestamp = TimePoint::now();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+ EXPECT_CALL(getController(),
+ addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _))
+ .WillOnce(Return(false));
+
+ mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, PendingState) FTL_FAKE_GUARD(kMainThreadContext) {
+ ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState());
+ mVsyncSchedule->setPendingHardwareVsyncState(true);
+ ASSERT_TRUE(mVsyncSchedule->getPendingHardwareVsyncState());
+
+ mVsyncSchedule->setPendingHardwareVsyncState(false);
+ ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState());
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNotMakeAllowed) {
+ ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+ ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, DisallowMakesNotAllowed) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+ ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, StillAllowedAfterDisable) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index f28b8d8..5dc3490 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -175,6 +175,7 @@
Error(aidl::android::hardware::graphics::composer3::OverlayProperties*));
MOCK_METHOD1(onHotplugConnect, void(Display));
MOCK_METHOD1(onHotplugDisconnect, void(Display));
+ MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index f8567bd..8d57049 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -29,27 +29,28 @@
EventThread();
~EventThread() override;
- MOCK_CONST_METHOD2(createEventConnection,
- sp<EventThreadConnection>(ResyncCallback, EventRegistrationFlags));
- MOCK_METHOD0(onScreenReleased, void());
- MOCK_METHOD0(onScreenAcquired, void());
- MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &));
- MOCK_METHOD2(onFrameRateOverridesChanged,
- void(PhysicalDisplayId, std::vector<FrameRateOverride>));
- MOCK_CONST_METHOD1(dump, void(std::string&));
- MOCK_METHOD2(setDuration,
- void(std::chrono::nanoseconds workDuration,
- std::chrono::nanoseconds readyDuration));
- MOCK_METHOD1(registerDisplayEventConnection,
- status_t(const sp<android::EventThreadConnection> &));
- MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
- MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
+ MOCK_METHOD(sp<EventThreadConnection>, createEventConnection,
+ (ResyncCallback, EventRegistrationFlags), (const, override));
+ MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
+ MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
+ MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override));
+ MOCK_METHOD(void, onFrameRateOverridesChanged,
+ (PhysicalDisplayId, std::vector<FrameRateOverride>), (override));
+ MOCK_METHOD(void, dump, (std::string&), (const, override));
+ MOCK_METHOD(void, setDuration,
+ (std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration),
+ (override));
+ MOCK_METHOD(status_t, registerDisplayEventConnection,
+ (const sp<android::EventThreadConnection>&), (override));
+ MOCK_METHOD(void, setVsyncRate, (uint32_t, const sp<android::EventThreadConnection>&),
+ (override));
+ MOCK_METHOD(void, requestNextVsync, (const sp<android::EventThreadConnection>&), (override));
MOCK_METHOD(VsyncEventData, getLatestVsyncEventData,
- (const sp<android::EventThreadConnection> &), (const));
- MOCK_METHOD1(requestLatestConfig, void(const sp<android::EventThreadConnection> &));
- MOCK_METHOD1(pauseVsyncCallback, void(bool));
- MOCK_METHOD0(getEventThreadConnectionCount, size_t());
+ (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));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 7d4b159..a8eca21 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -18,19 +18,19 @@
#include <gmock/gmock.h>
-#include "Scheduler/Scheduler.h"
+#include "Scheduler/ISchedulerCallback.h"
namespace android::scheduler::mock {
struct SchedulerCallback final : ISchedulerCallback {
- MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
+ MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
- void setVsyncEnabled(bool) override {}
+ void setVsyncEnabled(PhysicalDisplayId, bool) override {}
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 4ef91da..69ec60a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -28,12 +28,12 @@
~VsyncController() override;
MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override));
- MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
- MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
- MOCK_METHOD1(setIgnorePresentFences, void(bool));
+ MOCK_METHOD(bool, addHwVsyncTimestamp, (nsecs_t, std::optional<nsecs_t>, bool*), (override));
+ MOCK_METHOD(void, startPeriodTransition, (nsecs_t, bool), (override));
+ MOCK_METHOD(void, setIgnorePresentFences, (bool), (override));
MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override));
- MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_METHOD(void, dump, (std::string&), (const, override));
};
} // namespace android::mock