diff options
228 files changed, 7053 insertions, 3569 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 4dd4f7999d..6d837c2cea 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,11 +1,12 @@ [Builtin Hooks] -clang_format = true bpfmt = true +clang_format = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp cmds/idlcli/ + cmds/installd/ cmds/servicemanager/ include/input/ include/powermanager/ diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 9a8ec32608..bb1d206f99 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -239,7 +239,7 @@ static const TracingCategory k_categories[] = { } }, { "memory", "Memory", 0, { { OPT, "events/mm_event/mm_event_record/enable" }, - { OPT, "events/kmem/rss_stat/enable" }, + { OPT, "events/synthetic/rss_stat_throttled/enable" }, { OPT, "events/kmem/ion_heap_grow/enable" }, { OPT, "events/kmem/ion_heap_shrink/enable" }, { OPT, "events/ion/ion_stat/enable" }, diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 34ccb21c1e..8da1352cca 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -11,6 +11,286 @@ on late-init # Grant unix world read/write permissions to kernel tracepoints. # Access control to these files is now entirely in selinux policy. + chmod 0755 /sys/kernel/debug/tracing/events + chmod 0755 /sys/kernel/debug/tracing/events/binder + chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_lock + chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_locked + chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_set_priority + chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_transaction + chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_transaction_alloc_buf + chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_transaction_received + chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_unlock + chmod 0755 /sys/kernel/debug/tracing/events/block + chmod 0755 /sys/kernel/debug/tracing/events/block/block_rq_complete + chmod 0755 /sys/kernel/debug/tracing/events/block/block_rq_issue + chmod 0755 /sys/kernel/debug/tracing/events/cgroup + chmod 0755 /sys/kernel/debug/tracing/events/clk + chmod 0755 /sys/kernel/debug/tracing/events/clk/clk_disable + chmod 0755 /sys/kernel/debug/tracing/events/clk/clk_enable + chmod 0755 /sys/kernel/debug/tracing/events/clk/clk_set_rate + chmod 0755 /sys/kernel/debug/tracing/events/cpufreq_interactive + chmod 0755 /sys/kernel/debug/tracing/events/cpuhp + chmod 0755 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_enter + chmod 0755 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_exit + chmod 0755 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_pause + chmod 0755 /sys/kernel/debug/tracing/events/dma_fence + chmod 0755 /sys/kernel/debug/tracing/events/ext4 + chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin + chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end + chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_es_lookup_extent_enter + chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_es_lookup_extent_exit + chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_load_inode + chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter + chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit + chmod 0755 /sys/kernel/debug/tracing/events/f2fs + chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_get_data_block + chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_iget + chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter + chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit + chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin + chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end + chmod 0755 /sys/kernel/debug/tracing/events/fence + chmod 0755 /sys/kernel/debug/tracing/events/filemap + chmod 0755 /sys/kernel/debug/tracing/events/filemap/mm_filemap_add_to_page_cache + chmod 0755 /sys/kernel/debug/tracing/events/filemap/mm_filemap_delete_from_page_cache + chmod 0755 /sys/kernel/debug/tracing/events/gpu_mem + chmod 0755 /sys/kernel/debug/tracing/events/gpu_mem/gpu_mem_total + chmod 0755 /sys/kernel/debug/tracing/events/i2c + chmod 0755 /sys/kernel/debug/tracing/events/i2c/i2c_read + chmod 0755 /sys/kernel/debug/tracing/events/i2c/i2c_reply + chmod 0755 /sys/kernel/debug/tracing/events/i2c/i2c_result + chmod 0755 /sys/kernel/debug/tracing/events/i2c/i2c_write + chmod 0755 /sys/kernel/debug/tracing/events/i2c/smbus_read + chmod 0755 /sys/kernel/debug/tracing/events/i2c/smbus_reply + chmod 0755 /sys/kernel/debug/tracing/events/i2c/smbus_result + chmod 0755 /sys/kernel/debug/tracing/events/i2c/smbus_write + chmod 0755 /sys/kernel/debug/tracing/events/ion + chmod 0755 /sys/kernel/debug/tracing/events/ion/ion_stat + chmod 0755 /sys/kernel/debug/tracing/events/ipi + chmod 0755 /sys/kernel/debug/tracing/events/ipi/ipi_entry + chmod 0755 /sys/kernel/debug/tracing/events/ipi/ipi_exit + chmod 0755 /sys/kernel/debug/tracing/events/ipi/ipi_raise + chmod 0755 /sys/kernel/debug/tracing/events/irq + chmod 0755 /sys/kernel/debug/tracing/events/irq/irq_handler_entry + chmod 0755 /sys/kernel/debug/tracing/events/irq/irq_handler_exit + chmod 0755 /sys/kernel/debug/tracing/events/irq/softirq_entry + chmod 0755 /sys/kernel/debug/tracing/events/irq/softirq_exit + chmod 0755 /sys/kernel/debug/tracing/events/irq/softirq_raise + chmod 0755 /sys/kernel/debug/tracing/events/irq/tasklet_entry + chmod 0755 /sys/kernel/debug/tracing/events/irq/tasklet_exit + chmod 0755 /sys/kernel/debug/tracing/events/irq/tasklet_hi_entry + chmod 0755 /sys/kernel/debug/tracing/events/irq/tasklet_hi_exit + chmod 0755 /sys/kernel/debug/tracing/events/kmem + chmod 0755 /sys/kernel/debug/tracing/events/kmem/ion_heap_grow + chmod 0755 /sys/kernel/debug/tracing/events/kmem/ion_heap_shrink + chmod 0755 /sys/kernel/debug/tracing/events/kmem/rss_stat + chmod 0755 /sys/kernel/debug/tracing/events/lowmemorykiller + chmod 0755 /sys/kernel/debug/tracing/events/lowmemorykiller/lowmemory_kill + chmod 0755 /sys/kernel/debug/tracing/events/mm_event + chmod 0755 /sys/kernel/debug/tracing/events/mm_event/mm_event_record + chmod 0755 /sys/kernel/debug/tracing/events/oom + chmod 0755 /sys/kernel/debug/tracing/events/oom/mark_victim + chmod 0755 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update + chmod 0755 /sys/kernel/debug/tracing/events/power + chmod 0755 /sys/kernel/debug/tracing/events/power/clock_disable + chmod 0755 /sys/kernel/debug/tracing/events/power/clock_enable + chmod 0755 /sys/kernel/debug/tracing/events/power/clock_set_rate + chmod 0755 /sys/kernel/debug/tracing/events/power/cpu_frequency + chmod 0755 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits + chmod 0755 /sys/kernel/debug/tracing/events/power/cpu_idle + chmod 0755 /sys/kernel/debug/tracing/events/power/gpu_frequency + chmod 0755 /sys/kernel/debug/tracing/events/power/suspend_resume + chmod 0755 /sys/kernel/debug/tracing/events/sched + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_pi_setprio + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_process_exit + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_process_free + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_switch + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_wakeup + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_wakeup_new + chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_waking + chmod 0755 /sys/kernel/debug/tracing/events/signal + chmod 0755 /sys/kernel/debug/tracing/events/signal/signal_deliver + chmod 0755 /sys/kernel/debug/tracing/events/signal/signal_generate + chmod 0755 /sys/kernel/debug/tracing/events/sync + chmod 0755 /sys/kernel/debug/tracing/events/task + chmod 0755 /sys/kernel/debug/tracing/events/task/task_newtask + chmod 0755 /sys/kernel/debug/tracing/events/task/task_rename + chmod 0755 /sys/kernel/debug/tracing/events/thermal + chmod 0755 /sys/kernel/debug/tracing/events/thermal/cdev_update + chmod 0755 /sys/kernel/debug/tracing/events/thermal/thermal_temperature + chmod 0755 /sys/kernel/debug/tracing/events/vmscan + chmod 0755 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin + chmod 0755 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end + chmod 0755 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep + chmod 0755 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake + chmod 0755 /sys/kernel/debug/tracing/options + chmod 0755 /sys/kernel/debug/tracing/per_cpu + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu0 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu1 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu2 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu3 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu4 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu5 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu6 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu7 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu8 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu9 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu10 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu11 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu12 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu13 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu14 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu15 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu16 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu17 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu18 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu19 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu20 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu21 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu22 + chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu23 + chmod 0755 /sys/kernel/tracing/events + chmod 0755 /sys/kernel/tracing/events/binder + chmod 0755 /sys/kernel/tracing/events/binder/binder_lock + chmod 0755 /sys/kernel/tracing/events/binder/binder_locked + chmod 0755 /sys/kernel/tracing/events/binder/binder_set_priority + chmod 0755 /sys/kernel/tracing/events/binder/binder_transaction + chmod 0755 /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf + chmod 0755 /sys/kernel/tracing/events/binder/binder_transaction_received + chmod 0755 /sys/kernel/tracing/events/binder/binder_unlock + chmod 0755 /sys/kernel/tracing/events/block + chmod 0755 /sys/kernel/tracing/events/block/block_rq_complete + chmod 0755 /sys/kernel/tracing/events/block/block_rq_issue + chmod 0755 /sys/kernel/tracing/events/cgroup + chmod 0755 /sys/kernel/tracing/events/clk + chmod 0755 /sys/kernel/tracing/events/clk/clk_disable + chmod 0755 /sys/kernel/tracing/events/clk/clk_enable + chmod 0755 /sys/kernel/tracing/events/clk/clk_set_rate + chmod 0755 /sys/kernel/tracing/events/cpufreq_interactive + chmod 0755 /sys/kernel/tracing/events/cpuhp + chmod 0755 /sys/kernel/tracing/events/cpuhp/cpuhp_enter + chmod 0755 /sys/kernel/tracing/events/cpuhp/cpuhp_exit + chmod 0755 /sys/kernel/tracing/events/cpuhp/cpuhp_pause + chmod 0755 /sys/kernel/tracing/events/dma_fence + chmod 0755 /sys/kernel/tracing/events/ext4 + chmod 0755 /sys/kernel/tracing/events/ext4/ext4_da_write_begin + chmod 0755 /sys/kernel/tracing/events/ext4/ext4_da_write_end + chmod 0755 /sys/kernel/tracing/events/ext4/ext4_es_lookup_extent_enter + chmod 0755 /sys/kernel/tracing/events/ext4/ext4_es_lookup_extent_exit + chmod 0755 /sys/kernel/tracing/events/ext4/ext4_load_inode + chmod 0755 /sys/kernel/tracing/events/ext4/ext4_sync_file_enter + chmod 0755 /sys/kernel/tracing/events/ext4/ext4_sync_file_exit + chmod 0755 /sys/kernel/tracing/events/f2fs + chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block + chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_iget + chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_enter + chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_exit + chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_write_begin + chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_write_end + chmod 0755 /sys/kernel/tracing/events/fence + chmod 0755 /sys/kernel/tracing/events/filemap + chmod 0755 /sys/kernel/tracing/events/filemap/mm_filemap_add_to_page_cache + chmod 0755 /sys/kernel/tracing/events/filemap/mm_filemap_delete_from_page_cache + chmod 0755 /sys/kernel/tracing/events/gpu_mem + chmod 0755 /sys/kernel/tracing/events/gpu_mem/gpu_mem_total + chmod 0755 /sys/kernel/tracing/events/i2c + chmod 0755 /sys/kernel/tracing/events/i2c/i2c_read + chmod 0755 /sys/kernel/tracing/events/i2c/i2c_reply + chmod 0755 /sys/kernel/tracing/events/i2c/i2c_result + chmod 0755 /sys/kernel/tracing/events/i2c/i2c_write + chmod 0755 /sys/kernel/tracing/events/i2c/smbus_read + chmod 0755 /sys/kernel/tracing/events/i2c/smbus_reply + chmod 0755 /sys/kernel/tracing/events/i2c/smbus_result + chmod 0755 /sys/kernel/tracing/events/i2c/smbus_write + chmod 0755 /sys/kernel/tracing/events/ion + chmod 0755 /sys/kernel/tracing/events/ion/ion_stat + chmod 0755 /sys/kernel/tracing/events/ipi + chmod 0755 /sys/kernel/tracing/events/ipi/ipi_entry + chmod 0755 /sys/kernel/tracing/events/ipi/ipi_exit + chmod 0755 /sys/kernel/tracing/events/ipi/ipi_raise + chmod 0755 /sys/kernel/tracing/events/irq + chmod 0755 /sys/kernel/tracing/events/irq/irq_handler_entry + chmod 0755 /sys/kernel/tracing/events/irq/irq_handler_exit + chmod 0755 /sys/kernel/tracing/events/irq/softirq_entry + chmod 0755 /sys/kernel/tracing/events/irq/softirq_exit + chmod 0755 /sys/kernel/tracing/events/irq/softirq_raise + chmod 0755 /sys/kernel/tracing/events/irq/tasklet_entry + chmod 0755 /sys/kernel/tracing/events/irq/tasklet_exit + chmod 0755 /sys/kernel/tracing/events/irq/tasklet_hi_entry + chmod 0755 /sys/kernel/tracing/events/irq/tasklet_hi_exit + chmod 0755 /sys/kernel/tracing/events/kmem + chmod 0755 /sys/kernel/tracing/events/kmem/ion_heap_grow + chmod 0755 /sys/kernel/tracing/events/kmem/ion_heap_shrink + chmod 0755 /sys/kernel/tracing/events/kmem/rss_stat + chmod 0755 /sys/kernel/tracing/events/lowmemorykiller + chmod 0755 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill + chmod 0755 /sys/kernel/tracing/events/mm_event + chmod 0755 /sys/kernel/tracing/events/mm_event/mm_event_record + chmod 0755 /sys/kernel/tracing/events/oom + chmod 0755 /sys/kernel/tracing/events/oom/mark_victim + chmod 0755 /sys/kernel/tracing/events/oom/oom_score_adj_update + chmod 0755 /sys/kernel/tracing/events/power + chmod 0755 /sys/kernel/tracing/events/power/clock_disable + chmod 0755 /sys/kernel/tracing/events/power/clock_enable + chmod 0755 /sys/kernel/tracing/events/power/clock_set_rate + chmod 0755 /sys/kernel/tracing/events/power/cpu_frequency + chmod 0755 /sys/kernel/tracing/events/power/cpu_frequency_limits + chmod 0755 /sys/kernel/tracing/events/power/cpu_idle + chmod 0755 /sys/kernel/tracing/events/power/gpu_frequency + chmod 0755 /sys/kernel/tracing/events/power/suspend_resume + chmod 0755 /sys/kernel/tracing/events/sched + chmod 0755 /sys/kernel/tracing/events/sched/sched_blocked_reason + chmod 0755 /sys/kernel/tracing/events/sched/sched_cpu_hotplug + chmod 0755 /sys/kernel/tracing/events/sched/sched_pi_setprio + chmod 0755 /sys/kernel/tracing/events/sched/sched_process_exit + chmod 0755 /sys/kernel/tracing/events/sched/sched_process_free + chmod 0755 /sys/kernel/tracing/events/sched/sched_switch + chmod 0755 /sys/kernel/tracing/events/sched/sched_wakeup + chmod 0755 /sys/kernel/tracing/events/sched/sched_wakeup_new + chmod 0755 /sys/kernel/tracing/events/sched/sched_waking + chmod 0755 /sys/kernel/tracing/events/signal + chmod 0755 /sys/kernel/tracing/events/signal/signal_deliver + chmod 0755 /sys/kernel/tracing/events/signal/signal_generate + chmod 0755 /sys/kernel/tracing/events/sync + chmod 0755 /sys/kernel/tracing/events/task + chmod 0755 /sys/kernel/tracing/events/task/task_newtask + chmod 0755 /sys/kernel/tracing/events/task/task_rename + chmod 0755 /sys/kernel/tracing/events/thermal + chmod 0755 /sys/kernel/tracing/events/thermal/cdev_update + chmod 0755 /sys/kernel/tracing/events/thermal/thermal_temperature + chmod 0755 /sys/kernel/tracing/events/vmscan + chmod 0755 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin + chmod 0755 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end + chmod 0755 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep + chmod 0755 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake + chmod 0755 /sys/kernel/tracing/options + chmod 0755 /sys/kernel/tracing/per_cpu + chmod 0755 /sys/kernel/tracing/per_cpu/cpu0 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu1 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu2 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu3 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu4 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu5 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu6 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu7 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu8 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu9 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu10 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu11 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu12 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu13 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu14 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu15 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu16 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu17 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu18 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu19 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu20 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu21 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu22 + chmod 0755 /sys/kernel/tracing/per_cpu/cpu23 chmod 0666 /sys/kernel/debug/tracing/trace_clock chmod 0666 /sys/kernel/tracing/trace_clock chmod 0666 /sys/kernel/debug/tracing/buffer_size_kb diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 74dbf4b764..a2491e503f 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -86,9 +86,11 @@ cc_defaults { shared_libs: [ "android.hardware.dumpstate@1.0", "android.hardware.dumpstate@1.1", + "android.hardware.dumpstate-V1-ndk", "libziparchive", "libbase", "libbinder", + "libbinder_ndk", "libcrypto", "libcutils", "libdebuggerd_client", diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index ba25a5a603..77915d5376 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -192,7 +192,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "progress:\n"); ds_->progress_->Dump(fd, " "); dprintf(fd, "args: %s\n", ds_->options_->args.c_str()); - dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode.c_str()); + dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode_string.c_str()); dprintf(fd, "version: %s\n", ds_->version_.c_str()); dprintf(fd, "bugreport_dir: %s\n", destination.c_str()); dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str()); diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index eab72f48b0..32e680dfea 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -57,12 +57,15 @@ #include <utility> #include <vector> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> #include <android/content/pm/IPackageManagerNative.h> #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h> #include <android/hardware/dumpstate/1.1/IDumpstateDevice.h> @@ -89,11 +92,10 @@ #include "DumpstateService.h" #include "dumpstate.h" -using IDumpstateDevice_1_0 = ::android::hardware::dumpstate::V1_0::IDumpstateDevice; -using IDumpstateDevice_1_1 = ::android::hardware::dumpstate::V1_1::IDumpstateDevice; -using ::android::hardware::dumpstate::V1_1::DumpstateMode; -using ::android::hardware::dumpstate::V1_1::DumpstateStatus; -using ::android::hardware::dumpstate::V1_1::toString; +namespace dumpstate_hal_hidl_1_0 = android::hardware::dumpstate::V1_0; +namespace dumpstate_hal_hidl = android::hardware::dumpstate::V1_1; +namespace dumpstate_hal_aidl = aidl::android::hardware::dumpstate; + using ::std::literals::chrono_literals::operator""ms; using ::std::literals::chrono_literals::operator""s; using ::std::placeholders::_1; @@ -807,7 +809,7 @@ void Dumpstate::PrintHeader() const { printf("Bugreport format version: %s\n", version_.c_str()); printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n", id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(), - options_->args.c_str(), options_->bugreport_mode.c_str()); + options_->args.c_str(), options_->bugreport_mode_string.c_str()); printf("\n"); } @@ -2199,6 +2201,194 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { return RunStatus::OK; } +static dumpstate_hal_hidl::DumpstateMode GetDumpstateHalModeHidl( + const Dumpstate::BugreportMode bugreport_mode) { + switch (bugreport_mode) { + case Dumpstate::BugreportMode::BUGREPORT_FULL: + return dumpstate_hal_hidl::DumpstateMode::FULL; + case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: + return dumpstate_hal_hidl::DumpstateMode::INTERACTIVE; + case Dumpstate::BugreportMode::BUGREPORT_REMOTE: + return dumpstate_hal_hidl::DumpstateMode::REMOTE; + case Dumpstate::BugreportMode::BUGREPORT_WEAR: + return dumpstate_hal_hidl::DumpstateMode::WEAR; + case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: + return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY; + case Dumpstate::BugreportMode::BUGREPORT_WIFI: + return dumpstate_hal_hidl::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: + return dumpstate_hal_hidl::DumpstateMode::DEFAULT; + } + return dumpstate_hal_hidl::DumpstateMode::DEFAULT; +} + +static dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode GetDumpstateHalModeAidl( + const Dumpstate::BugreportMode bugreport_mode) { + switch (bugreport_mode) { + case Dumpstate::BugreportMode::BUGREPORT_FULL: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::FULL; + case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::INTERACTIVE; + case Dumpstate::BugreportMode::BUGREPORT_REMOTE: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::REMOTE; + case Dumpstate::BugreportMode::BUGREPORT_WEAR: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WEAR; + case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY; + case Dumpstate::BugreportMode::BUGREPORT_WIFI: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT; + } + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT; +} + +static void DoDumpstateBoardHidl( + const sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_1_0, + const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds, + const Dumpstate::BugreportMode bugreport_mode, + const size_t timeout_sec) { + + using ScopedNativeHandle = + std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>; + ScopedNativeHandle handle(native_handle_create(static_cast<int>(dumpstate_fds.size()), 0), + [](native_handle_t* handle) { + // we don't close file handle's here + // via native_handle_close(handle) + // instead we let dumpstate_fds close the file handles when + // dumpstate_fds gets destroyed + native_handle_delete(handle); + }); + if (handle == nullptr) { + MYLOGE("Could not create native_handle for dumpstate HAL\n"); + return; + } + + for (size_t i = 0; i < dumpstate_fds.size(); i++) { + handle.get()->data[i] = dumpstate_fds[i].get(); + } + + // Prefer version 1.1 if available. New devices launching with R are no longer allowed to + // implement just 1.0. + const char* descriptor_to_kill; + using DumpstateBoardTask = std::packaged_task<bool()>; + DumpstateBoardTask dumpstate_board_task; + sp<dumpstate_hal_hidl::IDumpstateDevice> dumpstate_hal( + dumpstate_hal_hidl::IDumpstateDevice::castFrom(dumpstate_hal_1_0)); + if (dumpstate_hal != nullptr) { + MYLOGI("Using IDumpstateDevice v1.1 HIDL HAL"); + + dumpstate_hal_hidl::DumpstateMode dumpstate_hal_mode = + GetDumpstateHalModeHidl(bugreport_mode); + + descriptor_to_kill = dumpstate_hal_hidl::IDumpstateDevice::descriptor; + dumpstate_board_task = + DumpstateBoardTask([timeout_sec, dumpstate_hal_mode, dumpstate_hal, &handle]() -> bool { + ::android::hardware::Return<dumpstate_hal_hidl::DumpstateStatus> status = + dumpstate_hal->dumpstateBoard_1_1(handle.get(), dumpstate_hal_mode, + SEC_TO_MSEC(timeout_sec)); + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); + return false; + } else if (status != dumpstate_hal_hidl::DumpstateStatus::OK) { + MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", + dumpstate_hal_hidl::toString(status).c_str()); + return false; + } + return true; + }); + } else { + MYLOGI("Using IDumpstateDevice v1.0 HIDL HAL"); + + descriptor_to_kill = dumpstate_hal_hidl_1_0::IDumpstateDevice::descriptor; + dumpstate_board_task = DumpstateBoardTask([dumpstate_hal_1_0, &handle]() -> bool { + ::android::hardware::Return<void> status = + dumpstate_hal_1_0->dumpstateBoard(handle.get()); + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); + return false; + } + return true; + }); + } + auto result = dumpstate_board_task.get_future(); + std::thread(std::move(dumpstate_board_task)).detach(); + + if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { + MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec); + if (!android::base::SetProperty( + "ctl.interface_restart", + android::base::StringPrintf("%s/default", descriptor_to_kill))) { + MYLOGE("Couldn't restart dumpstate HAL\n"); + } + } + // Wait some time for init to kill dumpstate vendor HAL + constexpr size_t killing_timeout_sec = 10; + if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { + MYLOGE( + "killing dumpstateBoard timed out after %zus, continue and " + "there might be racing in content\n", + killing_timeout_sec); + } +} + +static void DoDumpstateBoardAidl( + const std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal, + const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds, + const Dumpstate::BugreportMode bugreport_mode, const size_t timeout_sec) { + MYLOGI("Using IDumpstateDevice AIDL HAL"); + + const char* descriptor_to_kill; + using DumpstateBoardTask = std::packaged_task<bool()>; + DumpstateBoardTask dumpstate_board_task; + dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode dumpstate_hal_mode = + GetDumpstateHalModeAidl(bugreport_mode); + + descriptor_to_kill = dumpstate_hal_aidl::IDumpstateDevice::descriptor; + dumpstate_board_task = DumpstateBoardTask([dumpstate_hal, &dumpstate_fds, dumpstate_hal_mode, + timeout_sec]() -> bool { + auto status = dumpstate_hal->dumpstateBoard(dumpstate_fds, dumpstate_hal_mode, timeout_sec); + + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.getDescription().c_str()); + return false; + } + return true; + }); + auto result = dumpstate_board_task.get_future(); + std::thread(std::move(dumpstate_board_task)).detach(); + + if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { + MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec); + if (!android::base::SetProperty( + "ctl.interface_restart", + android::base::StringPrintf("%s/default", descriptor_to_kill))) { + MYLOGE("Couldn't restart dumpstate HAL\n"); + } + } + // Wait some time for init to kill dumpstate vendor HAL + constexpr size_t killing_timeout_sec = 10; + if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { + MYLOGE( + "killing dumpstateBoard timed out after %zus, continue and " + "there might be racing in content\n", + killing_timeout_sec); + } +} + +static std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> GetDumpstateBoardAidlService() { + const std::string aidl_instance_name = + std::string(dumpstate_hal_aidl::IDumpstateDevice::descriptor) + "/default"; + + if (!AServiceManager_isDeclared(aidl_instance_name.c_str())) { + return nullptr; + } + + ndk::SpAIBinder dumpstateBinder(AServiceManager_waitForService(aidl_instance_name.c_str())); + + return dumpstate_hal_aidl::IDumpstateDevice::fromBinder(dumpstateBinder); +} + void Dumpstate::DumpstateBoard(int out_fd) { dprintf(out_fd, "========================================================\n"); dprintf(out_fd, "== Board\n"); @@ -2220,8 +2410,7 @@ void Dumpstate::DumpstateBoard(int out_fd) { if (mount_debugfs) { RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"}, AS_ROOT_20); - RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, - AS_ROOT_20); + RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, AS_ROOT_20); } std::vector<std::string> paths; @@ -2233,24 +2422,32 @@ void Dumpstate::DumpstateBoard(int out_fd) { std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i]))); } - sp<IDumpstateDevice_1_0> dumpstate_device_1_0(IDumpstateDevice_1_0::getService()); - if (dumpstate_device_1_0 == nullptr) { - MYLOGE("No IDumpstateDevice implementation\n"); - return; + // get dumpstate HAL AIDL implementation + std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal_handle_aidl( + GetDumpstateBoardAidlService()); + if (dumpstate_hal_handle_aidl == nullptr) { + MYLOGI("No IDumpstateDevice AIDL implementation\n"); } - using ScopedNativeHandle = - std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>; - ScopedNativeHandle handle(native_handle_create(static_cast<int>(paths.size()), 0), - [](native_handle_t* handle) { - native_handle_close(handle); - native_handle_delete(handle); - }); - if (handle == nullptr) { - MYLOGE("Could not create native_handle\n"); + // get dumpstate HAL HIDL implementation, only if AIDL HAL implementation not found + sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_handle_hidl_1_0 = nullptr; + if (dumpstate_hal_handle_aidl == nullptr) { + dumpstate_hal_handle_hidl_1_0 = dumpstate_hal_hidl_1_0::IDumpstateDevice::getService(); + if (dumpstate_hal_handle_hidl_1_0 == nullptr) { + MYLOGI("No IDumpstateDevice HIDL implementation\n"); + } + } + + // if neither HIDL nor AIDL implementation found, then return + if (dumpstate_hal_handle_hidl_1_0 == nullptr && dumpstate_hal_handle_aidl == nullptr) { + MYLOGE("Could not find IDumpstateDevice implementation\n"); return; } + // this is used to hold the file descriptors and when this variable goes out of scope + // the file descriptors are closed + std::vector<::ndk::ScopedFileDescriptor> dumpstate_fds; + // TODO(128270426): Check for consent in between? for (size_t i = 0; i < paths.size(); i++) { MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str()); @@ -2262,65 +2459,26 @@ void Dumpstate::DumpstateBoard(int out_fd) { MYLOGE("Could not open file %s: %s\n", paths[i].c_str(), strerror(errno)); return; } - handle.get()->data[i] = fd.release(); + + dumpstate_fds.emplace_back(fd.release()); + // we call fd.release() here to make sure "fd" does not get closed + // after "fd" goes out of scope after this block. + // "fd" will be closed when "dumpstate_fds" goes out of scope + // i.e. when we exit this function } // Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount // of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we // will kill the HAL and grab whatever it dumped in time. constexpr size_t timeout_sec = 30; - // Prefer version 1.1 if available. New devices launching with R are no longer allowed to - // implement just 1.0. - const char* descriptor_to_kill; - using DumpstateBoardTask = std::packaged_task<bool()>; - DumpstateBoardTask dumpstate_board_task; - sp<IDumpstateDevice_1_1> dumpstate_device_1_1( - IDumpstateDevice_1_1::castFrom(dumpstate_device_1_0)); - if (dumpstate_device_1_1 != nullptr) { - MYLOGI("Using IDumpstateDevice v1.1"); - descriptor_to_kill = IDumpstateDevice_1_1::descriptor; - dumpstate_board_task = DumpstateBoardTask([this, dumpstate_device_1_1, &handle]() -> bool { - ::android::hardware::Return<DumpstateStatus> status = - dumpstate_device_1_1->dumpstateBoard_1_1(handle.get(), options_->dumpstate_hal_mode, - SEC_TO_MSEC(timeout_sec)); - if (!status.isOk()) { - MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); - return false; - } else if (status != DumpstateStatus::OK) { - MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", toString(status).c_str()); - return false; - } - return true; - }); - } else { - MYLOGI("Using IDumpstateDevice v1.0"); - descriptor_to_kill = IDumpstateDevice_1_0::descriptor; - dumpstate_board_task = DumpstateBoardTask([dumpstate_device_1_0, &handle]() -> bool { - ::android::hardware::Return<void> status = - dumpstate_device_1_0->dumpstateBoard(handle.get()); - if (!status.isOk()) { - MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); - return false; - } - return true; - }); - } - auto result = dumpstate_board_task.get_future(); - std::thread(std::move(dumpstate_board_task)).detach(); - if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { - MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec); - if (!android::base::SetProperty( - "ctl.interface_restart", - android::base::StringPrintf("%s/default", descriptor_to_kill))) { - MYLOGE("Couldn't restart dumpstate HAL\n"); - } - } - // Wait some time for init to kill dumpstate vendor HAL - constexpr size_t killing_timeout_sec = 10; - if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { - MYLOGE("killing dumpstateBoard timed out after %zus, continue and " - "there might be racing in content\n", killing_timeout_sec); + if (dumpstate_hal_handle_aidl != nullptr) { + DoDumpstateBoardAidl(dumpstate_hal_handle_aidl, dumpstate_fds, options_->bugreport_mode, + timeout_sec); + } else if (dumpstate_hal_handle_hidl_1_0 != nullptr) { + // run HIDL HAL only if AIDL HAL not found + DoDumpstateBoardHidl(dumpstate_hal_handle_hidl_1_0, dumpstate_fds, options_->bugreport_mode, + timeout_sec); } if (mount_debugfs) { @@ -2333,9 +2491,8 @@ void Dumpstate::DumpstateBoard(int out_fd) { auto file_sizes = std::make_unique<ssize_t[]>(paths.size()); for (size_t i = 0; i < paths.size(); i++) { struct stat s; - if (fstat(handle.get()->data[i], &s) == -1) { - MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), - strerror(errno)); + if (fstat(dumpstate_fds[i].get(), &s) == -1) { + MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), strerror(errno)); file_sizes[i] = -1; continue; } @@ -2574,40 +2731,35 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt bool is_screenshot_requested) { // Modify com.android.shell.BugreportProgressService#isDefaultScreenshotRequired as well for // default system screenshots. - options->bugreport_mode = ModeToString(mode); + options->bugreport_mode = mode; + options->bugreport_mode_string = ModeToString(mode); switch (mode) { case Dumpstate::BugreportMode::BUGREPORT_FULL: options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::FULL; break; case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: // Currently, the dumpstate binder is only used by Shell to update progress. options->do_progress_updates = true; options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE; break; case Dumpstate::BugreportMode::BUGREPORT_REMOTE: options->do_vibrate = false; options->is_remote_mode = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::REMOTE; break; case Dumpstate::BugreportMode::BUGREPORT_WEAR: options->do_progress_updates = true; options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::WEAR; break; // TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY. case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: options->telephony_only = true; options->do_progress_updates = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY; break; case Dumpstate::BugreportMode::BUGREPORT_WIFI: options->wifi_only = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::WIFI; break; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: break; @@ -2618,13 +2770,14 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI( "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d " "is_remote_mode: %d show_header_only: %d telephony_only: %d " - "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s " + "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s " "limited_only: %d args: %s\n", options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket, options.do_screenshot, options.is_remote_mode, options.show_header_only, options.telephony_only, options.wifi_only, - options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(), - toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str()); + options.do_progress_updates, options.bugreport_fd.get(), + options.bugreport_mode_string.c_str(), + options.limited_only, options.args.c_str()); } void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, @@ -2838,7 +2991,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n", - id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str()); + id_, options_->args.c_str(), options_->bugreport_mode_string.c_str(), version_.c_str()); do_early_screenshot_ = options_->do_progress_updates; diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 3722383e9e..773e292b63 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -25,6 +25,7 @@ #include <string> #include <vector> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/macros.h> #include <android-base/unique_fd.h> #include <android/hardware/dumpstate/1.1/types.h> @@ -400,19 +401,18 @@ class Dumpstate { bool limited_only = false; // Whether progress updates should be published. bool do_progress_updates = false; - // The mode we'll use when calling IDumpstateDevice::dumpstateBoard. + // this is used to derive dumpstate HAL bug report mode // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead. // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide). - ::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode = - ::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT; + BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT; // File descriptor to output zip file. Takes precedence over out_dir. android::base::unique_fd bugreport_fd; // File descriptor to screenshot file. android::base::unique_fd screenshot_fd; // Custom output directory. std::string out_dir; - // Bugreport mode of the bugreport. - std::string bugreport_mode; + // Bugreport mode of the bugreport as a string + std::string bugreport_mode_string; // Command-line arguments as string std::string args; // Notification title and description diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index db508b52bd..42beb2b6cf 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -33,6 +33,7 @@ #include <unistd.h> #include <thread> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -47,6 +48,7 @@ namespace android { namespace os { namespace dumpstate { +using DumpstateDeviceAidl = ::aidl::android::hardware::dumpstate::IDumpstateDevice; using ::android::hardware::dumpstate::V1_1::DumpstateMode; using ::testing::EndsWith; using ::testing::Eq; @@ -186,7 +188,6 @@ TEST_F(DumpOptionsTest, InitializeNone) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeAdbBugreport) { @@ -210,7 +211,6 @@ TEST_F(DumpOptionsTest, InitializeAdbBugreport) { EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.stream_to_socket); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { @@ -234,13 +234,11 @@ TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeFullBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -256,7 +254,6 @@ TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true); EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -272,7 +269,6 @@ TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { EXPECT_TRUE(options_.is_remote_mode); EXPECT_FALSE(options_.do_vibrate); EXPECT_FALSE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE); // Other options retain default values EXPECT_FALSE(options_.progress_updates_to_socket); @@ -286,7 +282,7 @@ TEST_F(DumpOptionsTest, InitializeWearBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true); EXPECT_TRUE(options_.do_screenshot); EXPECT_TRUE(options_.do_progress_updates); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR); + // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -302,7 +298,6 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { EXPECT_FALSE(options_.do_screenshot); EXPECT_TRUE(options_.telephony_only); EXPECT_TRUE(options_.do_progress_updates); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::CONNECTIVITY); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -317,7 +312,6 @@ TEST_F(DumpOptionsTest, InitializeWifiBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false); EXPECT_FALSE(options_.do_screenshot); EXPECT_TRUE(options_.wifi_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -354,7 +348,6 @@ TEST_F(DumpOptionsTest, InitializeLimitedOnlyBugreport) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.stream_to_socket); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { @@ -371,7 +364,6 @@ TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -408,7 +400,6 @@ TEST_F(DumpOptionsTest, InitializePartial1) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializePartial2) { @@ -436,7 +427,6 @@ TEST_F(DumpOptionsTest, InitializePartial2) { EXPECT_FALSE(options_.stream_to_socket); EXPECT_FALSE(options_.progress_updates_to_socket); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeHelp) { diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 3f180d94a7..faa84854ba 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -45,6 +45,7 @@ cc_defaults { "libprocessgroup", "libselinux", "libutils", + "libziparchive", "server_configurable_flags", ], static_libs: [ @@ -267,6 +268,7 @@ cc_binary { "libprocessgroup", "libselinux", "libutils", + "libziparchive", "server_configurable_flags", ], } diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp index e29ff4c248..27690a3039 100644 --- a/cmds/installd/CacheItem.cpp +++ b/cmds/installd/CacheItem.cpp @@ -116,6 +116,7 @@ int CacheItem::purge() { break; } } + fts_close(fts); } else { if (tombstone) { if (truncate(path.c_str(), 0) != 0) { diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 8d23efced8..2935c6acb6 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -77,6 +77,8 @@ #define LOG_TAG "installd" #endif +#define GRANULAR_LOCKS + using android::base::ParseUint; using android::base::StringPrintf; using std::endl; @@ -265,6 +267,104 @@ binder::Status checkArgumentPath(const std::optional<std::string>& path) { } \ } +#ifdef GRANULAR_LOCKS + +/** + * This class obtains in constructor and keeps the local strong pointer to the RefLock. + * On destruction, it checks if there are any other strong pointers, and remove the map entry if + * this was the last one. + */ +template <class Key, class Mutex> +struct LocalLockHolder { + using WeakPointer = std::weak_ptr<Mutex>; + using StrongPointer = std::shared_ptr<Mutex>; + using Map = std::unordered_map<Key, WeakPointer>; + using MapLock = std::recursive_mutex; + + LocalLockHolder(Key key, Map& map, MapLock& mapLock) + : mKey(std::move(key)), mMap(map), mMapLock(mapLock) { + std::lock_guard lock(mMapLock); + auto& weakPtr = mMap[mKey]; + + // Check if the RefLock is still alive. + mRefLock = weakPtr.lock(); + if (!mRefLock) { + // Create a new lock. + mRefLock = std::make_shared<Mutex>(); + weakPtr = mRefLock; + } + } + LocalLockHolder(LocalLockHolder&& other) noexcept + : mKey(std::move(other.mKey)), + mMap(other.mMap), + mMapLock(other.mMapLock), + mRefLock(std::move(other.mRefLock)) { + other.mRefLock.reset(); + } + ~LocalLockHolder() { + if (!mRefLock) { + return; + } + + std::lock_guard lock(mMapLock); + // Clear the strong pointer. + mRefLock.reset(); + auto found = mMap.find(mKey); + if (found == mMap.end()) { + return; + } + const auto& weakPtr = found->second; + // If this was the last pointer then it's ok to remove the map entry. + if (weakPtr.expired()) { + mMap.erase(found); + } + } + + void lock() { mRefLock->lock(); } + void unlock() { mRefLock->unlock(); } + void lock_shared() { mRefLock->lock_shared(); } + void unlock_shared() { mRefLock->unlock_shared(); } + +private: + Key mKey; + Map& mMap; + MapLock& mMapLock; + StrongPointer mRefLock; +}; + +using UserLock = LocalLockHolder<userid_t, std::shared_mutex>; +using UserWriteLockGuard = std::unique_lock<UserLock>; +using UserReadLockGuard = std::shared_lock<UserLock>; + +using PackageLock = LocalLockHolder<std::string, std::recursive_mutex>; +using PackageLockGuard = std::lock_guard<PackageLock>; + +#define LOCK_USER() \ + UserLock localUserLock(userId, mUserIdLock, mLock); \ + UserWriteLockGuard userLock(localUserLock) + +#define LOCK_USER_READ() \ + UserLock localUserLock(userId, mUserIdLock, mLock); \ + UserReadLockGuard userLock(localUserLock) + +#define LOCK_PACKAGE() \ + PackageLock localPackageLock(packageName, mPackageNameLock, mLock); \ + PackageLockGuard packageLock(localPackageLock) + +#define LOCK_PACKAGE_USER() \ + LOCK_USER_READ(); \ + LOCK_PACKAGE() + +#else + +#define LOCK_USER() std::lock_guard lock(mLock) +#define LOCK_PACKAGE() std::lock_guard lock(mLock) +#define LOCK_PACKAGE_USER() \ + (void)userId; \ + std::lock_guard lock(mLock) + +#endif // GRANULAR_LOCKS + } // namespace status_t InstalldNativeService::start() { @@ -288,8 +388,6 @@ status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */ return PERMISSION_DENIED; } - std::lock_guard<std::recursive_mutex> lock(mLock); - { std::lock_guard<std::recursive_mutex> lock(mMountsLock); dprintf(fd, "Storage mounts:\n"); @@ -543,14 +641,13 @@ static binder::Status createAppDataDirs(const std::string& path, return ok(); } -binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, - int64_t* _aidl_return) { +binder::Status InstalldNativeService::createAppDataLocked( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -619,10 +716,22 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str } binder::Status InstalldNativeService::createAppData( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE_USER(); + return createAppDataLocked(uuid, packageName, userId, flags, appId, previousAppId, seInfo, + targetSdkVersion, _aidl_return); +} + +binder::Status InstalldNativeService::createAppData( const android::os::CreateAppDataArgs& args, android::os::CreateAppDataResult* _aidl_return) { ENFORCE_UID(AID_SYSTEM); - std::lock_guard<std::recursive_mutex> lock(mLock); + // Locking is performed depeer in the callstack. int64_t ceDataInode = -1; auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId, @@ -637,7 +746,7 @@ binder::Status InstalldNativeService::createAppDataBatched( const std::vector<android::os::CreateAppDataArgs>& args, std::vector<android::os::CreateAppDataResult>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); - std::lock_guard<std::recursive_mutex> lock(mLock); + // Locking is performed depeer in the callstack. std::vector<android::os::CreateAppDataResult> results; for (const auto &arg : args) { @@ -654,7 +763,7 @@ binder::Status InstalldNativeService::migrateAppData(const std::optional<std::st ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -698,7 +807,7 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); binder::Status res = ok(); if (!clear_primary_reference_profile(packageName, profileName)) { @@ -715,7 +824,7 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -812,7 +921,7 @@ static int destroy_app_current_profiles(const std::string& pkgname, userid_t use binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); binder::Status res = ok(); std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr); @@ -832,7 +941,7 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -903,15 +1012,15 @@ binder::Status InstalldNativeService::fixupAppData(const std::optional<std::stri int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; - for (auto user : get_known_users(uuid_)) { + for (auto userId : get_known_users(uuid_)) { + LOCK_USER(); ATRACE_BEGIN("fixup user"); FTS* fts; FTSENT* p; - auto ce_path = create_data_user_ce_path(uuid_, user); - auto de_path = create_data_user_de_path(uuid_, user); + auto ce_path = create_data_user_ce_path(uuid_, userId); + auto de_path = create_data_user_de_path(uuid_, userId); char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open"); @@ -1018,14 +1127,14 @@ static int32_t copy_directory_recursive(const char* from, const char* to) { return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr); } -binder::Status InstalldNativeService::snapshotAppData( - const std::optional<std::string>& volumeUuid, - const std::string& packageName, int32_t user, int32_t snapshotId, - int32_t storageFlags, int64_t* _aidl_return) { +binder::Status InstalldNativeService::snapshotAppData(const std::optional<std::string>& volumeUuid, + const std::string& packageName, + int32_t userId, int32_t snapshotId, + int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); @@ -1038,19 +1147,19 @@ binder::Status InstalldNativeService::snapshotAppData( bool clear_ce_on_exit = false; bool clear_de_on_exit = false; - auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name, - &snapshotId] { + auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &userId, &package_name, + &snapshotId] { if (clear_de_on_exit) { - auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, - package_name); + auto to = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } if (clear_ce_on_exit) { - auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, - package_name); + auto to = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } @@ -1060,10 +1169,11 @@ binder::Status InstalldNativeService::snapshotAppData( auto scope_guard = android::base::make_scope_guard(deleter); if (storageFlags & FLAG_STORAGE_DE) { - auto from = create_data_user_de_package_path(volume_uuid, user, package_name); - auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId); - auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto from = create_data_user_de_package_path(volume_uuid, userId, package_name); + auto to = create_data_misc_de_rollback_path(volume_uuid, userId, snapshotId); + auto rollback_package_path = + create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); if (rc != 0) { @@ -1087,15 +1197,15 @@ binder::Status InstalldNativeService::snapshotAppData( } // The app may not have any data at all, in which case it's OK to skip here. - auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name); + auto from_ce = create_data_user_ce_package_path(volume_uuid, userId, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; return ok(); } // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set. - binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user, - storageFlags | FLAG_CLEAR_CACHE_ONLY, 0); + binder::Status clear_cache_result = + clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CACHE_ONLY, 0); if (!clear_cache_result.isOk()) { // It should be fine to continue snapshot if we for some reason failed // to clear cache. @@ -1103,8 +1213,9 @@ binder::Status InstalldNativeService::snapshotAppData( } // ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set. - binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user, - storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0); + binder::Status clear_code_cache_result = + clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, + 0); if (!clear_code_cache_result.isOk()) { // It should be fine to continue snapshot if we for some reason failed // to clear code_cache. @@ -1112,10 +1223,11 @@ binder::Status InstalldNativeService::snapshotAppData( } if (storageFlags & FLAG_STORAGE_CE) { - auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); - auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId); - auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto from = create_data_user_ce_package_path(volume_uuid, userId, package_name); + auto to = create_data_misc_ce_rollback_path(volume_uuid, userId, snapshotId); + auto rollback_package_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); if (rc != 0) { @@ -1134,8 +1246,9 @@ binder::Status InstalldNativeService::snapshotAppData( return res; } if (_aidl_return != nullptr) { - auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto ce_snapshot_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return)); if (rc != 0) { res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path); @@ -1150,20 +1263,20 @@ binder::Status InstalldNativeService::snapshotAppData( binder::Status InstalldNativeService::restoreAppDataSnapshot( const std::optional<std::string>& volumeUuid, const std::string& packageName, - const int32_t appId, const std::string& seInfo, const int32_t user, + const int32_t appId, const std::string& seInfo, const int32_t userId, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); - auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, - user, snapshotId, package_name); - auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, - user, snapshotId, package_name); + auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); + auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && (access(from_ce.c_str(), F_OK) == 0); @@ -1183,14 +1296,14 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( // It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot // can only be called when user unlocks the phone, meaning that CE user data // is decrypted. - binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, - 0 /* ceDataInode */); + binder::Status res = + clearAppData(volumeUuid, packageName, userId, storageFlags, 0 /* ceDataInode */); if (!res.isOk()) { return res; } if (needs_ce_rollback) { - auto to_ce = create_data_user_ce_path(volume_uuid, user); + auto to_ce = create_data_user_ce_path(volume_uuid, userId); int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from_ce + " to " + to_ce); @@ -1200,11 +1313,11 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( } if (needs_de_rollback) { - auto to_de = create_data_user_de_path(volume_uuid, user); + auto to_de = create_data_user_de_path(volume_uuid, userId); int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str()); if (rc != 0) { if (needs_ce_rollback) { - auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name); + auto ce_data = create_data_user_ce_package_path(volume_uuid, userId, package_name); LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data; if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data; @@ -1217,24 +1330,24 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( } // Finally, restore the SELinux label on the app data. - return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); + return restoreconAppData(volumeUuid, packageName, userId, storageFlags, appId, seInfo); } binder::Status InstalldNativeService::destroyAppDataSnapshot( - const std::optional<std::string> &volumeUuid, const std::string& packageName, - const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, + const std::optional<std::string>& volumeUuid, const std::string& packageName, + const int32_t userId, const int64_t ceSnapshotInode, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (storageFlags & FLAG_STORAGE_DE) { - auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, - user, snapshotId, package_name); + auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, userId, + snapshotId, package_name); int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */); if (res != 0) { @@ -1243,8 +1356,9 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( } if (storageFlags & FLAG_STORAGE_CE) { - auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, - user, snapshotId, package_name, ceSnapshotInode); + auto ce_snapshot_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name, ceSnapshotInode); int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + ce_snapshot_path); @@ -1254,15 +1368,15 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( } binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( - const std::optional<std::string> &volumeUuid, const int32_t user, + const std::optional<std::string>& volumeUuid, const int32_t userId, const std::vector<int32_t>& retainSnapshotIds) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; - auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user); + auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId); std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir); if (!dir) { @@ -1280,8 +1394,8 @@ binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( if (parse_ok && std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(), snapshot_id) == retainSnapshotIds.end()) { - auto rollback_path = create_data_misc_ce_rollback_path( - volume_uuid, user, snapshot_id); + auto rollback_path = + create_data_misc_ce_rollback_path(volume_uuid, userId, snapshot_id); int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + rollback_path); @@ -1299,7 +1413,7 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s CHECK_ARGUMENT_UUID(fromUuid); CHECK_ARGUMENT_UUID(toUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr; const char* to_uuid = toUuid ? toUuid->c_str() : nullptr; @@ -1327,24 +1441,25 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } // Copy private data for all known users - for (auto user : users) { + for (auto userId : users) { + LOCK_USER(); // Data source may not exist for all users; that's okay - auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name); + auto from_ce = create_data_user_ce_package_path(from_uuid, userId, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; continue; } - if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId, - /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr).isOk()) { + if (!createAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE, + appId, /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr) + .isOk()) { res = error("Failed to create package target"); goto fail; } - { - auto from = create_data_user_de_package_path(from_uuid, user, package_name); - auto to = create_data_user_de_path(to_uuid, user); + auto from = create_data_user_de_package_path(from_uuid, userId, package_name); + auto to = create_data_user_de_path(to_uuid, userId); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { @@ -1353,8 +1468,8 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } } { - auto from = create_data_user_ce_package_path(from_uuid, user, package_name); - auto to = create_data_user_ce_path(to_uuid, user); + auto from = create_data_user_ce_package_path(from_uuid, userId, package_name); + auto to = create_data_user_ce_path(to_uuid, userId); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { @@ -1363,8 +1478,9 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } } - if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, - appId, seInfo).isOk()) { + if (!restoreconAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE, + appId, seInfo) + .isOk()) { res = error("Failed to restorecon"); goto fail; } @@ -1382,15 +1498,16 @@ fail: LOG(WARNING) << "Failed to rollback " << to_app_package_path; } } - for (auto user : users) { + for (auto userId : users) { + LOCK_USER(); { - auto to = create_data_user_de_package_path(to_uuid, user, package_name); + auto to = create_data_user_de_package_path(to_uuid, userId, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } { - auto to = create_data_user_ce_package_path(to_uuid, user, package_name); + auto to = create_data_user_ce_package_path(to_uuid, userId, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } @@ -1403,7 +1520,7 @@ binder::Status InstalldNativeService::createUserData(const std::optional<std::st int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (flags & FLAG_STORAGE_DE) { @@ -1421,7 +1538,7 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; binder::Status res = ok(); @@ -1458,7 +1575,9 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> int64_t targetFreeBytes, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); +#ifndef GRANULAR_LOCKS + std::lock_guard lock(mLock); +#endif // !GRANULAR_LOCKS auto uuidString = uuid.value_or(""); const char* uuid_ = uuid ? uuid->c_str() : nullptr; @@ -1485,13 +1604,24 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> // 1. Create trackers for every known UID ATRACE_BEGIN("create"); + const auto users = get_known_users(uuid_); +#ifdef GRANULAR_LOCKS + std::vector<UserLock> userLocks; + userLocks.reserve(users.size()); + std::vector<UserWriteLockGuard> lockGuards; + lockGuards.reserve(users.size()); +#endif // GRANULAR_LOCKS std::unordered_map<uid_t, std::shared_ptr<CacheTracker>> trackers; - for (auto user : get_known_users(uuid_)) { + for (auto userId : users) { +#ifdef GRANULAR_LOCKS + userLocks.emplace_back(userId, mUserIdLock, mLock); + lockGuards.emplace_back(userLocks.back()); +#endif // GRANULAR_LOCKS FTS *fts; FTSENT *p; - auto ce_path = create_data_user_ce_path(uuid_, user); - auto de_path = create_data_user_de_path(uuid_, user); - auto media_path = findDataMediaPath(uuid, user) + "/Android/data/"; + auto ce_path = create_data_user_ce_path(uuid_, userId); + auto de_path = create_data_user_de_path(uuid_, userId); + auto media_path = findDataMediaPath(uuid, userId) + "/Android/data/"; char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), (char*) media_path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { @@ -1621,7 +1751,6 @@ binder::Status InstalldNativeService::rmdex(const std::string& codePath, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); char dex_path[PKG_PATH_MAX]; @@ -1851,7 +1980,18 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st } fts_close(fts); } - +static bool ownsExternalStorage(int32_t appId) { + // Fetch external storage owner appid and check if it is the same as the + // current appId whose size is calculated + struct stat s; + auto _picDir = StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str()); + // check if the stat are present + if (stat(_picDir.c_str(), &s) == 0) { + // fetch the appId from the uid of the media app + return ((int32_t)multiuser_get_app_id(s.st_uid) == appId); + } + return false; +} binder::Status InstalldNativeService::getAppSize(const std::optional<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector<int64_t>& ceDataInodes, @@ -1906,8 +2046,10 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string calculate_tree_size(obbCodePath, &extStats.codeSize); } ATRACE_END(); - - if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) { + // Calculating the app size of the external storage owning app in a manual way, since + // calculating it through quota apis also includes external media storage in the app storage + // numbers + if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START && !ownsExternalStorage(appId)) { ATRACE_BEGIN("code"); for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize, -1, @@ -2337,7 +2479,7 @@ binder::Status InstalldNativeService::getAppCrates( CHECK_ARGUMENT_PACKAGE_NAME(packageName); } #ifdef ENABLE_STORAGE_CRATES - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); auto retVector = std::vector<std::optional<CrateMetadata>>(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; @@ -2383,7 +2525,7 @@ binder::Status InstalldNativeService::getUserCrates( ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); #ifdef ENABLE_STORAGE_CRATES - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto retVector = std::vector<std::optional<CrateMetadata>>(); @@ -2440,7 +2582,7 @@ binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::strin ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = dump_profiles(uid, packageName, profileName, codePath); return ok(); @@ -2452,7 +2594,7 @@ binder::Status InstalldNativeService::copySystemProfile(const std::string& syste bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName); return ok(); } @@ -2462,7 +2604,7 @@ binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::stri const std::string& profileName, int* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = analyze_primary_profiles(uid, packageName, profileName); return ok(); @@ -2473,7 +2615,7 @@ binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId, const std::string& classpath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath); return ok(); @@ -2483,7 +2625,7 @@ binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); std::string snapshot = create_snapshot_profile_path(packageName, profileName); if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) { @@ -2496,35 +2638,34 @@ static const char* getCStr(const std::optional<std::string>& data, const char* default_value = nullptr) { return data ? data->c_str() : default_value; } -binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid, - const std::optional<std::string>& packageName, const std::string& instructionSet, - int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags, +binder::Status InstalldNativeService::dexopt( + const std::string& apkPath, int32_t uid, const std::string& packageName, + const std::string& instructionSet, int32_t dexoptNeeded, + const std::optional<std::string>& outputPath, int32_t dexFlags, const std::string& compilerFilter, const std::optional<std::string>& uuid, const std::optional<std::string>& classLoaderContext, const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion, const std::optional<std::string>& profileName, const std::optional<std::string>& dexMetadataPath, - const std::optional<std::string>& compilationReason, - bool* aidl_return) { + const std::optional<std::string>& compilationReason, bool* aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PATH(apkPath); - if (packageName && *packageName != "*") { - CHECK_ARGUMENT_PACKAGE_NAME(*packageName); - } + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(outputPath); CHECK_ARGUMENT_PATH(dexMetadataPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + const auto userId = multiuser_get_user_id(uid); + LOCK_PACKAGE_USER(); const char* oat_dir = getCStr(outputPath); const char* instruction_set = instructionSet.c_str(); - if (oat_dir != nullptr && !createOatDir(oat_dir, instruction_set).isOk()) { + if (oat_dir != nullptr && !createOatDir(packageName, oat_dir, instruction_set).isOk()) { // Can't create oat dir - let dexopt use cache dir. oat_dir = nullptr; } const char* apk_path = apkPath.c_str(); - const char* pkgname = getCStr(packageName, "*"); + const char* pkgname = packageName.c_str(); const char* compiler_filter = compilerFilter.c_str(); const char* volume_uuid = getCStr(uuid); const char* class_loader_context = getCStr(classLoaderContext); @@ -2565,7 +2706,7 @@ binder::Status InstalldNativeService::linkNativeLibraryDirectory( CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(nativeLibPath32); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -2656,7 +2797,16 @@ binder::Status InstalldNativeService::restoreconAppData(const std::optional<std: ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); + return restoreconAppDataLocked(uuid, packageName, userId, flags, appId, seInfo); +} + +binder::Status InstalldNativeService::restoreconAppDataLocked( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); binder::Status res = ok(); @@ -2682,11 +2832,13 @@ binder::Status InstalldNativeService::restoreconAppData(const std::optional<std: return res; } -binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, - const std::string& instructionSet) { +binder::Status InstalldNativeService::createOatDir(const std::string& packageName, + const std::string& oatDir, + const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(oatDir); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* oat_dir = oatDir.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2708,10 +2860,12 @@ binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, return ok(); } -binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) { +binder::Status InstalldNativeService::rmPackageDir(const std::string& packageName, + const std::string& packageDir) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(packageDir); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); if (validate_apk_path(packageDir.c_str())) { return error("Invalid path " + packageDir); @@ -2722,12 +2876,15 @@ binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir return ok(); } -binder::Status InstalldNativeService::linkFile(const std::string& relativePath, - const std::string& fromBase, const std::string& toBase) { +binder::Status InstalldNativeService::linkFile(const std::string& packageName, + const std::string& relativePath, + const std::string& fromBase, + const std::string& toBase) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(fromBase); CHECK_ARGUMENT_PATH(toBase); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* relative_path = relativePath.c_str(); const char* from_base = fromBase.c_str(); @@ -2752,12 +2909,15 @@ binder::Status InstalldNativeService::linkFile(const std::string& relativePath, return ok(); } -binder::Status InstalldNativeService::moveAb(const std::string& apkPath, - const std::string& instructionSet, const std::string& outputPath) { +binder::Status InstalldNativeService::moveAb(const std::string& packageName, + const std::string& apkPath, + const std::string& instructionSet, + const std::string& outputPath) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* apk_path = apkPath.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2767,13 +2927,16 @@ binder::Status InstalldNativeService::moveAb(const std::string& apkPath, return success ? ok() : error(); } -binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath, - const std::string& instructionSet, const std::optional<std::string>& outputPath, - int64_t* _aidl_return) { +binder::Status InstalldNativeService::deleteOdex(const std::string& packageName, + const std::string& apkPath, + const std::string& instructionSet, + const std::optional<std::string>& outputPath, + int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* apk_path = apkPath.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2802,11 +2965,14 @@ struct fsverity_measurement { #endif -binder::Status InstalldNativeService::installApkVerity(const std::string& filePath, - android::base::unique_fd verityInputAshmem, int32_t contentSize) { +binder::Status InstalldNativeService::installApkVerity(const std::string& packageName, + const std::string& filePath, + android::base::unique_fd verityInputAshmem, + int32_t contentSize) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(filePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { return ok(); @@ -2884,11 +3050,13 @@ binder::Status InstalldNativeService::installApkVerity(const std::string& filePa return ok(); } -binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::string& filePath, +binder::Status InstalldNativeService::assertFsverityRootHashMatches( + const std::string& packageName, const std::string& filePath, const std::vector<uint8_t>& expectedHash) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(filePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { return ok(); @@ -2927,7 +3095,8 @@ binder::Status InstalldNativeService::reconcileSecondaryDexFile( CHECK_ARGUMENT_UUID(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(dexPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + const auto userId = multiuser_get_user_id(uid); + LOCK_PACKAGE_USER(); bool result = android::installd::reconcile_secondary_dex_file( dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return); @@ -3007,8 +3176,9 @@ binder::Status InstalldNativeService::tryMountDataMirror( const char* uuid_ = uuid->c_str(); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_)); - std::lock_guard<std::recursive_mutex> lock(mLock); if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create CE mirror"); } @@ -3077,8 +3247,9 @@ binder::Status InstalldNativeService::onPrivateVolumeRemoved( std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_)); std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_)); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + // Unmount CE storage - std::lock_guard<std::recursive_mutex> lock(mLock); if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) { if (errno != ENOENT) { res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(), @@ -3127,7 +3298,7 @@ binder::Status InstalldNativeService::prepareAppProfile(const std::string& packa ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath, dexMetadata); diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 3fdb01ad8b..04662eaf0b 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -21,8 +21,9 @@ #include <inttypes.h> #include <unistd.h> -#include <vector> +#include <shared_mutex> #include <unordered_map> +#include <vector> #include <android-base/macros.h> #include <binder/BinderService.h> @@ -49,6 +50,11 @@ public: const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); + binder::Status createAppDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, + const std::string& seInfo, int32_t targetSdkVersion, + int64_t* _aidl_return); binder::Status createAppData( const android::os::CreateAppDataArgs& args, @@ -60,6 +66,9 @@ public: binder::Status restoreconAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo); + binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo); binder::Status migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags); binder::Status clearAppData(const std::optional<std::string>& uuid, @@ -110,16 +119,15 @@ public: int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, const std::string& fromCodePath); - binder::Status dexopt(const std::string& apkPath, int32_t uid, - const std::optional<std::string>& packageName, const std::string& instructionSet, - int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags, - const std::string& compilerFilter, const std::optional<std::string>& uuid, - const std::optional<std::string>& classLoaderContext, - const std::optional<std::string>& seInfo, bool downgrade, - int32_t targetSdkVersion, const std::optional<std::string>& profileName, - const std::optional<std::string>& dexMetadataPath, - const std::optional<std::string>& compilationReason, - bool* aidl_return); + binder::Status dexopt(const std::string& apkPath, int32_t uid, const std::string& packageName, + const std::string& instructionSet, int32_t dexoptNeeded, + const std::optional<std::string>& outputPath, int32_t dexFlags, + const std::string& compilerFilter, const std::optional<std::string>& uuid, + const std::optional<std::string>& classLoaderContext, + const std::optional<std::string>& seInfo, bool downgrade, + int32_t targetSdkVersion, const std::optional<std::string>& profileName, + const std::optional<std::string>& dexMetadataPath, + const std::optional<std::string>& compilationReason, bool* aidl_return); binder::Status controlDexOptBlocking(bool block); @@ -143,22 +151,25 @@ public: binder::Status destroyProfileSnapshot(const std::string& packageName, const std::string& profileName); - binder::Status rmPackageDir(const std::string& packageDir); + binder::Status rmPackageDir(const std::string& packageName, const std::string& packageDir); binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes, int32_t flags); binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId); - binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet); - binder::Status linkFile(const std::string& relativePath, const std::string& fromBase, - const std::string& toBase); - binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet, - const std::string& outputPath); - binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet, - const std::optional<std::string>& outputPath, int64_t* _aidl_return); - binder::Status installApkVerity(const std::string& filePath, - android::base::unique_fd verityInput, int32_t contentSize); - binder::Status assertFsverityRootHashMatches(const std::string& filePath, - const std::vector<uint8_t>& expectedHash); + binder::Status createOatDir(const std::string& packageName, const std::string& oatDir, + const std::string& instructionSet); + binder::Status linkFile(const std::string& packageName, const std::string& relativePath, + const std::string& fromBase, const std::string& toBase); + binder::Status moveAb(const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, const std::string& outputPath); + binder::Status deleteOdex(const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, + const std::optional<std::string>& outputPath, int64_t* _aidl_return); + binder::Status installApkVerity(const std::string& packageName, const std::string& filePath, + android::base::unique_fd verityInput, int32_t contentSize); + binder::Status assertFsverityRootHashMatches(const std::string& packageName, + const std::string& filePath, + const std::vector<uint8_t>& expectedHash); binder::Status reconcileSecondaryDexFile(const std::string& dexPath, const std::string& packageName, int32_t uid, const std::vector<std::string>& isa, const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return); @@ -181,6 +192,8 @@ public: private: std::recursive_mutex mLock; + std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock; + std::unordered_map<std::string, std::weak_ptr<std::recursive_mutex>> mPackageNameLock; std::recursive_mutex mMountsLock; std::recursive_mutex mQuotasLock; diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 9c51ff749d..e024548a2c 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -56,7 +56,7 @@ interface IInstalld { @utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath); // Returns false if it is cancelled. Returns true if it is completed or have other errors. - boolean dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName, + boolean dexopt(@utf8InCpp String apkPath, int uid, @utf8InCpp String packageName, @utf8InCpp String instructionSet, int dexoptNeeded, @nullable @utf8InCpp String outputPath, int dexFlags, @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid, @@ -85,20 +85,22 @@ interface IInstalld { @utf8InCpp String profileName, @utf8InCpp String classpath); void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName); - void rmPackageDir(@utf8InCpp String packageDir); + void rmPackageDir(@utf8InCpp String packageName, @utf8InCpp String packageDir); void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, int flags); void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId); - void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet); - void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase, - @utf8InCpp String toBase); - void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet, - @utf8InCpp String outputPath); - long deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet, - @nullable @utf8InCpp String outputPath); - void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput, - int contentSize); - void assertFsverityRootHashMatches(@utf8InCpp String filePath, in byte[] expectedHash); + void createOatDir(@utf8InCpp String packageName, @utf8InCpp String oatDir, + @utf8InCpp String instructionSet); + void linkFile(@utf8InCpp String packageName, @utf8InCpp String relativePath, + @utf8InCpp String fromBase, @utf8InCpp String toBase); + void moveAb(@utf8InCpp String packageName, @utf8InCpp String apkPath, + @utf8InCpp String instructionSet, @utf8InCpp String outputPath); + long deleteOdex(@utf8InCpp String packageName, @utf8InCpp String apkPath, + @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); + void installApkVerity(@utf8InCpp String packageName, @utf8InCpp String filePath, + in FileDescriptor verityInput, int contentSize); + void assertFsverityRootHashMatches(@utf8InCpp String packageName, @utf8InCpp String filePath, + in byte[] expectedHash); boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid, diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index f3ec63f870..2bcf2d473d 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -52,6 +52,7 @@ #include <server_configurable_flags/get_flags.h> #include <system/thread_defs.h> #include <utils/Mutex.h> +#include <ziparchive/zip_archive.h> #include "dexopt.h" #include "dexopt_return_codes.h" @@ -459,8 +460,8 @@ static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::st }); } -static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name, - const std::string& location) { +static unique_fd open_snapshot_profile(uid_t uid, const std::string& package_name, + const std::string& location) { std::string profile = create_snapshot_profile_path(package_name, location); return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); } @@ -2562,7 +2563,7 @@ static bool create_app_profile_snapshot(int32_t app_id, const std::string& classpath) { int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id); - unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); + unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name); if (snapshot_fd < 0) { return false; } @@ -2636,7 +2637,7 @@ static bool create_boot_image_profile_snapshot(const std::string& package_name, } // Open and create the snapshot profile. - unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); + unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name); // Collect all non empty profiles. // The collection will traverse all applications profiles and find the non empty files. @@ -2738,6 +2739,20 @@ bool create_profile_snapshot(int32_t app_id, const std::string& package_name, } } +static bool check_profile_exists_in_dexmetadata(const std::string& dex_metadata) { + ZipArchiveHandle zip = nullptr; + if (OpenArchive(dex_metadata.c_str(), &zip) != 0) { + PLOG(ERROR) << "Failed to open dm '" << dex_metadata << "'"; + return false; + } + + ZipEntry64 entry; + int result = FindEntry(zip, "primary.prof", &entry); + CloseArchive(zip); + + return result != 0 ? false : true; +} + bool prepare_app_profile(const std::string& package_name, userid_t user_id, appid_t app_id, @@ -2754,7 +2769,7 @@ bool prepare_app_profile(const std::string& package_name, } // Check if we need to install the profile from the dex metadata. - if (!dex_metadata) { + if (!dex_metadata || !check_profile_exists_in_dexmetadata(dex_metadata->c_str())) { return true; } diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 70820172b7..4cde7e3fb7 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -13,7 +13,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_utils_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libutils", @@ -33,7 +36,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_cache_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libbinder", @@ -48,6 +54,7 @@ cc_test { "libasync_safe", "libdiskusage", "libinstalld", + "libziparchive", "liblog", "liblogwrap", ], @@ -74,7 +81,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_service_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libbinder", @@ -83,12 +93,14 @@ cc_test { "libprocessgroup", "libselinux", "libutils", + "packagemanager_aidl-cpp", "server_configurable_flags", ], static_libs: [ "libasync_safe", "libdiskusage", "libinstalld", + "libziparchive", "liblog", "liblogwrap", ], @@ -115,7 +127,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_dexopt_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libbinder", @@ -158,7 +173,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_otapreopt_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libcutils", @@ -167,6 +185,6 @@ cc_test { ], static_libs: [ "liblog", - "libotapreoptparameters" + "libotapreoptparameters", ], } diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index 9a1e17eb20..8a27a06087 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -122,6 +122,7 @@ protected: service = new InstalldNativeService(); testUuid = kTestUuid; + system("rm -rf /data/local/tmp/user"); system("mkdir -p /data/local/tmp/user/0"); } diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index a937436f9c..bb36c395e1 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -635,6 +635,7 @@ protected: int64_t bytes_freed; binder::Status result = service_->deleteOdex( + package_name_, apk_path_, kRuntimeIsa, in_dalvik_cache ? std::nullopt : std::make_optional<std::string>(app_oat_dir_.c_str()), @@ -729,7 +730,7 @@ TEST_F(DexoptTest, DexoptPrimaryPublic) { TEST_F(DexoptTest, DexoptPrimaryPublicCreateOatDir) { LOG(INFO) << "DexoptPrimaryPublic"; - ASSERT_BINDER_SUCCESS(service_->createOatDir(app_oat_dir_, kRuntimeIsa)); + ASSERT_BINDER_SUCCESS(service_->createOatDir(package_name_, app_oat_dir_, kRuntimeIsa)); CompilePrimaryDexOk("verify", DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, app_oat_dir_.c_str(), diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 1e7559d174..b831515b94 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -18,10 +18,11 @@ #include <string> #include <fcntl.h> +#include <pwd.h> #include <stdlib.h> #include <string.h> -#include <sys/statvfs.h> #include <sys/stat.h> +#include <sys/statvfs.h> #include <sys/xattr.h> #include <android-base/file.h> @@ -32,8 +33,10 @@ #include <cutils/properties.h> #include <gtest/gtest.h> -#include "binder_test_utils.h" +#include <android/content/pm/IPackageManagerNative.h> +#include <binder/IServiceManager.h> #include "InstalldNativeService.h" +#include "binder_test_utils.h" #include "dexopt.h" #include "globals.h" #include "utils.h" @@ -41,6 +44,34 @@ using android::base::StringPrintf; namespace android { +std::string get_package_name(uid_t uid) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<content::pm::IPackageManagerNative> package_mgr; + if (sm.get() == nullptr) { + LOG(INFO) << "Cannot find service manager"; + } else { + sp<IBinder> binder = sm->getService(String16("package_native")); + if (binder.get() == nullptr) { + LOG(INFO) << "Cannot find package_native"; + } else { + package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder); + } + } + // find package name + std::string pkg; + if (package_mgr != nullptr) { + std::vector<std::string> names; + binder::Status status = package_mgr->getNamesForUids({(int)uid}, &names); + if (!status.isOk()) { + LOG(INFO) << "getNamesForUids failed: %s", status.exceptionMessage().c_str(); + } else { + if (!names[0].empty()) { + pkg = names[0].c_str(); + } + } + } + return pkg; +} namespace installd { constexpr const char* kTestUuid = "TEST"; @@ -107,6 +138,7 @@ protected: service = new InstalldNativeService(); testUuid = kTestUuid; + system("rm -rf /data/local/tmp/user"); system("mkdir -p /data/local/tmp/user/0"); init_globals_from_data_and_root(); @@ -247,7 +279,50 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa")); EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } - +TEST_F(ServiceTest, GetAppSize) { + struct stat s; + + std::string externalPicDir = + StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str()); + if (stat(externalPicDir.c_str(), &s) == 0) { + // fetch the appId from the uid of the external storage owning app + int32_t externalStorageAppId = multiuser_get_app_id(s.st_uid); + // Fetch Package Name for the external storage owning app uid + std::string pkg = get_package_name(s.st_uid); + + std::vector<int64_t> externalStorageSize, externalStorageSizeAfterAddingExternalFile; + std::vector<int64_t> ceDataInodes; + + std::vector<std::string> codePaths; + std::vector<std::string> packageNames; + // set up parameters + packageNames.push_back(pkg); + ceDataInodes.push_back(0); + // initialise the mounts + service->invalidateMounts(); + // call the getAppSize to get the current size of the external storage owning app + service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, + externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize); + // add a file with 20MB size to the external storage + std::string externalFileLocation = + StringPrintf("%s/Pictures/%s", getenv("EXTERNAL_STORAGE"), "External.jpg"); + std::string externalFileContentCommand = + StringPrintf("dd if=/dev/zero of=%s bs=1M count=20", externalFileLocation.c_str()); + system(externalFileContentCommand.c_str()); + // call the getAppSize again to get the new size of the external storage owning app + service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, + externalStorageAppId, ceDataInodes, codePaths, + &externalStorageSizeAfterAddingExternalFile); + // check that the size before adding the file and after should be the same, as the app size + // is not changed. + for (size_t i = 0; i < externalStorageSize.size(); i++) { + ASSERT_TRUE(externalStorageSize[i] == externalStorageSizeAfterAddingExternalFile[i]); + } + // remove the external file + std::string removeCommand = StringPrintf("rm -f %s", externalFileLocation.c_str()); + system(removeCommand.c_str()); + } +} static bool mkdirs(const std::string& path, mode_t mode) { struct stat sb; if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 3ebdeee7aa..80c0548fca 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -47,6 +47,15 @@ cc_binary { } cc_binary { + name: "servicemanager.recovery", + stem: "servicemanager", + recovery: true, + defaults: ["servicemanager_defaults"], + init_rc: ["servicemanager.recovery.rc"], + srcs: ["main.cpp"], +} + +cc_binary { name: "vndservicemanager", defaults: ["servicemanager_defaults"], init_rc: ["vndservicemanager.rc"], diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 4e44ac7323..4374abe2ef 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -28,6 +28,9 @@ #ifndef VENDORSERVICEMANAGER #include <vintf/VintfObject.h> +#ifdef __ANDROID_RECOVERY__ +#include <vintf/VintfObjectRecovery.h> +#endif // __ANDROID_RECOVERY__ #include <vintf/constants.h> #endif // !VENDORSERVICEMANAGER @@ -37,16 +40,33 @@ using ::android::internal::Stability; namespace android { #ifndef VENDORSERVICEMANAGER + struct ManifestWithDescription { std::shared_ptr<const vintf::HalManifest> manifest; const char* description; }; +static std::vector<ManifestWithDescription> GetManifestsWithDescription() { +#ifdef __ANDROID_RECOVERY__ + auto vintfObject = vintf::VintfObjectRecovery::GetInstance(); + if (vintfObject == nullptr) { + LOG(ERROR) << "NULL VintfObjectRecovery!"; + return {}; + } + return {ManifestWithDescription{vintfObject->getRecoveryHalManifest(), "recovery"}}; +#else + auto vintfObject = vintf::VintfObject::GetInstance(); + if (vintfObject == nullptr) { + LOG(ERROR) << "NULL VintfObject!"; + return {}; + } + return {ManifestWithDescription{vintfObject->getDeviceHalManifest(), "device"}, + ManifestWithDescription{vintfObject->getFrameworkHalManifest(), "framework"}}; +#endif +} + // func true -> stop search and forEachManifest will return true static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) { - for (const ManifestWithDescription& mwd : { - ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" }, - ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" }, - }) { + for (const ManifestWithDescription& mwd : GetManifestsWithDescription()) { if (mwd.manifest == nullptr) { LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description; // note, we explicitly do not retry here, so that we can detect VINTF diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index 8c1beaca20..2fb9c2bc9a 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -111,6 +111,10 @@ private: }; int main(int argc, char** argv) { +#ifdef __ANDROID_RECOVERY__ + android::base::InitLogging(argv, android::base::KernelLogger); +#endif + if (argc > 2) { LOG(FATAL) << "usage: " << argv[0] << " [binder driver]"; } diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc new file mode 100644 index 0000000000..067faf9c8f --- /dev/null +++ b/cmds/servicemanager/servicemanager.recovery.rc @@ -0,0 +1,4 @@ +service servicemanager /system/bin/servicemanager + disabled + group system readproc + seclabel u:r:servicemanager:s0 diff --git a/include/input/Input.h b/include/input/Input.h index 7cc595a264..29503af65f 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -201,8 +201,17 @@ namespace android { class Parcel; #endif +/* + * Apply the given transform to the point without applying any translation/offset. + */ +vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy); + const char* inputEventTypeToString(int32_t type); +std::string inputEventSourceToString(int32_t source); + +bool isFromSource(uint32_t source, uint32_t test); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. @@ -565,6 +574,8 @@ public: inline ui::Transform getTransform() const { return mTransform; } + int getSurfaceRotation() const; + inline float getXPrecision() const { return mXPrecision; } inline float getYPrecision() const { return mYPrecision; } @@ -840,15 +851,12 @@ public: inline bool getHasFocus() const { return mHasFocus; } - inline bool getInTouchMode() const { return mInTouchMode; } - - void initialize(int32_t id, bool hasFocus, bool inTouchMode); + void initialize(int32_t id, bool hasFocus); void initialize(const FocusEvent& from); protected: bool mHasFocus; - bool mInTouchMode; }; /* diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index d655b28278..edcb615491 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -178,10 +178,9 @@ struct InputMessage { struct Focus { int32_t eventId; - // The following 3 fields take up 4 bytes total + // The following 2 fields take up 4 bytes total bool hasFocus; - bool inTouchMode; - uint8_t empty[2]; + uint8_t empty[3]; inline size_t size() const { return sizeof(Focus); } } focus; @@ -381,7 +380,7 @@ public: * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode); + status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus); /* Publishes a capture event to the input channel. * diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 8270ae5ce7..d8101fad4c 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -202,7 +202,7 @@ cc_library { sanitize: { misc_undefined: ["integer"], }, - min_sdk_version: "29", + min_sdk_version: "30", tidy: true, tidy_flags: [ diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 745d9e91d8..ba57a983fb 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -824,7 +824,7 @@ void* Parcel::writeInplace(size_t len) const size_t padded = pad_size(len); - // sanity check for integer overflow + // check for integer overflow if (mDataPos+padded < mDataPos) { return nullptr; } diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 7d14315b01..f5abb859bc 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -228,10 +228,8 @@ constexpr const char* const kManualInterfaces[] = { "android.gfx.tests.IIPCTest", "android.gfx.tests.ISafeInterfaceTest", "android.graphicsenv.IGpuService", - "android.gui.DisplayEventConnection", "android.gui.IConsumerListener", "android.gui.IGraphicBufferConsumer", - "android.gui.IRegionSamplingListener", "android.gui.ITransactionComposerListener", "android.gui.SensorEventConnection", "android.gui.SensorServer", diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h index 0ad400bffb..c903998021 100644 --- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h @@ -365,6 +365,8 @@ class ScopedFileDescriptor : public impl::ScopedAResource<int, internal::closeWi ScopedFileDescriptor(ScopedFileDescriptor&&) = default; ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default; + ScopedFileDescriptor dup() const { return ScopedFileDescriptor(::dup(get())); } + bool operator!=(const ScopedFileDescriptor& rhs) const { return get() != rhs.get(); } bool operator<(const ScopedFileDescriptor& rhs) const { return get() < rhs.get(); } bool operator<=(const ScopedFileDescriptor& rhs) const { return get() <= rhs.get(); } diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h index 5de64f8c5c..09411e76a3 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -190,9 +190,9 @@ class BnCInterface : public INTERFACE { BnCInterface() {} virtual ~BnCInterface() {} - SpAIBinder asBinder() override; + SpAIBinder asBinder() override final; - bool isRemote() override { return false; } + bool isRemote() override final { return false; } protected: /** @@ -215,9 +215,9 @@ class BpCInterface : public INTERFACE { explicit BpCInterface(const SpAIBinder& binder) : mBinder(binder) {} virtual ~BpCInterface() {} - SpAIBinder asBinder() override; + SpAIBinder asBinder() override final; - bool isRemote() override { return AIBinder_isRemote(mBinder.get()); } + bool isRemote() override final { return AIBinder_isRemote(mBinder.get()); } binder_status_t dump(int fd, const char** args, uint32_t numArgs) override { return AIBinder_dump(asBinder().get(), fd, args, numArgs); diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 4561d6e494..e2fc18d859 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -24,13 +24,15 @@ rust_library { target: { darwin: { enabled: false, - } + }, }, apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "current", } rust_library { @@ -45,7 +47,7 @@ rust_library { target: { darwin: { enabled: false, - } + }, }, apex_available: [ "//apex_available:platform", @@ -69,13 +71,15 @@ rust_library { target: { darwin: { enabled: false, - } + }, }, apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "current", lints: "none", clippy_lints: "none", } @@ -88,20 +92,31 @@ rust_bindgen { bindgen_flags: [ // Unfortunately the only way to specify the rust_non_exhaustive enum // style for a type is to make it the default - "--default-enum-style", "rust_non_exhaustive", + "--default-enum-style", + "rust_non_exhaustive", // and then specify constified enums for the enums we don't want // rustified - "--constified-enum", "android::c_interface::consts::.*", + "--constified-enum", + "android::c_interface::consts::.*", - "--allowlist-type", "android::c_interface::.*", - "--allowlist-type", "AStatus", - "--allowlist-type", "AIBinder_Class", - "--allowlist-type", "AIBinder", - "--allowlist-type", "AIBinder_Weak", - "--allowlist-type", "AIBinder_DeathRecipient", - "--allowlist-type", "AParcel", - "--allowlist-type", "binder_status_t", - "--allowlist-function", ".*", + "--allowlist-type", + "android::c_interface::.*", + "--allowlist-type", + "AStatus", + "--allowlist-type", + "AIBinder_Class", + "--allowlist-type", + "AIBinder", + "--allowlist-type", + "AIBinder_Weak", + "--allowlist-type", + "AIBinder_DeathRecipient", + "--allowlist-type", + "AParcel", + "--allowlist-type", + "binder_status_t", + "--allowlist-function", + ".*", ], shared_libs: [ "libbinder_ndk", @@ -127,8 +142,10 @@ rust_bindgen { apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "current", } // TODO(b/184872979): remove once the Rust API is created. @@ -142,8 +159,10 @@ rust_bindgen { ], apex_available: [ "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "current", } rust_test { diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs index 64833b6d60..91047bea48 100644 --- a/libs/binder/rust/binder_tokio/lib.rs +++ b/libs/binder/rust/binder_tokio/lib.rs @@ -35,6 +35,11 @@ use std::future::Future; /// Retrieve an existing service for a particular interface, sleeping for a few /// seconds if it doesn't yet exist. pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { + if binder::is_handling_transaction() { + // See comment in the BinderAsyncPool impl. + return binder::public_api::get_interface::<T>(name); + } + let name = name.to_string(); let res = tokio::task::spawn_blocking(move || { binder::public_api::get_interface::<T>(&name) @@ -54,6 +59,11 @@ pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Res /// Retrieve an existing service for a particular interface, or start it if it /// is configured as a dynamic service and isn't yet started. pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { + if binder::is_handling_transaction() { + // See comment in the BinderAsyncPool impl. + return binder::public_api::wait_for_interface::<T>(name); + } + let name = name.to_string(); let res = tokio::task::spawn_blocking(move || { binder::public_api::wait_for_interface::<T>(&name) @@ -86,18 +96,27 @@ impl BinderAsyncPool for Tokio { B: Send + 'a, E: From<crate::StatusCode>, { - let handle = tokio::task::spawn_blocking(spawn_me); - Box::pin(async move { - // The `is_panic` branch is not actually reachable in Android as we compile - // with `panic = abort`. - match handle.await { - Ok(res) => after_spawn(res).await, - Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()), - Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()), - Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()), - } - }) + if binder::is_handling_transaction() { + // We are currently on the thread pool for a binder server, so we should execute the + // transaction on the current thread so that the binder kernel driver is able to apply + // its deadlock prevention strategy to the sub-call. + // + // This shouldn't cause issues with blocking the thread as only one task will run in a + // call to `block_on`, so there aren't other tasks to block. + let result = spawn_me(); + Box::pin(after_spawn(result)) + } else { + let handle = tokio::task::spawn_blocking(spawn_me); + Box::pin(async move { + // The `is_panic` branch is not actually reachable in Android as we compile + // with `panic = abort`. + match handle.await { + Ok(res) => after_spawn(res).await, + Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()), + Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()), + Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()), + } + }) + } } } - - diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index d09ac83785..3d2eddf611 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -1027,16 +1027,20 @@ macro_rules! declare_binder_interface { #[macro_export] macro_rules! declare_binder_enum { { + $( #[$attr:meta] )* $enum:ident : [$backing:ty; $size:expr] { $( $name:ident = $value:expr, )* } } => { + $( #[$attr] )* #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] + #[allow(missing_docs)] pub struct $enum(pub $backing); impl $enum { - $( pub const $name: Self = Self($value); )* + $( #[allow(missing_docs)] pub const $name: Self = Self($value); )* #[inline(always)] + #[allow(missing_docs)] pub const fn enum_values() -> [Self; $size] { [$(Self::$name),*] } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index cce55c0188..b94dfa137e 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -114,7 +114,7 @@ pub use crate::binder::{ }; pub use crate::binder_async::{BoxFuture, BinderAsyncPool}; pub use error::{status_t, ExceptionCode, Result, Status, StatusCode}; -pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder}; +pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder}; pub use parcel::{BorrowedParcel, Parcel}; pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service}; pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder}; diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index e183ea30fc..b7c7ae4b54 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -517,3 +517,12 @@ impl Remotable for () { } impl Interface for () {} + +/// Determine whether the current thread is currently executing an incoming +/// transaction. +pub fn is_handling_transaction() -> bool { + unsafe { + // Safety: This method is always safe to call. + sys::AIBinder_isHandlingTransaction() + } +} diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index 1fd2eadc4e..40359b4749 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -16,7 +16,7 @@ //! Rust Binder crate integration tests -use binder::declare_binder_interface; +use binder::{declare_binder_enum, declare_binder_interface}; use binder::parcel::BorrowedParcel; use binder::{ Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode, @@ -294,6 +294,23 @@ impl ITestSameDescriptor for BpTestSameDescriptor {} impl ITestSameDescriptor for Binder<BnTestSameDescriptor> {} +declare_binder_enum! { + TestEnum : [i32; 3] { + FOO = 1, + BAR = 2, + BAZ = 3, + } +} + +declare_binder_enum! { + #[deprecated(since = "1.0.0")] + TestDeprecatedEnum : [i32; 3] { + FOO = 1, + BAR = 2, + BAZ = 3, + } +} + #[cfg(test)] mod tests { use selinux_bindgen as selinux_sys; diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp index f2d0943e86..858739c9dd 100644 --- a/libs/graphicsenv/GpuStatsInfo.cpp +++ b/libs/graphicsenv/GpuStatsInfo.cpp @@ -88,6 +88,7 @@ status_t GpuStatsAppInfo::writeToParcel(Parcel* parcel) const { if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status; if ((status = parcel->writeBool(falsePrerotation)) != OK) return status; if ((status = parcel->writeBool(gles1InUse)) != OK) return status; + if ((status = parcel->writeBool(angleInUse)) != OK) return status; return OK; } @@ -101,6 +102,7 @@ status_t GpuStatsAppInfo::readFromParcel(const Parcel* parcel) { if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status; if ((status = parcel->readBool(&falsePrerotation)) != OK) return status; if ((status = parcel->readBool(&gles1InUse)) != OK) return status; + if ((status = parcel->readBool(&angleInUse)) != OK) return status; return OK; } @@ -111,6 +113,7 @@ std::string GpuStatsAppInfo::toString() const { StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse); StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation); StringAppendF(&result, "gles1InUse = %d\n", gles1InUse); + StringAppendF(&result, "angleInUse = %d\n", angleInUse); result.append("glDriverLoadingTime:"); for (int32_t loadingTime : glDriverLoadingTime) { StringAppendF(&result, " %d", loadingTime); diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h index 9aba69fd07..5b513d2a79 100644 --- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -16,6 +16,7 @@ #pragma once +#include <chrono> #include <string> #include <vector> @@ -52,7 +53,7 @@ public: }; /* - * class for transporting gpu app stats from GpuService to authorized recipents. + * class for transporting gpu app stats from GpuService to authorized recipients. * This class is intended to be a data container. */ class GpuStatsAppInfo : public Parcelable { @@ -72,6 +73,9 @@ public: bool cpuVulkanInUse = false; bool falsePrerotation = false; bool gles1InUse = false; + bool angleInUse = false; + + std::chrono::time_point<std::chrono::system_clock> lastAccessTime; }; /* diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 19a29c196d..c5dec19582 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -180,11 +180,9 @@ cc_library_shared { "FrameTimelineInfo.cpp", "GLConsumer.cpp", "IConsumerListener.cpp", - "IDisplayEventConnection.cpp", "IGraphicBufferConsumer.cpp", "IGraphicBufferProducer.cpp", "IProducerListener.cpp", - "IRegionSamplingListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", "ITransactionCompletedListener.cpp", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 55703214a5..85a4b67f34 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -132,12 +132,11 @@ void BLASTBufferItemConsumer::onSidebandStreamChanged() { } } -BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, - int width, int height, int32_t format) - : mSurfaceControl(surface), - mSize(width, height), +BLASTBufferQueue::BLASTBufferQueue(const std::string& name) + : mSurfaceControl(nullptr), + mSize(1, 1), mRequestedSize(mSize), - mFormat(format), + mFormat(PIXEL_FORMAT_RGBA_8888), mSyncTransaction(nullptr) { createBufferQueue(&mProducer, &mConsumer); // since the adapter is in the client process, set dequeue timeout @@ -158,25 +157,21 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont mBufferItemConsumer->setName(String8(consumerName.c_str())); mBufferItemConsumer->setFrameAvailableListener(this); mBufferItemConsumer->setBufferFreedListener(this); - mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height); - mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format)); mBufferItemConsumer->setBlastBufferQueue(this); ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers); mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers); mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers; - - mTransformHint = mSurfaceControl->getTransformHint(); - mBufferItemConsumer->setTransformHint(mTransformHint); - SurfaceComposerClient::Transaction() - .setFlags(surface, layer_state_t::eEnableBackpressure, - layer_state_t::eEnableBackpressure) - .setApplyToken(mApplyToken) - .apply(); mNumAcquired = 0; mNumFrameAvailable = 0; - BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", width, - height, format, mTransformHint); + BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", mSize.width, + mSize.height, mFormat, mTransformHint); +} + +BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, + int width, int height, int32_t format) + : BLASTBufferQueue(name) { + update(surface, width, height, format); } BLASTBufferQueue::~BLASTBufferQueue() { @@ -228,12 +223,9 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, // If the buffer supports scaling, update the frame immediately since the client may // want to scale the existing buffer to the new size. mSize = mRequestedSize; - // We only need to update the scale if we've received at least one buffer. The reason - // for this is the scale is calculated based on the requested size and buffer size. - // If there's no buffer, the scale will always be 1. SurfaceComposerClient::Transaction* destFrameTransaction = (outTransaction) ? outTransaction : &t; - if (mSurfaceControl != nullptr && mLastBufferInfo.hasBuffer) { + if (mSurfaceControl != nullptr) { destFrameTransaction->setDestinationFrame(mSurfaceControl, Rect(0, 0, newSize.getWidth(), newSize.getHeight())); @@ -507,11 +499,9 @@ void BLASTBufferQueue::acquireNextBufferLocked( // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. incStrong((void*)transactionCallbackThunk); - incStrong((void*)transactionCommittedCallbackThunk); - const bool sizeHasChanged = mRequestedSize != mSize; + const bool updateDestinationFrame = mRequestedSize != mSize; mSize = mRequestedSize; - const bool updateDestinationFrame = sizeHasChanged || !mLastBufferInfo.hasBuffer; Rect crop = computeCrop(bufferItem); mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(), bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, @@ -527,7 +517,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata); t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage); t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this)); - t->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast<void*>(this)); + mSurfaceControlsWithPendingCallback.push(mSurfaceControl); if (updateDestinationFrame) { @@ -650,6 +640,9 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { // add to shadow queue mNumFrameAvailable++; + if (mWaitForTransactionCallback && mNumFrameAvailable == 2) { + acquireAndReleaseBuffer(); + } ATRACE_INT(mQueuedBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); @@ -658,6 +651,13 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { if (syncTransactionSet) { acquireNextBufferLocked(mSyncTransaction); + + // Only need a commit callback when syncing to ensure the buffer that's synced has been sent + // to SF + incStrong((void*)transactionCommittedCallbackThunk); + mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, + static_cast<void*>(this)); + if (mAcquireSingleBuffer) { mSyncTransaction = nullptr; } diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 8edf60400c..a62697064b 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -71,6 +71,7 @@ static bool isPossiblyYUV(PixelFormat format) { case HAL_PIXEL_FORMAT_Y8: case HAL_PIXEL_FORMAT_Y16: case HAL_PIXEL_FORMAT_RAW16: + case HAL_PIXEL_FORMAT_RAW12: case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW_OPAQUE: case HAL_PIXEL_FORMAT_BLOB: diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 837967551d..ee8008270e 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -182,7 +182,6 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, outVsyncEventData->id = ev.vsync.vsyncId; outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp; outVsyncEventData->frameInterval = ev.vsync.frameInterval; - outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp; outVsyncEventData->preferredFrameTimelineIndex = ev.vsync.preferredFrameTimelineIndex; populateFrameTimelines(ev, outVsyncEventData); diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index 03b33c7330..b916e48f79 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -19,7 +19,6 @@ #include <utils/Errors.h> #include <gui/DisplayEventReceiver.h> -#include <gui/IDisplayEventConnection.h> #include <gui/ISurfaceComposer.h> #include <private/gui/ComposerService.h> diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp deleted file mode 100644 index c0e246fa15..0000000000 --- a/libs/gui/IDisplayEventConnection.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <gui/IDisplayEventConnection.h> - -#include <private/gui/BitTube.h> - -namespace android { - -namespace { // Anonymous - -enum class Tag : uint32_t { - STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, - SET_VSYNC_RATE, - REQUEST_NEXT_VSYNC, - LAST = REQUEST_NEXT_VSYNC, -}; - -} // Anonymous namespace - -class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> { -public: - explicit BpDisplayEventConnection(const sp<IBinder>& impl) - : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {} - - ~BpDisplayEventConnection() override; - - status_t stealReceiveChannel(gui::BitTube* outChannel) override { - return callRemote<decltype( - &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL, - outChannel); - } - - status_t setVsyncRate(uint32_t count) override { - return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE, - count); - } - - void requestNextVsync() override { - callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>( - Tag::REQUEST_NEXT_VSYNC); - } -}; - -// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see -// clang warning -Wweak-vtables) -BpDisplayEventConnection::~BpDisplayEventConnection() = default; - -IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection"); - -status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { - return BBinder::onTransact(code, data, reply, flags); - } - auto tag = static_cast<Tag>(code); - switch (tag) { - case Tag::STEAL_RECEIVE_CHANNEL: - return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel); - case Tag::SET_VSYNC_RATE: - return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate); - case Tag::REQUEST_NEXT_VSYNC: - return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync); - } -} - -} // namespace android diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp deleted file mode 100644 index 40cbfceaf7..0000000000 --- a/libs/gui/IRegionSamplingListener.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2019 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 "IRegionSamplingListener" -//#define LOG_NDEBUG 0 - -#include <gui/IRegionSamplingListener.h> - -namespace android { - -namespace { // Anonymous - -enum class Tag : uint32_t { - ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION, - LAST = ON_SAMPLE_COLLECTED, -}; - -} // Anonymous namespace - -class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> { -public: - explicit BpRegionSamplingListener(const sp<IBinder>& impl) - : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {} - - ~BpRegionSamplingListener() override; - - void onSampleCollected(float medianLuma) override { - callRemoteAsync<decltype( - &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma); - } -}; - -// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see -// clang warning -Wweak-vtables) -BpRegionSamplingListener::~BpRegionSamplingListener() = default; - -IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener"); - -status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { - return BBinder::onTransact(code, data, reply, flags); - } - auto tag = static_cast<Tag>(code); - switch (tag) { - case Tag::ON_SAMPLE_COLLECTED: - return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected); - } -} - -} // namespace android diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 02950995af..7f73013f6e 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -17,13 +17,13 @@ // tag as surfaceflinger #define LOG_TAG "SurfaceFlinger" +#include <android/gui/IDisplayEventConnection.h> +#include <android/gui/IRegionSamplingListener.h> #include <android/gui/ITransactionTraceListener.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/Parcel.h> -#include <gui/IDisplayEventConnection.h> #include <gui/IGraphicBufferProducer.h> -#include <gui/IRegionSamplingListener.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> #include <gui/LayerDebugInfo.h> @@ -44,6 +44,8 @@ namespace android { +using gui::IDisplayEventConnection; +using gui::IRegionSamplingListener; using gui::IWindowInfosListener; using ui::ColorMode; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index f848e4ffde..ec0573a1a9 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -502,6 +502,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eDropInputModeChanged; dropInputMode = other.dropInputMode; } + if (other.what & eColorChanged) { + what |= eColorChanged; + color = other.color; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64, @@ -546,9 +550,7 @@ bool InputWindowCommands::merge(const InputWindowCommands& other) { } bool InputWindowCommands::empty() const { - bool empty = true; - empty = focusRequests.empty() && !syncInputWindows; - return empty; + return focusRequests.empty() && !syncInputWindows; } void InputWindowCommands::clear() { diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index b139cf126c..e1fe26aa7f 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -53,6 +53,7 @@ namespace android { using gui::FocusRequest; +using gui::IRegionSamplingListener; using gui::WindowInfo; using gui::WindowInfoHandle; using gui::WindowInfosListener; @@ -352,6 +353,10 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener // through all until the SC is found. int32_t layerId = -1; for (auto callbackId : transactionStats.callbackIds) { + if (callbackId.type != CallbackId::Type::ON_COMPLETE) { + // We only want to run the stats callback for ON_COMPLETE + continue; + } sp<SurfaceControl> sc = callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]; if (sc != nullptr) { @@ -360,7 +365,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener } } - { + if (layerId != -1) { // Acquire surface stats listener lock such that we guarantee that after calling // unregister, there won't be any further callback. std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex); diff --git a/libs/gui/aidl/android/gui/BitTube.aidl b/libs/gui/aidl/android/gui/BitTube.aidl new file mode 100644 index 0000000000..6b0595ec66 --- /dev/null +++ b/libs/gui/aidl/android/gui/BitTube.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +parcelable BitTube cpp_header "private/gui/BitTube.h"; diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl index cff22a368a..9f41593539 100644 --- a/libs/gui/include/gui/IDisplayEventConnection.h +++ b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,52 +14,28 @@ * limitations under the License. */ -#pragma once +package android.gui; -#include <binder/IInterface.h> -#include <binder/SafeInterface.h> -#include <gui/ISurfaceComposer.h> -#include <utils/Errors.h> - -#include <cstdint> - -namespace android { - -namespace gui { -class BitTube; -} // namespace gui - -class IDisplayEventConnection : public IInterface { -public: - DECLARE_META_INTERFACE(DisplayEventConnection) +import android.gui.BitTube; +/** @hide */ +interface IDisplayEventConnection { /* * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file * descriptor of outChannel will be initialized, and this effectively "steals" the receive * channel from the remote end (such that the remote end can only use its send channel). */ - virtual status_t stealReceiveChannel(gui::BitTube* outChannel) = 0; + void stealReceiveChannel(out BitTube outChannel); /* * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event. * A value of 2 returns every other event, etc. A value of 0 returns no event unless * requestNextVsync() has been called. */ - virtual status_t setVsyncRate(uint32_t count) = 0; + void setVsyncRate(in int count); /* * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0. */ - virtual void requestNextVsync() = 0; // Asynchronous -}; - -class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> { -public: - BnDisplayEventConnection() - : SafeBnInterface<IDisplayEventConnection>("BnDisplayEventConnection") {} - - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0) override; -}; - -} // namespace android + oneway void requestNextVsync(); // Asynchronous +} diff --git a/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl new file mode 100644 index 0000000000..00a3959d79 --- /dev/null +++ b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl @@ -0,0 +1,22 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +oneway interface IRegionSamplingListener { + void onSampleCollected(float medianLuma); +} diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index d76617373a..8a2f392e35 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -74,6 +74,7 @@ class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener { public: + BLASTBufferQueue(const std::string& name); BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, int height, int32_t format); diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index 8a3a47644c..40621ddb15 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -35,9 +35,6 @@ struct VsyncEventData { // The current frame interval in ns when this frame was scheduled. int64_t frameInterval = 0; - // The anticipated Vsync present time. - int64_t expectedPresentTime = 0; - struct FrameTimeline { // The Vsync Id corresponsing to this vsync event. This will be used to // populate ISurfaceComposer::setFrameTimelineVsync and diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index ca368433b1..456bbfb611 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -33,7 +33,7 @@ namespace android { // ---------------------------------------------------------------------------- -class IDisplayEventConnection; +using gui::IDisplayEventConnection; namespace gui { class BitTube; diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h deleted file mode 100644 index 1803d9a1da..0000000000 --- a/libs/gui/include/gui/IRegionSamplingListener.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2019 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 <cstdint> -#include <vector> - -#include <binder/IInterface.h> -#include <binder/SafeInterface.h> - -namespace android { - -class IRegionSamplingListener : public IInterface { -public: - DECLARE_META_INTERFACE(RegionSamplingListener) - - virtual void onSampleCollected(float medianLuma) = 0; -}; - -class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> { -public: - BnRegionSamplingListener() - : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {} - - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0) override; -}; - -} // namespace android diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index e0183adbe6..2546e4c753 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -17,8 +17,10 @@ #pragma once #include <android/gui/DisplayBrightness.h> +#include <android/gui/IDisplayEventConnection.h> #include <android/gui/IFpsListener.h> #include <android/gui/IHdrLayerInfoListener.h> +#include <android/gui/IRegionSamplingListener.h> #include <android/gui/IScreenCaptureListener.h> #include <android/gui/ITransactionTraceListener.h> #include <android/gui/ITunnelModeEnabledListener.h> @@ -60,13 +62,13 @@ struct InputWindowCommands; struct LayerCaptureArgs; class LayerDebugInfo; class HdrCapabilities; -class IDisplayEventConnection; class IGraphicBufferProducer; class ISurfaceComposerClient; -class IRegionSamplingListener; class Rect; enum class FrameEvent; +using gui::IDisplayEventConnection; +using gui::IRegionSamplingListener; using gui::IScreenCaptureListener; namespace ui { diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index de14b3d785..27f4d379e9 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -59,4 +59,14 @@ struct LayerMetadata : public Parcelable { std::string itemToString(uint32_t key, const char* separator) const; }; +// Keep in sync with the GameManager.java constants. +enum class GameMode : int32_t { + Unsupported = 0, + Standard = 1, + Performance = 2, + Battery = 3, + + ftl_last = Battery +}; + } // namespace android diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index e05c3646c6..0fe1253f94 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -51,10 +51,11 @@ namespace android { class HdrCapabilities; class ISurfaceComposerClient; class IGraphicBufferProducer; -class IRegionSamplingListener; class ITunnelModeEnabledListener; class Region; +using gui::IRegionSamplingListener; + struct SurfaceControlStats { SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime, const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence, diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index d9beb23019..f960e07d2f 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -936,6 +936,20 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { EXPECT_EQ(surface->consumeEvent(100), nullptr); } +TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + + std::unique_ptr<BlastInputSurface> bufferSurface = + BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); + + surface->showAt(100, 100); + bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200)); + bufferSurface->showAt(100, 100, Rect::EMPTY_RECT); + + injectTap(101, 101); + surface->expectTap(1, 1); +} + TEST_F(InputSurfacesTest, drop_input_policy) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->doTransaction( diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp index 6746b0a827..c9106bed4c 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -17,9 +17,9 @@ #include <gtest/gtest.h> #include <thread> +#include <android/gui/BnRegionSamplingListener.h> #include <binder/ProcessState.h> #include <gui/DisplayEventReceiver.h> -#include <gui/IRegionSamplingListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -135,12 +135,13 @@ private: std::atomic<bool> poll_{true}; }; -struct Listener : BnRegionSamplingListener { - void onSampleCollected(float medianLuma) override { +struct Listener : android::gui::BnRegionSamplingListener { + binder::Status onSampleCollected(float medianLuma) override { std::unique_lock<decltype(mutex)> lk(mutex); received = true; mLuma = medianLuma; cv.notify_all(); + return binder::Status::ok(); }; bool wait_event(std::chrono::milliseconds timeout) { std::unique_lock<decltype(mutex)> lk(mutex); diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp index 0cd150d3cb..a083a228a6 100644 --- a/libs/gui/tests/SamplingDemo.cpp +++ b/libs/gui/tests/SamplingDemo.cpp @@ -20,9 +20,9 @@ #include <chrono> #include <thread> +#include <android/gui/BnRegionSamplingListener.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> -#include <gui/IRegionSamplingListener.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> @@ -33,7 +33,7 @@ using namespace std::chrono_literals; namespace android { -class Button : public BnRegionSamplingListener { +class Button : public gui::BnRegionSamplingListener { public: Button(const char* name, const Rect& samplingArea) { sp<SurfaceComposerClient> client = new SurfaceComposerClient; @@ -99,9 +99,10 @@ private: .apply(); } - void onSampleCollected(float medianLuma) override { + binder::Status onSampleCollected(float medianLuma) override { ATRACE_CALL(); setColor(medianLuma); + return binder::Status::ok(); } sp<SurfaceComposerClient> mClient; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index b2baea6aba..d6ac3f9745 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -19,11 +19,11 @@ #include <gtest/gtest.h> #include <SurfaceFlingerProperties.h> +#include <android/gui/IDisplayEventConnection.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <binder/ProcessState.h> #include <configstore/Utils.h> #include <gui/BufferItemConsumer.h> -#include <gui/IDisplayEventConnection.h> #include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> @@ -45,6 +45,8 @@ using namespace std::chrono_literals; // retrieve wide-color and hdr settings from configstore using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; +using gui::IDisplayEventConnection; +using gui::IRegionSamplingListener; using ui::ColorMode; using Transaction = SurfaceComposerClient::Transaction; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 24a77209c4..44487c3a85 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -24,6 +24,7 @@ #include <android-base/stringprintf.h> #include <gui/constants.h> +#include <input/DisplayViewport.h> #include <input/Input.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> @@ -60,16 +61,6 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { return atan2f(transformedPoint.x, -transformedPoint.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; -} - -bool isFromSource(uint32_t source, uint32_t test) { - return (source & test) == test; -} - bool shouldDisregardTransformation(uint32_t source) { // Do not apply any transformations to axes from joysticks or touchpads. return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || @@ -124,6 +115,12 @@ int32_t IdGenerator::nextId() const { // --- InputEvent --- +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; +} + const char* inputEventTypeToString(int32_t type) { switch (type) { case AINPUT_EVENT_TYPE_KEY: { @@ -148,6 +145,49 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } +std::string inputEventSourceToString(int32_t source) { + if (source == AINPUT_SOURCE_UNKNOWN) { + return "UNKNOWN"; + } + if (source == static_cast<int32_t>(AINPUT_SOURCE_ANY)) { + return "ANY"; + } + static const std::map<int32_t, const char*> SOURCES{ + {AINPUT_SOURCE_KEYBOARD, "KEYBOARD"}, + {AINPUT_SOURCE_DPAD, "DPAD"}, + {AINPUT_SOURCE_GAMEPAD, "GAMEPAD"}, + {AINPUT_SOURCE_TOUCHSCREEN, "TOUCHSCREEN"}, + {AINPUT_SOURCE_MOUSE, "MOUSE"}, + {AINPUT_SOURCE_STYLUS, "STYLUS"}, + {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BLUETOOTH_STYLUS"}, + {AINPUT_SOURCE_TRACKBALL, "TRACKBALL"}, + {AINPUT_SOURCE_MOUSE_RELATIVE, "MOUSE_RELATIVE"}, + {AINPUT_SOURCE_TOUCHPAD, "TOUCHPAD"}, + {AINPUT_SOURCE_TOUCH_NAVIGATION, "TOUCH_NAVIGATION"}, + {AINPUT_SOURCE_JOYSTICK, "JOYSTICK"}, + {AINPUT_SOURCE_HDMI, "HDMI"}, + {AINPUT_SOURCE_SENSOR, "SENSOR"}, + {AINPUT_SOURCE_ROTARY_ENCODER, "ROTARY_ENCODER"}, + }; + std::string result; + for (const auto& [source_entry, str] : SOURCES) { + if ((source & source_entry) == source_entry) { + if (!result.empty()) { + result += " | "; + } + result += str; + } + } + if (result.empty()) { + result = StringPrintf("0x%08x", source); + } + return result; +} + +bool isFromSource(uint32_t source, uint32_t test) { + return (source & test) == test; +} + VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), event.getSource(), event.getDisplayId()}, @@ -467,6 +507,24 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +int MotionEvent::getSurfaceRotation() const { + // The surface rotation is the rotation from the window's coordinate space to that of the + // display. Since the event's transform takes display space coordinates to window space, the + // returned surface rotation is the inverse of the rotation for the surface. + switch (mTransform.getOrientation()) { + case ui::Transform::ROT_0: + return DISPLAY_ORIENTATION_0; + case ui::Transform::ROT_90: + return DISPLAY_ORIENTATION_270; + case ui::Transform::ROT_180: + return DISPLAY_ORIENTATION_180; + case ui::Transform::ROT_270: + return DISPLAY_ORIENTATION_90; + default: + return -1; + } +} + float MotionEvent::getXCursorPosition() const { vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); return vals.x; @@ -820,17 +878,15 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, // --- FocusEvent --- -void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { +void FocusEvent::initialize(int32_t id, bool hasFocus) { InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; - mInTouchMode = inTouchMode; } void FocusEvent::initialize(const FocusEvent& from) { InputEvent::initialize(from); mHasFocus = from.mHasFocus; - mInTouchMode = from.mInTouchMode; } // --- CaptureEvent --- diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 015bd81361..ac84627b3f 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -208,10 +208,8 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( int32_t axis, uint32_t source) const { - size_t numRanges = mMotionRanges.size(); - for (size_t i = 0; i < numRanges; i++) { - const MotionRange& range = mMotionRanges[i]; - if (range.axis == axis && range.source == source) { + for (const MotionRange& range : mMotionRanges) { + if (range.axis == axis && isFromSource(range.source, source)) { return ⦥ } } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 02a5a0807b..a065ce25f7 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -278,7 +278,6 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::FOCUS: { msg->body.focus.eventId = body.focus.eventId; msg->body.focus.hasFocus = body.focus.hasFocus; - msg->body.focus.inTouchMode = body.focus.inTouchMode; break; } case InputMessage::Type::CAPTURE: { @@ -622,13 +621,10 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } -status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, - bool inTouchMode) { +status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) { if (ATRACE_ENABLED()) { - std::string message = - StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)", - mChannel->getName().c_str(), toString(hasFocus), - toString(inTouchMode)); + std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)", + mChannel->getName().c_str(), toString(hasFocus)); ATRACE_NAME(message.c_str()); } @@ -637,7 +633,6 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool h msg.header.seq = seq; msg.body.focus.eventId = eventId; msg.body.focus.hasFocus = hasFocus; - msg.body.focus.inTouchMode = inTouchMode; return mChannel->sendMessage(&msg); } @@ -1371,8 +1366,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus, - msg->body.focus.inTouchMode); + event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus); } void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) { @@ -1491,9 +1485,8 @@ std::string InputConsumer::dump() const { break; } case InputMessage::Type::FOCUS: { - out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s", - toString(msg.body.focus.hasFocus), - toString(msg.body.focus.inTouchMode)); + out += android::base::StringPrintf("hasFocus=%s", + toString(msg.body.focus.hasFocus)); break; } case InputMessage::Type::CAPTURE: { diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 973194c8f6..05bc0bcbe8 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -290,10 +290,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { constexpr uint32_t seq = 15; int32_t eventId = InputEvent::nextId(); constexpr bool hasFocus = true; - constexpr bool inTouchMode = true; const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); - status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode); + status = mPublisher->publishFocusEvent(seq, eventId, hasFocus); ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK"; uint32_t consumeSeq; @@ -309,7 +308,6 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(eventId, focusEvent->getId()); EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); - EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 2f88704d63..b6a94764e5 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -84,8 +84,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); - CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5); - CHECK_OFFSET(InputMessage::Body::Focus, empty, 6); + CHECK_OFFSET(InputMessage::Body::Focus, empty, 5); CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0); CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4); diff --git a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h index 35ae3d2144..6fd4b8fff4 100644 --- a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h +++ b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h @@ -42,7 +42,8 @@ public: typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle); sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace, - bool* outQueueEmpty, SurfaceTexture& cb, + HdrMetadata* outHdrMetadata, bool* outQueueEmpty, + SurfaceTexture& cb, SurfaceTexture_createReleaseFence createFence, SurfaceTexture_fenceWait fenceWait, void* fencePassThroughHandle); diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index bac44c978a..0f119f3fc1 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -272,8 +272,8 @@ public: status_t attachToContext(uint32_t tex); sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace, - float* outTransformMatrix, uint32_t* outTransform, - bool* outQueueEmpty, + HdrMetadata* outHdrMetadata, float* outTransformMatrix, + uint32_t* outTransform, bool* outQueueEmpty, SurfaceTexture_createReleaseFence createFence, SurfaceTexture_fenceWait fenceWait, void* fencePassThroughHandle, ARect* currentCrop); diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h index e85009c206..2987f3a87a 100644 --- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h +++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h @@ -19,6 +19,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <android/hdr_metadata.h> #include <jni.h> #include <system/graphics.h> @@ -82,13 +83,12 @@ typedef int (*ASurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle * The caller gets ownership of the buffer and need to release it with * AHardwareBuffer_release. */ -AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid, - android_dataspace* outDataspace, - float* outTransformMatrix, uint32_t* outTransform, - bool* outNewContent, - ASurfaceTexture_createReleaseFence createFence, - ASurfaceTexture_fenceWait fenceWait, - void* fencePassThroughHandle, ARect* currentCrop); +AHardwareBuffer* ASurfaceTexture_dequeueBuffer( + ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace, + AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3, + android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform, + bool* outNewContent, ASurfaceTexture_createReleaseFence createFence, + ASurfaceTexture_fenceWait fenceWait, void* fencePassThroughHandle, ARect* currentCrop); } // namespace android diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index 365e788ea6..cf16739e89 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -28,7 +28,8 @@ void ImageConsumer::onReleaseBufferLocked(int buf) { } sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace, - bool* outQueueEmpty, SurfaceTexture& st, + HdrMetadata* outHdrMetadata, bool* outQueueEmpty, + SurfaceTexture& st, SurfaceTexture_createReleaseFence createFence, SurfaceTexture_fenceWait fenceWait, void* fencePassThroughHandle) { @@ -121,6 +122,7 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace st.computeCurrentTransformMatrixLocked(); *outDataspace = item.mDataSpace; + *outHdrMetadata = item.mHdrMetadata; *outSlotid = slot; return st.mSlots[slot].mGraphicBuffer; } diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index 3535e67895..d3d4cbafdf 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -464,6 +464,7 @@ void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { } sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace, + HdrMetadata* outHdrMetadata, float* outTransformMatrix, uint32_t* outTransform, bool* outQueueEmpty, SurfaceTexture_createReleaseFence createFence, @@ -482,8 +483,8 @@ sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspac return buffer; } - buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this, - createFence, fenceWait, fencePassThroughHandle); + buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outHdrMetadata, outQueueEmpty, + *this, createFence, fenceWait, fencePassThroughHandle); memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); *outTransform = mCurrentTransform; *currentCrop = mCurrentCrop; diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp index cc0a12d2c6..39a925f712 100644 --- a/libs/nativedisplay/surfacetexture/surface_texture.cpp +++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp @@ -192,20 +192,23 @@ void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) { texture->consumer->releaseConsumerOwnership(); } -AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid, - android_dataspace* outDataspace, - float* outTransformMatrix, uint32_t* outTransform, - bool* outNewContent, - ASurfaceTexture_createReleaseFence createFence, - ASurfaceTexture_fenceWait fenceWait, void* handle, - ARect* currentCrop) { +AHardwareBuffer* ASurfaceTexture_dequeueBuffer( + ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace, + AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3, + android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform, + bool* outNewContent, ASurfaceTexture_createReleaseFence createFence, + ASurfaceTexture_fenceWait fenceWait, void* handle, ARect* currentCrop) { sp<GraphicBuffer> buffer; *outNewContent = false; bool queueEmpty; do { - buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix, + HdrMetadata metadata; + buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, &metadata, outTransformMatrix, outTransform, &queueEmpty, createFence, fenceWait, handle, currentCrop); + *outHdrType = static_cast<AHdrMetadataType>(metadata.validTypes); + *outCta861_3 = metadata.cta8613; + *outSmpte2086 = metadata.smpte2086; if (!queueEmpty) { *outNewContent = true; } diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index e2f32e374a..cb3361b431 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -30,7 +30,7 @@ #include <private/android/AHardwareBufferHelpers.h> #include <android/hardware/graphics/common/1.1/types.h> - +#include <aidl/android/hardware/graphics/common/PixelFormat.h> static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints @@ -370,7 +370,7 @@ int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) { if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0; bool supported = false; - GraphicBuffer* gBuffer = new GraphicBuffer(); + sp<GraphicBuffer> gBuffer(new GraphicBuffer()); status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers, desc->usage, &supported); @@ -588,8 +588,12 @@ bool AHardwareBuffer_isValidPixelFormat(uint32_t format) { "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I, "HAL and AHardwareBuffer pixel format don't match"); + static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) == + AHARDWAREBUFFER_FORMAT_R8_UNORM, + "HAL and AHardwareBuffer pixel format don't match"); switch (format) { + case AHARDWAREBUFFER_FORMAT_R8_UNORM: case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: @@ -641,6 +645,8 @@ bool AHardwareBuffer_formatIsYuv(uint32_t format) { uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) { switch (format) { + case AHARDWAREBUFFER_FORMAT_R8_UNORM: + return 1; case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: case AHARDWAREBUFFER_FORMAT_D16_UNORM: return 2; diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 93e7239783..c447d31606 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -162,6 +162,8 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB)); static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ)); + static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_PQ) == + static_cast<int>(HAL_DATASPACE_BT2020_ITU_PQ)); static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB)); static_assert(static_cast<int>(ADATASPACE_JFIF) == static_cast<int>(HAL_DATASPACE_V0_JFIF)); static_assert(static_cast<int>(ADATASPACE_BT601_625) == static_cast<int>(HAL_DATASPACE_V0_BT601_625)); @@ -170,6 +172,10 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709)); static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3)); static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR)); + static_assert(static_cast<int>(ADATASPACE_BT2020_HLG) == + static_cast<int>(HAL_DATASPACE_BT2020_HLG)); + static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_HLG) == + static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG)); if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || !isDataSpaceValid(window, dataSpace)) { diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 0565f424f2..30ac220052 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -444,6 +444,15 @@ enum ADataSpace { ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL /** + * ITU-R Recommendation 2020 (BT.2020) + * + * Ultra High-definition television + * + * Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard + */ + ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED + + /** * Adobe RGB * * Use full range, gamma 2.2 transfer and Adobe RGB primaries @@ -519,6 +528,20 @@ enum ADataSpace { * components. */ ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL + + /** + * Hybrid Log Gamma encoding: + * + * Use full range, hybrid log gamma transfer and BT2020 standard. + */ + ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL + + /** + * ITU Hybrid Log Gamma encoding: + * + * Use limited range, hybrid log gamma transfer and BT2020 standard. + */ + ADATASPACE_BT2020_ITU_HLG = 302383104 // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED }; __END_DECLS diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index d5e7cb299b..6f1f04df34 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -158,6 +158,13 @@ enum AHardwareBuffer_Format { * cube-maps or multi-layered textures. */ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23, + + /** + * Corresponding formats: + * Vulkan: VK_FORMAT_R8_UNORM + * OpenGL ES: GR_GL_R8 + */ + AHARDWAREBUFFER_FORMAT_R8_UNORM = 0x38, }; /** diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index ecfaef8928..07c5dd8a82 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -42,6 +42,7 @@ cc_defaults { ], static_libs: [ + "libshaders", "libtonemap", ], local_include_dirs: ["include"], diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 65d48951c6..c7ad058ab9 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -26,23 +26,7 @@ namespace android { namespace renderengine { -std::unique_ptr<RenderEngine> RenderEngine::create(RenderEngineCreationArgs args) { - // Keep the ability to override by PROPERTIES: - char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); - if (strcmp(prop, "gles") == 0) { - args.renderEngineType = RenderEngineType::GLES; - } - if (strcmp(prop, "threaded") == 0) { - args.renderEngineType = RenderEngineType::THREADED; - } - if (strcmp(prop, "skiagl") == 0) { - args.renderEngineType = RenderEngineType::SKIA_GL; - } - if (strcmp(prop, "skiaglthreaded") == 0) { - args.renderEngineType = RenderEngineType::SKIA_GL_THREADED; - } - +std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { switch (args.renderEngineType) { case RenderEngineType::THREADED: ALOGD("Threaded RenderEngine with GLES Backend"); diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp index baa50544b7..471159f390 100644 --- a/libs/renderengine/benchmark/Android.bp +++ b/libs/renderengine/benchmark/Android.bp @@ -35,6 +35,7 @@ cc_benchmark { ], static_libs: [ "librenderengine", + "libshaders", "libtonemap", ], cflags: [ diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 6b85c57069..b9cc6485e5 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -99,7 +99,7 @@ public: SKIA_GL_THREADED = 4, }; - static std::unique_ptr<RenderEngine> create(RenderEngineCreationArgs args); + static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); virtual ~RenderEngine() = 0; diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp index ff4d348f0d..f367a84946 100644 --- a/libs/renderengine/skia/ColorSpaces.cpp +++ b/libs/renderengine/skia/ColorSpaces.cpp @@ -20,6 +20,7 @@ namespace android { namespace renderengine { namespace skia { +// please keep in sync with hwui/utils/Color.cpp sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) { skcms_Matrix3x3 gamut; switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { @@ -32,6 +33,17 @@ sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) { case HAL_DATASPACE_STANDARD_DCI_P3: gamut = SkNamedGamut::kDisplayP3; break; + case HAL_DATASPACE_STANDARD_ADOBE_RGB: + gamut = SkNamedGamut::kAdobeRGB; + break; + case HAL_DATASPACE_STANDARD_BT601_625: + case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED: + case HAL_DATASPACE_STANDARD_BT601_525: + case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED: + case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE: + case HAL_DATASPACE_STANDARD_BT470M: + case HAL_DATASPACE_STANDARD_FILM: + case HAL_DATASPACE_STANDARD_UNSPECIFIED: default: gamut = SkNamedGamut::kSRGB; break; @@ -42,10 +54,19 @@ sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) { return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut); case HAL_DATASPACE_TRANSFER_SRGB: return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut); + case HAL_DATASPACE_TRANSFER_GAMMA2_2: + return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); + case HAL_DATASPACE_TRANSFER_GAMMA2_6: + return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); + case HAL_DATASPACE_TRANSFER_GAMMA2_8: + return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_ST2084: return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut); + case HAL_DATASPACE_TRANSFER_SMPTE_170M: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut); case HAL_DATASPACE_TRANSFER_HLG: return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut); + case HAL_DATASPACE_TRANSFER_UNSPECIFIED: default: return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut); } diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 21d56032c2..376e279473 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -636,9 +636,9 @@ sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> sh const ui::Dataspace outputDataspace = mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR; - LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace, - .outputDataspace = outputDataspace, - .undoPremultipliedAlpha = undoPremultipliedAlpha}; + auto effect = shaders::LinearEffect{.inputDataspace = inputDataspace, + .outputDataspace = outputDataspace, + .undoPremultipliedAlpha = undoPremultipliedAlpha}; auto effectIter = mRuntimeEffects.find(effect); sk_sp<SkRuntimeEffect> runtimeEffect = nullptr; diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index 74ce6513e9..53792f9731 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -133,7 +133,8 @@ private: // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts. std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache GUARDED_BY(mRenderingMutex); - std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects; + std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher> + mRuntimeEffects; AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex); StretchShaderFactory mStretchShaderFactory; diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp index 53136e40b7..36305aeea8 100644 --- a/libs/renderengine/skia/filters/LinearEffect.cpp +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -19,288 +19,19 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <SkString.h> -#include <tonemap/tonemap.h> +#include <log/log.h> +#include <shaders/shaders.h> #include <utils/Trace.h> -#include <optional> - -#include "log/log.h" -#include "math/mat4.h" -#include "system/graphics-base-v1.0.h" -#include "ui/ColorSpace.h" +#include <math/mat4.h> namespace android { namespace renderengine { namespace skia { -static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace( - ui::Dataspace dataspace) { - return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace); -} - -static void generateEOTF(ui::Dataspace dataspace, SkString& shader) { - switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { - case HAL_DATASPACE_TRANSFER_ST2084: - shader.append(R"( - - float3 EOTF(float3 color) { - float m1 = (2610.0 / 4096.0) / 4.0; - float m2 = (2523.0 / 4096.0) * 128.0; - float c1 = (3424.0 / 4096.0); - float c2 = (2413.0 / 4096.0) * 32.0; - float c3 = (2392.0 / 4096.0) * 32.0; - - float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2)); - tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp); - return pow(tmp, 1.0 / float3(m1)); - } - )"); - break; - case HAL_DATASPACE_TRANSFER_HLG: - shader.append(R"( - float EOTF_channel(float channel) { - const float a = 0.17883277; - const float b = 0.28466892; - const float c = 0.55991073; - return channel <= 0.5 ? channel * channel / 3.0 : - (exp((channel - c) / a) + b) / 12.0; - } - - float3 EOTF(float3 color) { - return float3(EOTF_channel(color.r), EOTF_channel(color.g), - EOTF_channel(color.b)); - } - )"); - break; - case HAL_DATASPACE_TRANSFER_LINEAR: - shader.append(R"( - float3 EOTF(float3 color) { - return color; - } - )"); - break; - case HAL_DATASPACE_TRANSFER_SRGB: - default: - shader.append(R"( - - float EOTF_sRGB(float srgb) { - return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4); - } - - float3 EOTF_sRGB(float3 srgb) { - return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); - } - - float3 EOTF(float3 srgb) { - return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); - } - )"); - break; - } -} - -static void generateXYZTransforms(SkString& shader) { - shader.append(R"( - uniform float4x4 in_rgbToXyz; - uniform float4x4 in_xyzToRgb; - float3 ToXYZ(float3 rgb) { - return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0); - } - - float3 ToRGB(float3 xyz) { - return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0); - } - )"); -} - -// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits]) -static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkString& shader) { - switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) { - case HAL_DATASPACE_TRANSFER_ST2084: - shader.append(R"( - float3 ScaleLuminance(float3 xyz) { - return xyz * 10000.0; - } - )"); - break; - case HAL_DATASPACE_TRANSFER_HLG: - shader.append(R"( - float3 ScaleLuminance(float3 xyz) { - return xyz * 1000.0 * pow(xyz.y, 0.2); - } - )"); - break; - default: - shader.append(R"( - float3 ScaleLuminance(float3 xyz) { - return xyz * in_libtonemap_inputMaxLuminance; - } - )"); - break; - } -} - -// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1]) -static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, SkString& shader) { - switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) { - case HAL_DATASPACE_TRANSFER_ST2084: - shader.append(R"( - float3 NormalizeLuminance(float3 xyz) { - return xyz / 10000.0; - } - )"); - break; - case HAL_DATASPACE_TRANSFER_HLG: - shader.append(R"( - float3 NormalizeLuminance(float3 xyz) { - return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2); - } - )"); - break; - default: - shader.append(R"( - float3 NormalizeLuminance(float3 xyz) { - return xyz / in_libtonemap_displayMaxLuminance; - } - )"); - break; - } -} - -static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, - SkString& shader) { - shader.append(tonemap::getToneMapper() - ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace), - toAidlDataspace(outputDataspace)) - .c_str()); - - generateLuminanceScalesForOOTF(inputDataspace, shader); - generateLuminanceNormalizationForOOTF(outputDataspace, shader); - - shader.append(R"( - float3 OOTF(float3 linearRGB, float3 xyz) { - float3 scaledLinearRGB = ScaleLuminance(linearRGB); - float3 scaledXYZ = ScaleLuminance(xyz); - - float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ); - - return NormalizeLuminance(scaledXYZ * gain); - } - )"); -} - -static void generateOETF(ui::Dataspace dataspace, SkString& shader) { - switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { - case HAL_DATASPACE_TRANSFER_ST2084: - shader.append(R"( - - float3 OETF(float3 xyz) { - float m1 = (2610.0 / 4096.0) / 4.0; - float m2 = (2523.0 / 4096.0) * 128.0; - float c1 = (3424.0 / 4096.0); - float c2 = (2413.0 / 4096.0) * 32.0; - float c3 = (2392.0 / 4096.0) * 32.0; - - float3 tmp = pow(xyz, float3(m1)); - tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); - return pow(tmp, float3(m2)); - } - )"); - break; - case HAL_DATASPACE_TRANSFER_HLG: - shader.append(R"( - float OETF_channel(float channel) { - const float a = 0.17883277; - const float b = 0.28466892; - const float c = 0.55991073; - return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : - a * log(12.0 * channel - b) + c; - } - - float3 OETF(float3 linear) { - return float3(OETF_channel(linear.r), OETF_channel(linear.g), - OETF_channel(linear.b)); - } - )"); - break; - case HAL_DATASPACE_TRANSFER_LINEAR: - shader.append(R"( - float3 OETF(float3 linear) { - return linear; - } - )"); - break; - case HAL_DATASPACE_TRANSFER_SRGB: - default: - shader.append(R"( - float OETF_sRGB(float linear) { - return linear <= 0.0031308 ? - linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055; - } - - float3 OETF_sRGB(float3 linear) { - return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); - } - - float3 OETF(float3 linear) { - return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); - } - )"); - break; - } -} - -static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) { - shader.append(R"( - uniform shader child; - half4 main(float2 xy) { - float4 c = float4(child.eval(xy)); - )"); - if (undoPremultipliedAlpha) { - shader.append(R"( - c.rgb = c.rgb / (c.a + 0.0019); - )"); - } - shader.append(R"( - float3 linearRGB = EOTF(c.rgb); - float3 xyz = ToXYZ(linearRGB); - c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz))); - )"); - if (undoPremultipliedAlpha) { - shader.append(R"( - c.rgb = c.rgb * (c.a + 0.0019); - )"); - } - shader.append(R"( - return c; - } - )"); -} -static ColorSpace toColorSpace(ui::Dataspace dataspace) { - switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { - case HAL_DATASPACE_STANDARD_BT709: - return ColorSpace::sRGB(); - break; - case HAL_DATASPACE_STANDARD_DCI_P3: - return ColorSpace::DisplayP3(); - break; - case HAL_DATASPACE_STANDARD_BT2020: - return ColorSpace::BT2020(); - break; - default: - return ColorSpace::sRGB(); - break; - } -} - -sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) { +sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect) { ATRACE_CALL(); - SkString shaderString; - generateEOTF(linearEffect.inputDataspace, shaderString); - generateXYZTransforms(shaderString); - generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString); - generateOETF(linearEffect.outputDataspace, shaderString); - generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString); + SkString shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect)); auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString); if (!shader) { @@ -309,7 +40,8 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) { return shader; } -sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect, +sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, + const shaders::LinearEffect& linearEffect, sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance, float maxLuminance) { @@ -318,27 +50,8 @@ sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEff effectBuilder.child("child") = shader; - if (linearEffect.inputDataspace == linearEffect.outputDataspace) { - effectBuilder.uniform("in_rgbToXyz") = mat4(); - effectBuilder.uniform("in_xyzToRgb") = colorTransform; - } else { - ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace); - ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace); - - effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ()); - effectBuilder.uniform("in_xyzToRgb") = - colorTransform * mat4(outputColorSpace.getXYZtoRGB()); - } - - tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance, - // If the input luminance is unknown, use display luminance (aka, - // no-op any luminance changes) - // This will be the case for eg screenshots in addition to - // uncalibrated displays - .contentMaxLuminance = - maxLuminance > 0 ? maxLuminance : maxDisplayLuminance}; - - const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata); + const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, colorTransform, + maxDisplayLuminance, maxLuminance); for (const auto& uniform : uniforms) { effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size()); diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h index 14a3b61ede..8eb6670150 100644 --- a/libs/renderengine/skia/filters/LinearEffect.h +++ b/libs/renderengine/skia/filters/LinearEffect.h @@ -20,6 +20,7 @@ #include <optional> +#include <shaders/shaders.h> #include "SkRuntimeEffect.h" #include "SkShader.h" #include "ui/GraphicTypes.h" @@ -28,61 +29,7 @@ namespace android { namespace renderengine { namespace skia { -/** - * Arguments for creating an effect that applies color transformations in linear XYZ space. - * A linear effect is decomposed into the following steps when operating on an image: - * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended - * relative display brightness of the scene in nits for each RGB channel - * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display - * luminance. - * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone - * mapping to display SDR content alongside HDR content, or any number of subjective transformations - * 4. Transformation matrix from linear XYZ back to linear RGB brightness. - * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to - * output RGB colors. - * - * For further reading, consult the recommendation in ITU-R BT.2390-4: - * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf - * - * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is - * intended to be the output surface. However, Skia does not support complex tone mapping such as - * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied - * to the source colors. so that the tone mapping process is only applied once by this effect. Tone - * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions) - * alongside other content, whereby maximum input luminance is mapped to maximum output luminance - * and intermediate values are interpolated. - */ -struct LinearEffect { - // Input dataspace of the source colors. - const ui::Dataspace inputDataspace = ui::Dataspace::SRGB; - - // Working dataspace for the output surface, for conversion from linear space. - const ui::Dataspace outputDataspace = ui::Dataspace::SRGB; - - // Sets whether alpha premultiplication must be undone. - // This is required if the source colors use premultiplied alpha and is not opaque. - const bool undoPremultipliedAlpha = false; -}; - -static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) { - return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace && - lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha; -} - -struct LinearEffectHasher { - // Inspired by art/runtime/class_linker.cc - // Also this is what boost:hash_combine does - static size_t HashCombine(size_t seed, size_t val) { - return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2)); - } - size_t operator()(const LinearEffect& le) const { - size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace); - result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace)); - return HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha)); - } -}; - -sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect); +sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect); // Generates a shader resulting from applying the a linear effect created from // LinearEffectArgs::buildEffect to an inputShader. @@ -93,7 +40,7 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect); // * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086 // or as the max light level from the CTA 861.3 standard. sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader, - const LinearEffect& linearEffect, + const shaders::LinearEffect& linearEffect, sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance, float maxLuminance); diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 52b6c8ff72..a426850350 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -39,6 +39,7 @@ cc_test { "libgmock", "librenderengine", "librenderengine_mocks", + "libshaders", "libtonemap", ], diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index c2c05f41b7..82590638a8 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -27,6 +27,9 @@ #include <renderengine/ExternalTexture.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> +#include <system/graphics-base-v1.0.h> +#include <tonemap/tonemap.h> +#include <ui/ColorSpace.h> #include <ui/PixelFormat.h> #include <chrono> @@ -282,6 +285,13 @@ public: void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { + auto generator = [=](Point) { return ubyte4(r, g, b, a); }; + expectBufferColor(rect, generator, tolerance); + } + + using ColorGenerator = std::function<ubyte4(Point location)>; + + void expectBufferColor(const Rect& rect, ColorGenerator generator, uint8_t tolerance = 0) { auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) { auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) { uint8_t tmp = a >= b ? a - b : b - a; @@ -290,10 +300,10 @@ public: return std::equal(colorA, colorA + 4, colorB, colorBitCompare); }; - expectBufferColor(rect, r, g, b, a, colorCompare); + expectBufferColor(rect, generator, colorCompare); } - void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, + void expectBufferColor(const Rect& region, ColorGenerator generator, std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) { uint8_t* pixels; mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -304,19 +314,22 @@ public: const uint8_t* src = pixels + (mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4; for (int32_t i = 0; i < region.getWidth(); i++) { - const uint8_t expected[4] = {r, g, b, a}; - bool equal = colorCompare(src, expected); - EXPECT_TRUE(equal) + const auto location = Point(region.left + i, region.top + j); + const ubyte4 colors = generator(location); + const uint8_t expected[4] = {colors.r, colors.g, colors.b, colors.a}; + bool colorMatches = colorCompare(src, expected); + EXPECT_TRUE(colorMatches) << GetParam()->name().c_str() << ": " - << "pixel @ (" << region.left + i << ", " << region.top + j << "): " - << "expected (" << static_cast<uint32_t>(r) << ", " - << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", " - << static_cast<uint32_t>(a) << "), " + << "pixel @ (" << location.x << ", " << location.y << "): " + << "expected (" << static_cast<uint32_t>(colors.r) << ", " + << static_cast<uint32_t>(colors.g) << ", " + << static_cast<uint32_t>(colors.b) << ", " + << static_cast<uint32_t>(colors.a) << "), " << "got (" << static_cast<uint32_t>(src[0]) << ", " << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2]) << ", " << static_cast<uint32_t>(src[3]) << ")"; src += 4; - if (!equal && ++fails >= maxFails) { + if (!colorMatches && ++fails >= maxFails) { break; } } @@ -328,10 +341,11 @@ public: } void expectAlpha(const Rect& rect, uint8_t a) { + auto generator = [=](Point) { return ubyte4(0, 0, 0, a); }; auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) { return colorA[3] == colorB[3]; }; - expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare); + expectBufferColor(rect, generator, colorCompare); } void expectShadowColor(const renderengine::LayerSettings& castingLayer, @@ -490,6 +504,18 @@ public: void fillBufferColorTransform(); template <typename SourceVariant> + void fillBufferWithColorTransformAndSourceDataspace(const ui::Dataspace sourceDataspace); + + template <typename SourceVariant> + void fillBufferColorTransformAndSourceDataspace(); + + template <typename SourceVariant> + void fillBufferWithColorTransformAndOutputDataspace(const ui::Dataspace outputDataspace); + + template <typename SourceVariant> + void fillBufferColorTransformAndOutputDataspace(); + + template <typename SourceVariant> void fillBufferWithColorTransformZeroLayerAlpha(); template <typename SourceVariant> @@ -867,12 +893,104 @@ void RenderEngineTest::fillBufferWithColorTransform() { } template <typename SourceVariant> +void RenderEngineTest::fillBufferWithColorTransformAndSourceDataspace( + const ui::Dataspace sourceDataspace) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = Rect(1, 1); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.sourceDataspace = sourceDataspace; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this); + layer.alpha = 1.0f; + + // construct a fake color matrix + // annihilate green and blue channels + settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1)); + // set red channel to red + green + layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + + layer.alpha = 1.0f; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + + layers.push_back(layer); + + invokeDraw(settings, layers); +} + +template <typename SourceVariant> void RenderEngineTest::fillBufferColorTransform() { fillBufferWithColorTransform<SourceVariant>(); expectBufferColor(fullscreenRect(), 172, 0, 0, 255, 1); } template <typename SourceVariant> +void RenderEngineTest::fillBufferColorTransformAndSourceDataspace() { + unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap; + dataspaceToColorMap[ui::Dataspace::V0_BT709] = {172, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::BT2020] = {172, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {172, 0, 0, 255}; + ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>( + ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_2 | + ui::Dataspace::RANGE_FULL); + dataspaceToColorMap[customizedDataspace] = {172, 0, 0, 255}; + for (const auto& [sourceDataspace, color] : dataspaceToColorMap) { + fillBufferWithColorTransformAndSourceDataspace<SourceVariant>(sourceDataspace); + expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1); + } +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferWithColorTransformAndOutputDataspace( + const ui::Dataspace outputDataspace) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = Rect(1, 1); + settings.outputDataspace = outputDataspace; + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this); + layer.alpha = 1.0f; + + // construct a fake color matrix + // annihilate green and blue channels + settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1)); + // set red channel to red + green + layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + + layer.alpha = 1.0f; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + + layers.push_back(layer); + + invokeDraw(settings, layers); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferColorTransformAndOutputDataspace() { + unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap; + dataspaceToColorMap[ui::Dataspace::V0_BT709] = {202, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::BT2020] = {192, 0, 0, 255}; + dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {202, 0, 0, 255}; + ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>( + ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_6 | + ui::Dataspace::RANGE_FULL); + dataspaceToColorMap[customizedDataspace] = {202, 0, 0, 255}; + for (const auto& [outputDataspace, color] : dataspaceToColorMap) { + fillBufferWithColorTransformAndOutputDataspace<SourceVariant>(outputDataspace); + expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1); + } +} + +template <typename SourceVariant> void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); @@ -1099,7 +1217,7 @@ void RenderEngineTest::fillRedBufferTextureTransform() { layer.source.buffer.buffer = buf; layer.source.buffer.textureName = texName; // Transform coordinates to only be inside the red quadrant. - layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1)); + layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f)); layer.alpha = 1.0f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); @@ -1281,7 +1399,8 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { settings.clip = fullscreenRect(); // 255, 255, 255, 255 is full opaque white. - const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f); + const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), + static_cast<uint8_t>(255), static_cast<uint8_t>(255)); // Create layer with given color. renderengine::LayerSettings bgLayer; bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; @@ -1412,6 +1531,36 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { fillBufferColorTransform<ColorSourceVariant>(); } +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { + const auto& renderEngineFactory = GetParam(); + // skip for non color management + if (!renderEngineFactory->useColorManagement()) { + return; + } + // skip for GLESRenderEngine + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + return; + } + + initializeRenderEngine(); + fillBufferColorTransformAndSourceDataspace<ColorSourceVariant>(); +} + +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { + const auto& renderEngineFactory = GetParam(); + // skip for non color management + if (!renderEngineFactory->useColorManagement()) { + return; + } + // skip for GLESRenderEngine + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + return; + } + + initializeRenderEngine(); + fillBufferColorTransformAndOutputDataspace<ColorSourceVariant>(); +} + TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { initializeRenderEngine(); fillBufferWithRoundedCorners<ColorSourceVariant>(); @@ -1492,6 +1641,36 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) { + const auto& renderEngineFactory = GetParam(); + // skip for non color management + if (!renderEngineFactory->useColorManagement()) { + return; + } + // skip for GLESRenderEngine + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + return; + } + + initializeRenderEngine(); + fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) { + const auto& renderEngineFactory = GetParam(); + // skip for non color management + if (!renderEngineFactory->useColorManagement()) { + return; + } + // skip for GLESRenderEngine + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + return; + } + + initializeRenderEngine(); + fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { initializeRenderEngine(); fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); @@ -1572,6 +1751,36 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) { + const auto& renderEngineFactory = GetParam(); + // skip for non color management + if (!renderEngineFactory->useColorManagement()) { + return; + } + // skip for GLESRenderEngine + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + return; + } + + initializeRenderEngine(); + fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) { + const auto& renderEngineFactory = GetParam(); + // skip for non color management + if (!renderEngineFactory->useColorManagement()) { + return; + } + // skip for GLESRenderEngine + if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) { + return; + } + + initializeRenderEngine(); + fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { initializeRenderEngine(); fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); @@ -1615,7 +1824,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { initializeRenderEngine(); - const ubyte4 backgroundColor(255, 255, 255, 255); + const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), + static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); @@ -1630,8 +1840,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { initializeRenderEngine(); - const ubyte4 casterColor(255, 0, 0, 255); - const ubyte4 backgroundColor(255, 255, 255, 255); + const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), + static_cast<uint8_t>(0), static_cast<uint8_t>(255)); + const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), + static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(1, 1); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); @@ -1649,8 +1861,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { initializeRenderEngine(); - const ubyte4 casterColor(255, 0, 0, 255); - const ubyte4 backgroundColor(255, 255, 255, 255); + const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), + static_cast<uint8_t>(0), static_cast<uint8_t>(255)); + const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), + static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); @@ -1669,8 +1883,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { initializeRenderEngine(); - const ubyte4 casterColor(255, 0, 0, 255); - const ubyte4 backgroundColor(255, 255, 255, 255); + const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), + static_cast<uint8_t>(0), static_cast<uint8_t>(255)); + const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), + static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); @@ -1690,8 +1906,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { initializeRenderEngine(); - const ubyte4 casterColor(255, 0, 0, 255); - const ubyte4 backgroundColor(255, 255, 255, 255); + const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), + static_cast<uint8_t>(0), static_cast<uint8_t>(255)); + const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), + static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); @@ -2027,6 +2245,155 @@ TEST_P(RenderEngineTest, test_isOpaque) { expectBufferColor(rect, 0, 255, 0, 255); } } + +double EOTF_PQ(double channel) { + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2); + tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp); + return std::pow(tmp, 1.0 / m1); +} + +vec3 EOTF_PQ(vec3 color) { + return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b)); +} + +double OETF_sRGB(double channel) { + return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055; +} + +int sign(float in) { + return in >= 0.0 ? 1 : -1; +} + +vec3 OETF_sRGB(vec3 linear) { + return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g), + sign(linear.b) * OETF_sRGB(linear.b)); +} + +TEST_P(RenderEngineTest, test_tonemapPQMatches) { + if (!GetParam()->useColorManagement()) { + return; + } + + if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + return; + } + + initializeRenderEngine(); + + constexpr int32_t kGreyLevels = 256; + + const auto rect = Rect(0, 0, kGreyLevels, 1); + const renderengine::DisplaySettings display{ + .physicalDisplay = rect, + .clip = rect, + .maxLuminance = 750.0f, + .outputDataspace = ui::Dataspace::DISPLAY_P3, + }; + + auto buf = std::make_shared< + renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "input"), + *mRE, + renderengine::ExternalTexture::Usage::READABLE | + renderengine::ExternalTexture::Usage::WRITEABLE); + ASSERT_EQ(0, buf->getBuffer()->initCheck()); + + { + uint8_t* pixels; + buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + + uint8_t color = 0; + for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) { + uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4); + for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) { + dest[0] = color; + dest[1] = color; + dest[2] = color; + dest[3] = 255; + color++; + dest += 4; + } + } + buf->getBuffer()->unlock(); + } + + mBuffer = std::make_shared< + renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "output"), + *mRE, + renderengine::ExternalTexture::Usage::READABLE | + renderengine::ExternalTexture::Usage::WRITEABLE); + ASSERT_EQ(0, mBuffer->getBuffer()->initCheck()); + + const renderengine::LayerSettings layer{ + .geometry.boundaries = rect.toFloatRect(), + .source = + renderengine::PixelSource{ + .buffer = + renderengine::Buffer{ + .buffer = std::move(buf), + .usePremultipliedAlpha = true, + }, + }, + .alpha = 1.0f, + .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | + HAL_DATASPACE_TRANSFER_ST2084 | + HAL_DATASPACE_RANGE_FULL), + }; + + std::vector<renderengine::LayerSettings> layers{layer}; + invokeDraw(display, layers); + + ColorSpace displayP3 = ColorSpace::DisplayP3(); + ColorSpace bt2020 = ColorSpace::BT2020(); + + tonemap::Metadata metadata{.displayMaxLuminance = 750.0f}; + + auto generator = [=](Point location) { + const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1); + const vec3 rgb = vec3(normColor, normColor, normColor); + + const vec3 linearRGB = EOTF_PQ(rgb); + + static constexpr float kMaxPQLuminance = 10000.f; + const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance; + const double gain = + tonemap::getToneMapper() + ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: + Dataspace>( + HAL_DATASPACE_STANDARD_BT2020 | + HAL_DATASPACE_TRANSFER_ST2084 | + HAL_DATASPACE_RANGE_FULL), + static_cast<aidl::android::hardware::graphics::common:: + Dataspace>( + ui::Dataspace::DISPLAY_P3), + linearRGB * 10000.0, xyz, metadata); + const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance; + + const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255; + return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g), + static_cast<uint8_t>(targetRGB.b), 255); + }; + + expectBufferColor(Rect(kGreyLevels, 1), generator, 2); +} } // namespace renderengine } // namespace android diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp new file mode 100644 index 0000000000..390b22821e --- /dev/null +++ b/libs/shaders/Android.bp @@ -0,0 +1,44 @@ +// Copyright 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_static { + name: "libshaders", + + export_include_dirs: ["include"], + local_include_dirs: ["include"], + + shared_libs: [ + "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common@1.2", + ], + + static_libs: [ + "libmath", + "libtonemap", + "libui-types", + ], + + srcs: [ + "shaders.cpp", + ], +} diff --git a/libs/shaders/OWNERS b/libs/shaders/OWNERS new file mode 100644 index 0000000000..6d91da3bd2 --- /dev/null +++ b/libs/shaders/OWNERS @@ -0,0 +1,4 @@ +alecmouri@google.com +jreck@google.com +sallyqi@google.com +scroggo@google.com
\ No newline at end of file diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h new file mode 100644 index 0000000000..712a27a3eb --- /dev/null +++ b/libs/shaders/include/shaders/shaders.h @@ -0,0 +1,105 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> +#include <tonemap/tonemap.h> +#include <ui/GraphicTypes.h> +#include <cstddef> + +namespace android::shaders { + +/** + * Arguments for creating an effect that applies color transformations in linear XYZ space. + * A linear effect is decomposed into the following steps when operating on an image: + * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended + * relative display brightness of the scene in nits for each RGB channel + * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display + * luminance. + * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone + * mapping to display SDR content alongside HDR content, or any number of subjective transformations + * 4. Transformation matrix from linear XYZ back to linear RGB brightness. + * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to + * output RGB colors. + * + * For further reading, consult the recommendation in ITU-R BT.2390-4: + * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf + * + * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is + * intended to be the output surface. However, Skia does not support complex tone mapping such as + * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied + * to the source colors. so that the tone mapping process is only applied once by this effect. Tone + * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions) + * alongside other content, whereby maximum input luminance is mapped to maximum output luminance + * and intermediate values are interpolated. + */ +struct LinearEffect { + // Input dataspace of the source colors. + const ui::Dataspace inputDataspace = ui::Dataspace::SRGB; + + // Working dataspace for the output surface, for conversion from linear space. + const ui::Dataspace outputDataspace = ui::Dataspace::SRGB; + + // Sets whether alpha premultiplication must be undone. + // This is required if the source colors use premultiplied alpha and is not opaque. + const bool undoPremultipliedAlpha = false; + + // "Fake" dataspace of the source colors. This is used for applying an EOTF to compute linear + // RGB. This is used when Skia is expected to color manage the input image based on the + // dataspace of the provided source image and destination surface. SkRuntimeEffects use the + // destination color space as the working color space. RenderEngine deliberately sets the color + // space for input images and destination surfaces to be the same whenever LinearEffects are + // expected to be used so that color-management is controlled by RenderEngine, but other users + // of a LinearEffect may not be able to control the color space of the images and surfaces. So + // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output + // dataspace for correct conversion to linear colors. + ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN; +}; + +static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) { + return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace && + lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha && + lhs.fakeInputDataspace == rhs.fakeInputDataspace; +} + +struct LinearEffectHasher { + // Inspired by art/runtime/class_linker.cc + // Also this is what boost:hash_combine does + static size_t HashCombine(size_t seed, size_t val) { + return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + } + size_t operator()(const LinearEffect& le) const { + size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace); + result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace)); + result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha)); + return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeInputDataspace)); + } +}; + +// Generates a shader string that applies color transforms in linear space. +// Typical use-cases supported: +// 1. Apply tone-mapping +// 2. Apply color transform matrices in linear space +std::string buildLinearEffectSkSL(const LinearEffect& linearEffect); + +// Generates a list of uniforms to set on the LinearEffect shader above. +std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect, + const mat4& colorTransform, + float maxDisplayLuminance, + float maxLuminance); + +} // namespace android::shaders diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp new file mode 100644 index 0000000000..6019c4ac28 --- /dev/null +++ b/libs/shaders/shaders.cpp @@ -0,0 +1,497 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <shaders/shaders.h> + +#include <tonemap/tonemap.h> + +#include <optional> + +#include <math/mat4.h> +#include <system/graphics-base-v1.0.h> +#include <ui/ColorSpace.h> + +namespace android::shaders { + +static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace( + ui::Dataspace dataspace) { + return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace); +} + +static void generateEOTF(ui::Dataspace dataspace, std::string& shader) { + switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + + float3 EOTF(float3 color) { + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2)); + tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp); + return pow(tmp, 1.0 / float3(m1)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + shader.append(R"( + float EOTF_channel(float channel) { + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + return channel <= 0.5 ? channel * channel / 3.0 : + (exp((channel - c) / a) + b) / 12.0; + } + + float3 EOTF(float3 color) { + return float3(EOTF_channel(color.r), EOTF_channel(color.g), + EOTF_channel(color.b)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_LINEAR: + shader.append(R"( + float3 EOTF(float3 color) { + return color; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_SMPTE_170M: + shader.append(R"( + + float EOTF_sRGB(float srgb) { + return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 0.45); + } + + float3 EOTF_sRGB(float3 srgb) { + return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); + } + + float3 EOTF(float3 srgb) { + return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_GAMMA2_2: + shader.append(R"( + + float EOTF_sRGB(float srgb) { + return pow(srgb, 2.2); + } + + float3 EOTF_sRGB(float3 srgb) { + return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); + } + + float3 EOTF(float3 srgb) { + return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_GAMMA2_6: + shader.append(R"( + + float EOTF_sRGB(float srgb) { + return pow(srgb, 2.6); + } + + float3 EOTF_sRGB(float3 srgb) { + return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); + } + + float3 EOTF(float3 srgb) { + return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_GAMMA2_8: + shader.append(R"( + + float EOTF_sRGB(float srgb) { + return pow(srgb, 2.8); + } + + float3 EOTF_sRGB(float3 srgb) { + return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); + } + + float3 EOTF(float3 srgb) { + return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_SRGB: + default: + shader.append(R"( + + float EOTF_sRGB(float srgb) { + return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4); + } + + float3 EOTF_sRGB(float3 srgb) { + return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); + } + + float3 EOTF(float3 srgb) { + return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); + } + )"); + break; + } +} + +static void generateXYZTransforms(std::string& shader) { + shader.append(R"( + uniform float4x4 in_rgbToXyz; + uniform float4x4 in_xyzToRgb; + float3 ToXYZ(float3 rgb) { + return (in_rgbToXyz * float4(rgb, 1.0)).rgb; + } + + float3 ToRGB(float3 xyz) { + return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0); + } + )"); +} + +// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits]) +static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, + ui::Dataspace outputDataspace, std::string& shader) { + switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + float3 ScaleLuminance(float3 xyz) { + return xyz * 10000.0; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + shader.append(R"( + float3 ScaleLuminance(float3 xyz) { + return xyz * 1000.0 * pow(xyz.y, 0.2); + } + )"); + break; + default: + switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + case HAL_DATASPACE_TRANSFER_HLG: + // SDR -> HDR tonemap + shader.append(R"( + float3 ScaleLuminance(float3 xyz) { + return xyz * in_libtonemap_inputMaxLuminance; + } + )"); + break; + default: + // Input and output are both SDR, so no tone-mapping is expected so + // no-op the luminance normalization. + shader.append(R"( + float3 ScaleLuminance(float3 xyz) { + return xyz * in_libtonemap_displayMaxLuminance; + } + )"); + break; + } + } +} + +// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1]) +static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, + std::string& shader) { + switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + float3 NormalizeLuminance(float3 xyz) { + return xyz / 10000.0; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + shader.append(R"( + float3 NormalizeLuminance(float3 xyz) { + return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2); + } + )"); + break; + default: + shader.append(R"( + float3 NormalizeLuminance(float3 xyz) { + return xyz / in_libtonemap_displayMaxLuminance; + } + )"); + break; + } +} + +static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, + std::string& shader) { + shader.append(tonemap::getToneMapper() + ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace), + toAidlDataspace(outputDataspace)) + .c_str()); + + generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader); + generateLuminanceNormalizationForOOTF(outputDataspace, shader); + + shader.append(R"( + float3 OOTF(float3 linearRGB, float3 xyz) { + float3 scaledLinearRGB = ScaleLuminance(linearRGB); + float3 scaledXYZ = ScaleLuminance(xyz); + + float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ); + + return NormalizeLuminance(scaledXYZ * gain); + } + )"); +} + +static void generateOETF(ui::Dataspace dataspace, std::string& shader) { + switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + shader.append(R"( + + float3 OETF(float3 xyz) { + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float3 tmp = pow(xyz, float3(m1)); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return pow(tmp, float3(m2)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_HLG: + shader.append(R"( + float OETF_channel(float channel) { + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : + a * log(12.0 * channel - b) + c; + } + + float3 OETF(float3 linear) { + return float3(OETF_channel(linear.r), OETF_channel(linear.g), + OETF_channel(linear.b)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_LINEAR: + shader.append(R"( + float3 OETF(float3 linear) { + return linear; + } + )"); + break; + case HAL_DATASPACE_TRANSFER_SMPTE_170M: + shader.append(R"( + float OETF_sRGB(float linear) { + return linear <= 0.018 ? + linear * 4.50 : (pow(linear, 0.45) * 1.099) - 0.099; + } + + float3 OETF_sRGB(float3 linear) { + return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + float3 OETF(float3 linear) { + return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_GAMMA2_2: + shader.append(R"( + float OETF_sRGB(float linear) { + return pow(linear, (1.0 / 2.2)); + } + + float3 OETF_sRGB(float3 linear) { + return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + float3 OETF(float3 linear) { + return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_GAMMA2_6: + shader.append(R"( + float OETF_sRGB(float linear) { + return pow(linear, (1.0 / 2.6)); + } + + float3 OETF_sRGB(float3 linear) { + return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + float3 OETF(float3 linear) { + return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_GAMMA2_8: + shader.append(R"( + float OETF_sRGB(float linear) { + return pow(linear, (1.0 / 2.8)); + } + + float3 OETF_sRGB(float3 linear) { + return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + float3 OETF(float3 linear) { + return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); + } + )"); + break; + case HAL_DATASPACE_TRANSFER_SRGB: + default: + shader.append(R"( + float OETF_sRGB(float linear) { + return linear <= 0.0031308 ? + linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055; + } + + float3 OETF_sRGB(float3 linear) { + return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + float3 OETF(float3 linear) { + return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); + } + )"); + break; + } +} + +static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) { + shader.append(R"( + uniform shader child; + half4 main(float2 xy) { + float4 c = float4(child.eval(xy)); + )"); + if (undoPremultipliedAlpha) { + shader.append(R"( + c.rgb = c.rgb / (c.a + 0.0019); + )"); + } + shader.append(R"( + float3 linearRGB = EOTF(c.rgb); + float3 xyz = ToXYZ(linearRGB); + c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz))); + )"); + if (undoPremultipliedAlpha) { + shader.append(R"( + c.rgb = c.rgb * (c.a + 0.0019); + )"); + } + shader.append(R"( + return c; + } + )"); +} + +// please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp +static ColorSpace toColorSpace(ui::Dataspace dataspace) { + switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { + case HAL_DATASPACE_STANDARD_BT709: + return ColorSpace::sRGB(); + case HAL_DATASPACE_STANDARD_DCI_P3: + return ColorSpace::DisplayP3(); + case HAL_DATASPACE_STANDARD_BT2020: + case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE: + return ColorSpace::BT2020(); + case HAL_DATASPACE_STANDARD_ADOBE_RGB: + return ColorSpace::AdobeRGB(); + // TODO(b/208290320): BT601 format and variants return different primaries + case HAL_DATASPACE_STANDARD_BT601_625: + case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED: + case HAL_DATASPACE_STANDARD_BT601_525: + case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED: + // TODO(b/208290329): BT407M format returns different primaries + case HAL_DATASPACE_STANDARD_BT470M: + // TODO(b/208290904): FILM format returns different primaries + case HAL_DATASPACE_STANDARD_FILM: + case HAL_DATASPACE_STANDARD_UNSPECIFIED: + default: + return ColorSpace::sRGB(); + } +} + +std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) { + std::string shaderString; + generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN + ? linearEffect.inputDataspace + : linearEffect.fakeInputDataspace, + shaderString); + generateXYZTransforms(shaderString); + generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString); + generateOETF(linearEffect.outputDataspace, shaderString); + generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString); + return shaderString; +} + +template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true> +std::vector<uint8_t> buildUniformValue(T value) { + std::vector<uint8_t> result; + result.resize(sizeof(value)); + std::memcpy(result.data(), &value, sizeof(value)); + return result; +} + +// Generates a list of uniforms to set on the LinearEffect shader above. +std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect, + const mat4& colorTransform, + float maxDisplayLuminance, + float maxLuminance) { + std::vector<tonemap::ShaderUniform> uniforms; + if (linearEffect.inputDataspace == linearEffect.outputDataspace) { + uniforms.push_back({.name = "in_rgbToXyz", .value = buildUniformValue<mat4>(mat4())}); + uniforms.push_back( + {.name = "in_xyzToRgb", .value = buildUniformValue<mat4>(colorTransform)}); + } else { + ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace); + ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace); + uniforms.push_back({.name = "in_rgbToXyz", + .value = buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ()))}); + uniforms.push_back({.name = "in_xyzToRgb", + .value = buildUniformValue<mat4>( + colorTransform * mat4(outputColorSpace.getXYZtoRGB()))}); + } + + tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance, + // If the input luminance is unknown, use display luminance (aka, + // no-op any luminance changes) + // This will be the case for eg screenshots in addition to + // uncalibrated displays + .contentMaxLuminance = + maxLuminance > 0 ? maxLuminance : maxDisplayLuminance}; + + for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) { + uniforms.push_back(uniform); + } + + return uniforms; +} + +} // namespace android::shaders
\ No newline at end of file diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp index 231a342852..5360fe2b07 100644 --- a/libs/tonemap/Android.bp +++ b/libs/tonemap/Android.bp @@ -30,7 +30,13 @@ cc_library_static { shared_libs: [ "android.hardware.graphics.common-V3-ndk", + "liblog", ], + + static_libs: [ + "libmath", + ], + srcs: [ "tonemap.cpp", ], diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h index d350e160ca..bd7b72d186 100644 --- a/libs/tonemap/include/tonemap/tonemap.h +++ b/libs/tonemap/include/tonemap/tonemap.h @@ -17,6 +17,7 @@ #pragma once #include <aidl/android/hardware/graphics/common/Dataspace.h> +#include <math/vec3.h> #include <string> #include <vector> @@ -48,7 +49,9 @@ struct Metadata { class ToneMapper { public: virtual ~ToneMapper() {} - // Constructs a tonemap shader whose shader language is SkSL + // Constructs a tonemap shader whose shader language is SkSL, which tonemaps from an + // input whose dataspace is described by sourceDataspace, to an output whose dataspace + // is described by destinationDataspace // // The returned shader string *must* contain a function with the following signature: // float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz); @@ -94,6 +97,19 @@ public: // assume that there are predefined floats in_libtonemap_displayMaxLuminance and // in_libtonemap_inputMaxLuminance inside of the body of the tone-mapping shader. virtual std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) = 0; + + // CPU implementation of the tonemapping gain. This must match the GPU implementation returned + // by generateTonemapGainShaderSKSL() above, with some epsilon difference to account for + // differences in hardware precision. + // + // The gain is computed assuming an input described by sourceDataspace, tonemapped to an output + // described by destinationDataspace. To compute the gain, the input colors are provided by + // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also + // provided. Metadata is also provided for helping to compute the tonemapping curve. + virtual double lookupTonemapGain( + aidl::android::hardware::graphics::common::Dataspace sourceDataspace, + aidl::android::hardware::graphics::common::Dataspace destinationDataspace, + vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0; }; // Retrieves a tonemapper instance. diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp index e58d519224..f46f3fa27d 100644 --- a/libs/tonemap/tests/Android.bp +++ b/libs/tonemap/tests/Android.bp @@ -31,6 +31,7 @@ cc_test { "android.hardware.graphics.common-V3-ndk", ], static_libs: [ + "libmath", "libgmock", "libgtest", "libtonemap", diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp index 350bca427c..c2372fe828 100644 --- a/libs/tonemap/tonemap.cpp +++ b/libs/tonemap/tonemap.cpp @@ -16,6 +16,7 @@ #include <tonemap/tonemap.h> +#include <algorithm> #include <cstdint> #include <mutex> #include <type_traits> @@ -26,10 +27,11 @@ namespace { // Flag containing the variant of tone map algorithm to use. enum class ToneMapAlgorithm { - AndroidO, // Default algorithm in place since Android O, + AndroidO, // Default algorithm in place since Android O, + Android13, // Algorithm used in Android 13. }; -static const constexpr auto kToneMapAlgorithm = ToneMapAlgorithm::AndroidO; +static const constexpr auto kToneMapAlgorithm = ToneMapAlgorithm::Android13; static const constexpr auto kTransferMask = static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_MASK); @@ -231,9 +233,416 @@ public: .value = buildUniformValue<float>(metadata.displayMaxLuminance)}); uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance", .value = buildUniformValue<float>(metadata.contentMaxLuminance)}); + return uniforms; + } + + double lookupTonemapGain( + aidl::android::hardware::graphics::common::Dataspace sourceDataspace, + aidl::android::hardware::graphics::common::Dataspace destinationDataspace, + vec3 /* linearRGB */, vec3 xyz, const Metadata& metadata) override { + if (xyz.y <= 0.0) { + return 1.0; + } + const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); + const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace); + + double targetNits = 0.0; + switch (sourceDataspaceInt & kTransferMask) { + case kTransferST2084: + case kTransferHLG: + switch (destinationDataspaceInt & kTransferMask) { + case kTransferST2084: + targetNits = xyz.y; + break; + case kTransferHLG: + // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so + // we'll clamp the luminance range in case we're mapping from PQ input to + // HLG output. + targetNits = std::clamp(xyz.y, 0.0f, 1000.0f); + break; + default: + // Here we're mapping from HDR to SDR content, so interpolate using a + // Hermitian polynomial onto the smaller luminance range. + + targetNits = xyz.y; + // if the max input luminance is less than what we can output then + // no tone mapping is needed as all color values will be in range. + if (metadata.contentMaxLuminance > metadata.displayMaxLuminance) { + // three control points + const double x0 = 10.0; + const double y0 = 17.0; + double x1 = metadata.displayMaxLuminance * 0.75; + double y1 = x1; + double x2 = x1 + (metadata.contentMaxLuminance - x1) / 2.0; + double y2 = y1 + (metadata.displayMaxLuminance - y1) * 0.75; + + // horizontal distances between the last three control points + double h12 = x2 - x1; + double h23 = metadata.contentMaxLuminance - x2; + // tangents at the last three control points + double m1 = (y2 - y1) / h12; + double m3 = (metadata.displayMaxLuminance - y2) / h23; + double m2 = (m1 + m3) / 2.0; + + if (targetNits < x0) { + // scale [0.0, x0] to [0.0, y0] linearly + double slope = y0 / x0; + targetNits *= slope; + } else if (targetNits < x1) { + // scale [x0, x1] to [y0, y1] linearly + double slope = (y1 - y0) / (x1 - x0); + targetNits = y0 + (targetNits - x0) * slope; + } else if (targetNits < x2) { + // scale [x1, x2] to [y1, y2] using Hermite interp + double t = (targetNits - x1) / h12; + targetNits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * + (1.0 - t) + + (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; + } else { + // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp + double t = (targetNits - x2) / h23; + targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * + (1.0 - t) + + (metadata.displayMaxLuminance * (3.0 - 2.0 * t) + + h23 * m3 * (t - 1.0)) * + t * t; + } + } + break; + } + break; + default: + // source is SDR + switch (destinationDataspaceInt & kTransferMask) { + case kTransferST2084: + case kTransferHLG: { + // Map from SDR onto an HDR output buffer + // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto + // [0, maxOutLumi] which is hard-coded to be 3000 nits. + const double maxOutLumi = 3000.0; + + double x0 = 5.0; + double y0 = 2.5; + double x1 = metadata.displayMaxLuminance * 0.7; + double y1 = maxOutLumi * 0.15; + double x2 = metadata.displayMaxLuminance * 0.9; + double y2 = maxOutLumi * 0.45; + double x3 = metadata.displayMaxLuminance; + double y3 = maxOutLumi; + + double c1 = y1 / 3.0; + double c2 = y2 / 2.0; + double c3 = y3 / 1.5; + + targetNits = xyz.y; + + if (targetNits <= x0) { + // scale [0.0, x0] to [0.0, y0] linearly + double slope = y0 / x0; + targetNits *= slope; + } else if (targetNits <= x1) { + // scale [x0, x1] to [y0, y1] using a curve + double t = (targetNits - x0) / (x1 - x0); + targetNits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + + t * t * y1; + } else if (targetNits <= x2) { + // scale [x1, x2] to [y1, y2] using a curve + double t = (targetNits - x1) / (x2 - x1); + targetNits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + + t * t * y2; + } else { + // scale [x2, x3] to [y2, y3] using a curve + double t = (targetNits - x2) / (x3 - x2); + targetNits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + + t * t * y3; + } + } break; + default: + // For completeness, this is tone-mapping from SDR to SDR, where this is + // just a no-op. + targetNits = xyz.y; + break; + } + } + + return targetNits / xyz.y; + } +}; + +class ToneMapper13 : public ToneMapper { +private: + double OETF_ST2084(double nits) { + nits = nits / 10000.0; + double m1 = (2610.0 / 4096.0) / 4.0; + double m2 = (2523.0 / 4096.0) * 128.0; + double c1 = (3424.0 / 4096.0); + double c2 = (2413.0 / 4096.0) * 32.0; + double c3 = (2392.0 / 4096.0) * 32.0; + + double tmp = std::pow(nits, m1); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return std::pow(tmp, m2); + } + + double OETF_HLG(double nits) { + nits = nits / 1000.0; + const double a = 0.17883277; + const double b = 0.28466892; + const double c = 0.55991073; + return nits <= 1.0 / 12.0 ? std::sqrt(3.0 * nits) : a * std::log(12.0 * nits - b) + c; + } + +public: + std::string generateTonemapGainShaderSkSL( + aidl::android::hardware::graphics::common::Dataspace sourceDataspace, + aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override { + const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); + const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace); + + std::string program; + // Input uniforms + program.append(R"( + uniform float in_libtonemap_displayMaxLuminance; + uniform float in_libtonemap_inputMaxLuminance; + )"); + switch (sourceDataspaceInt & kTransferMask) { + case kTransferST2084: + case kTransferHLG: + switch (destinationDataspaceInt & kTransferMask) { + case kTransferST2084: + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return maxRGB; + } + )"); + break; + case kTransferHLG: + // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so + // we'll clamp the luminance range in case we're mapping from PQ input to + // HLG output. + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return clamp(maxRGB, 0.0, 1000.0); + } + )"); + break; + + default: + switch (sourceDataspaceInt & kTransferMask) { + case kTransferST2084: + program.append(R"( + float libtonemap_OETFTone(float channel) { + channel = channel / 10000.0; + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float tmp = pow(channel, float(m1)); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return pow(tmp, float(m2)); + } + )"); + break; + case kTransferHLG: + program.append(R"( + float libtonemap_OETFTone(float channel) { + channel = channel / 1000.0; + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : + a * log(12.0 * channel - b) + c; + } + )"); + break; + } + // Here we're mapping from HDR to SDR content, so interpolate using a + // Hermitian polynomial onto the smaller luminance range. + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + float maxInLumi = in_libtonemap_inputMaxLuminance; + float maxOutLumi = in_libtonemap_displayMaxLuminance; + + float nits = maxRGB; + + float x1 = maxOutLumi * 0.65; + float y1 = x1; + + float x3 = maxInLumi; + float y3 = maxOutLumi; + + float x2 = x1 + (x3 - x1) * 4.0 / 17.0; + float y2 = maxOutLumi * 0.9; + + float greyNorm1 = libtonemap_OETFTone(x1); + float greyNorm2 = libtonemap_OETFTone(x2); + float greyNorm3 = libtonemap_OETFTone(x3); + + float slope1 = 0; + float slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); + float slope3 = (y3 - y2 ) / (greyNorm3 - greyNorm2); + + if (nits < x1) { + return nits; + } + + if (nits > maxInLumi) { + return maxOutLumi; + } + + float greyNits = libtonemap_OETFTone(nits); + + if (greyNits <= greyNorm2) { + nits = (greyNits - greyNorm2) * slope2 + y2; + } else if (greyNits <= greyNorm3) { + nits = (greyNits - greyNorm3) * slope3 + y3; + } else { + nits = maxOutLumi; + } + + return nits; + } + )"); + break; + } + break; + default: + // Inverse tone-mapping and SDR-SDR mapping is not supported. + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return maxRGB; + } + )"); + break; + } + + program.append(R"( + float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) { + float maxRGB = max(linearRGB.r, max(linearRGB.g, linearRGB.b)); + if (maxRGB <= 0.0) { + return 1.0; + } + return libtonemap_ToneMapTargetNits(maxRGB) / maxRGB; + } + )"); + return program; + } + std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override { + // Hardcode the max content luminance to a "reasonable" level + static const constexpr float kContentMaxLuminance = 4000.f; + std::vector<ShaderUniform> uniforms; + uniforms.reserve(2); + uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance", + .value = buildUniformValue<float>(metadata.displayMaxLuminance)}); + uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance", + .value = buildUniformValue<float>(kContentMaxLuminance)}); return uniforms; } + + double lookupTonemapGain( + aidl::android::hardware::graphics::common::Dataspace sourceDataspace, + aidl::android::hardware::graphics::common::Dataspace destinationDataspace, + vec3 linearRGB, vec3 /* xyz */, const Metadata& metadata) override { + double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b}); + + if (maxRGB <= 0.0) { + return 1.0; + } + + const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace); + const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace); + + double targetNits = 0.0; + switch (sourceDataspaceInt & kTransferMask) { + case kTransferST2084: + case kTransferHLG: + switch (destinationDataspaceInt & kTransferMask) { + case kTransferST2084: + targetNits = maxRGB; + break; + case kTransferHLG: + // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so + // we'll clamp the luminance range in case we're mapping from PQ input to + // HLG output. + targetNits = std::clamp(maxRGB, 0.0, 1000.0); + break; + default: + // Here we're mapping from HDR to SDR content, so interpolate using a + // Hermitian polynomial onto the smaller luminance range. + + double maxInLumi = 4000; + double maxOutLumi = metadata.displayMaxLuminance; + + targetNits = maxRGB; + + double x1 = maxOutLumi * 0.65; + double y1 = x1; + + double x3 = maxInLumi; + double y3 = maxOutLumi; + + double x2 = x1 + (x3 - x1) * 4.0 / 17.0; + double y2 = maxOutLumi * 0.9; + + double greyNorm1 = 0.0; + double greyNorm2 = 0.0; + double greyNorm3 = 0.0; + + if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) { + greyNorm1 = OETF_ST2084(x1); + greyNorm2 = OETF_ST2084(x2); + greyNorm3 = OETF_ST2084(x3); + } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) { + greyNorm1 = OETF_HLG(x1); + greyNorm2 = OETF_HLG(x2); + greyNorm3 = OETF_HLG(x3); + } + + double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); + double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2); + + if (targetNits < x1) { + break; + } + + if (targetNits > maxInLumi) { + targetNits = maxOutLumi; + break; + } + + double greyNits = 0.0; + if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) { + greyNits = OETF_ST2084(targetNits); + } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) { + greyNits = OETF_HLG(targetNits); + } + + if (greyNits <= greyNorm2) { + targetNits = (greyNits - greyNorm2) * slope2 + y2; + } else if (greyNits <= greyNorm3) { + targetNits = (greyNits - greyNorm3) * slope3 + y3; + } else { + targetNits = maxOutLumi; + } + break; + } + break; + default: + switch (destinationDataspaceInt & kTransferMask) { + case kTransferST2084: + case kTransferHLG: + default: + targetNits = maxRGB; + break; + } + break; + } + + return targetNits / maxRGB; + } }; } // namespace @@ -247,10 +656,11 @@ ToneMapper* getToneMapper() { case ToneMapAlgorithm::AndroidO: sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapperO()); break; + case ToneMapAlgorithm::Android13: + sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapper13()); } }); return sToneMapper.get(); } - } // namespace android::tonemap
\ No newline at end of file diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 36d001fe0f..a7b5900d67 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -225,6 +225,11 @@ cc_library_shared { "libui_headers", ], min_sdk_version: "29", + + pgo: { + sampling: true, + profile_file: "libui/libui.profdata", + }, } cc_library_headers { diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index 1f006ceb69..073da89758 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -302,6 +302,8 @@ std::string decodePixelFormat(android::PixelFormat format) { return std::string("RGB_565"); case android::PIXEL_FORMAT_BGRA_8888: return std::string("BGRA_8888"); + case android::PIXEL_FORMAT_R_8: + return std::string("R_8"); default: return StringPrintf("Unknown %#08x", format); } diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index e88fdd5e84..799fbc9d54 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -35,25 +35,8 @@ uint32_t bytesPerPixel(PixelFormat format) { case PIXEL_FORMAT_RGBA_5551: case PIXEL_FORMAT_RGBA_4444: return 2; - } - return 0; -} - -uint32_t bitsPerPixel(PixelFormat format) { - switch (format) { - case PIXEL_FORMAT_RGBA_FP16: - return 64; - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_BGRA_8888: - case PIXEL_FORMAT_RGBA_1010102: - return 32; - case PIXEL_FORMAT_RGB_888: - return 24; - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_RGBA_5551: - case PIXEL_FORMAT_RGBA_4444: - return 16; + case PIXEL_FORMAT_R_8: + return 1; } return 0; } diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h index 02773d92fc..f422ce439e 100644 --- a/libs/ui/include/ui/PixelFormat.h +++ b/libs/ui/include/ui/PixelFormat.h @@ -62,12 +62,12 @@ enum { PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA + PIXEL_FORMAT_R_8 = 0x38, }; typedef int32_t PixelFormat; uint32_t bytesPerPixel(PixelFormat format); -uint32_t bitsPerPixel(PixelFormat format); }; // namespace android diff --git a/libs/ui/include_types/ui/GraphicTypes.h b/libs/ui/include_types/ui/GraphicTypes.h new file mode 120000 index 0000000000..b1859e0f51 --- /dev/null +++ b/libs/ui/include_types/ui/GraphicTypes.h @@ -0,0 +1 @@ +../../include/ui/GraphicTypes.h
\ No newline at end of file diff --git a/opengl/libs/EGL/GLES_layers.md b/opengl/libs/EGL/GLES_layers.md index bfc44dbb69..f6a8f14a73 100644 --- a/opengl/libs/EGL/GLES_layers.md +++ b/opengl/libs/EGL/GLES_layers.md @@ -251,7 +251,7 @@ When layering is enabled, GLES 1.x exclusive functions will continue to route to - Secondly, if you want to determine from an application that can't call out to ADB for this, you can check for the [EGL_ANDROID_GLES_layers](../../specs/EGL_ANDROID_GLES_layers.txt). It simply indicates support of this layering system: ```cpp std::string display_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - if (display_extension.find("EGL_ANDROID_GLES_layers") != std::string::npos) + if (display_extensions.find("EGL_ANDROID_GLES_layers") != std::string::npos) { // Layers are supported! } diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp index 3aa862f31f..dd3cc3bd86 100644 --- a/services/gpuservice/gpumem/GpuMem.cpp +++ b/services/gpuservice/gpumem/GpuMem.cpp @@ -22,7 +22,7 @@ #include <android-base/stringprintf.h> #include <libbpf.h> -#include <libbpf_android.h> +#include <bpf/WaitForProgsLoaded.h> #include <log/log.h> #include <unistd.h> #include <utils/Timers.h> diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp index 220952d892..d033453dd7 100644 --- a/services/gpuservice/gpustats/GpuStats.cpp +++ b/services/gpuservice/gpustats/GpuStats.cpp @@ -84,6 +84,38 @@ static void addLoadingTime(GpuStatsInfo::Driver driver, int64_t driverLoadingTim } } +void GpuStats::purgeOldDriverStats() { + ALOG_ASSERT(mAppStats.size() == MAX_NUM_APP_RECORDS); + + struct GpuStatsApp { + // Key is <app package name>+<driver version code>. + const std::string *appStatsKey = nullptr; + const std::chrono::time_point<std::chrono::system_clock> *lastAccessTime = nullptr; + }; + std::vector<GpuStatsApp> gpuStatsApps(MAX_NUM_APP_RECORDS); + + // Create a list of pointers to package names and their last access times. + int index = 0; + for (const auto & [appStatsKey, gpuStatsAppInfo] : mAppStats) { + GpuStatsApp &gpuStatsApp = gpuStatsApps[index]; + gpuStatsApp.appStatsKey = &appStatsKey; + gpuStatsApp.lastAccessTime = &gpuStatsAppInfo.lastAccessTime; + ++index; + } + + // Sort the list with the oldest access times at the front. + std::sort(gpuStatsApps.begin(), gpuStatsApps.end(), [](GpuStatsApp a, GpuStatsApp b) -> bool { + return *a.lastAccessTime < *b.lastAccessTime; + }); + + // Remove the oldest packages from mAppStats to make room for new apps. + for (int i = 0; i < APP_RECORD_HEADROOM; ++i) { + mAppStats.erase(*gpuStatsApps[i].appStatsKey); + gpuStatsApps[i].appStatsKey = nullptr; + gpuStatsApps[i].lastAccessTime = nullptr; + } +} + void GpuStats::insertDriverStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, @@ -123,19 +155,22 @@ void GpuStats::insertDriverStats(const std::string& driverPackageName, const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode); if (!mAppStats.count(appStatsKey)) { if (mAppStats.size() >= MAX_NUM_APP_RECORDS) { - ALOGV("GpuStatsAppInfo has reached maximum size. Ignore new stats."); - return; + ALOGV("GpuStatsAppInfo has reached maximum size. Removing old stats to make room."); + purgeOldDriverStats(); } GpuStatsAppInfo appInfo; addLoadingTime(driver, driverLoadingTime, &appInfo); appInfo.appPackageName = appPackageName; appInfo.driverVersionCode = driverVersionCode; + appInfo.angleInUse = driverPackageName == "angle"; + appInfo.lastAccessTime = std::chrono::system_clock::now(); mAppStats.insert({appStatsKey, appInfo}); - return; + } else { + mAppStats[appStatsKey].angleInUse = driverPackageName == "angle"; + addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]); + mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now(); } - - addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]); } void GpuStats::insertTargetStats(const std::string& appPackageName, @@ -311,7 +346,8 @@ AStatsManager_PullAtomCallbackReturn GpuStats::pullAppInfoAtom(AStatsEventList* angleDriverBytes.length()), ele.second.cpuVulkanInUse, ele.second.falsePrerotation, - ele.second.gles1InUse); + ele.second.gles1InUse, + ele.second.angleInUse); } } diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h index 55f0da1bc5..2aba651af9 100644 --- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h +++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h @@ -46,6 +46,11 @@ public: // This limits the worst case number of loading times tracked. static const size_t MAX_NUM_LOADING_TIMES = 50; + // Below limits the memory usage of GpuStats to be less than 10KB. This is + // the preferred number for statsd while maintaining nice data quality. + static const size_t MAX_NUM_APP_RECORDS = 100; + // The number of apps to remove when mAppStats fills up. + static const size_t APP_RECORD_HEADROOM = 10; private: // Friend class for testing. @@ -55,6 +60,10 @@ private: static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag, AStatsEventList* data, void* cookie); + + // Remove old packages from mAppStats. + void purgeOldDriverStats(); + // Pull global into into global atom. AStatsManager_PullAtomCallbackReturn pullGlobalInfoAtom(AStatsEventList* data); // Pull app into into app atom. @@ -68,9 +77,6 @@ private: // Registers statsd callbacks if they have not already been registered void registerStatsdCallbacksIfNeeded(); - // Below limits the memory usage of GpuStats to be less than 10KB. This is - // the preferred number for statsd while maintaining nice data quality. - static const size_t MAX_NUM_APP_RECORDS = 100; // GpuStats access should be guarded by mLock. std::mutex mLock; // True if statsd callbacks have been registered. diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp index 37ebeae18d..20c8ccf9bf 100644 --- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp +++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "gpuservice_unittest" +#include <unistd.h> #include <cutils/properties.h> #include <gmock/gmock.h> #include <gpustats/GpuStats.h> @@ -221,6 +222,51 @@ TEST_F(GpuStatsTest, canInsertTargetStatsAfterProperSetup) { EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1")); } +// Verify we always have the most recently used apps in mAppStats, even when we fill it. +TEST_F(GpuStatsTest, canInsertMoreThanMaxNumAppRecords) { + constexpr int kNumExtraApps = 15; + static_assert(kNumExtraApps > GpuStats::APP_RECORD_HEADROOM); + + // Insert stats for GpuStats::MAX_NUM_APP_RECORDS so we fill it up. + for (int i = 0; i < GpuStats::MAX_NUM_APP_RECORDS + kNumExtraApps; ++i) { + std::stringstream nameStream; + nameStream << "testapp" << "_" << i; + std::string fullPkgName = nameStream.str(); + + mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME, + BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, + fullPkgName, VULKAN_VERSION, GpuStatsInfo::Driver::GL, true, + DRIVER_LOADING_TIME_1); + mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE, + GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0); + mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE, + GpuStatsInfo::Stats::FALSE_PREROTATION, 0); + mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE, + GpuStatsInfo::Stats::GLES_1_IN_USE, 0); + + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str())); + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1")); + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1")); + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1")); + } + + // mAppStats purges GpuStats::APP_RECORD_HEADROOM apps removed everytime it's filled up. + int numPurges = kNumExtraApps / GpuStats::APP_RECORD_HEADROOM; + numPurges += (kNumExtraApps % GpuStats::APP_RECORD_HEADROOM) == 0 ? 0 : 1; + + // Verify the remaining apps are present. + for (int i = numPurges * GpuStats::APP_RECORD_HEADROOM; + i < GpuStats::MAX_NUM_APP_RECORDS + kNumExtraApps; + ++i) { + std::stringstream nameStream; + // Add a newline to search for the exact package name. + nameStream << "testapp" << "_" << i << "\n"; + std::string fullPkgName = nameStream.str(); + + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str())); + } +} + TEST_F(GpuStatsTest, canDumpAllBeforeClearAll) { mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME, BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1, diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 29d8a0f441..19cad7b9ad 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -68,7 +68,8 @@ static MotionClassification getMotionClassification(common::V1_0::Classification } static bool isTouchEvent(const NotifyMotionArgs& args) { - return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN; + return isFromSource(args.source, AINPUT_SOURCE_TOUCHPAD) || + isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN); } // --- ClassifierEvent --- diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index cee9c39abd..b4497fd653 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -22,7 +22,7 @@ namespace android::inputdispatcher { Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator) - : status(STATUS_NORMAL), + : status(Status::NORMAL), inputChannel(inputChannel), monitor(monitor), inputPublisher(inputChannel), @@ -40,19 +40,6 @@ const std::string Connection::getWindowName() const { return "?"; } -const char* Connection::getStatusLabel() const { - switch (status) { - case STATUS_NORMAL: - return "NORMAL"; - case STATUS_BROKEN: - return "BROKEN"; - case STATUS_ZOMBIE: - return "ZOMBIE"; - default: - return "UNKNOWN"; - } -} - std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) { for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) { if ((*it)->seq == seq) { diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index ba602831eb..dc6a081ff6 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -33,13 +33,16 @@ protected: virtual ~Connection(); public: - enum Status { + enum class Status { // Everything is peachy. - STATUS_NORMAL, + NORMAL, // An unrecoverable communication error has occurred. - STATUS_BROKEN, + BROKEN, // The input channel has been unregistered. - STATUS_ZOMBIE + ZOMBIE, + + ftl_first = NORMAL, + ftl_last = ZOMBIE, }; Status status; @@ -66,7 +69,6 @@ public: inline const std::string getInputChannelName() const { return inputChannel->getName(); } const std::string getWindowName() const; - const char* getStatusLabel() const; std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq); }; diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index c03581d8da..f6bb6a63a7 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -175,13 +175,13 @@ std::string KeyEntry::getDescription() const { if (!GetBoolProperty("ro.debuggable", false)) { return "KeyEvent"; } - return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 - ", source=0x%08x, displayId=%" PRId32 ", action=%s, " + return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32 + ", action=%s, " "flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", - deviceId, eventTime, source, displayId, KeyEvent::actionToString(action), - flags, KeyEvent::getLabel(keyCode), keyCode, scanCode, metaState, - repeatCount, policyFlags); + deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId, + KeyEvent::actionToString(action), flags, KeyEvent::getLabel(keyCode), + keyCode, scanCode, metaState, repeatCount, policyFlags); } void KeyEntry::recycle() { @@ -249,12 +249,12 @@ std::string MotionEntry::getDescription() const { } std::string msg; msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64 - ", source=0x%08x, displayId=%" PRId32 + ", source=%s, displayId=%" PRId32 ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " "buttonState=0x%08x, " "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", - deviceId, eventTime, source, displayId, + deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId, MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState, buttonState, motionClassificationToString(classification), edgeFlags, xPrecision, yPrecision, xCursorPosition, yCursorPosition); @@ -289,9 +289,10 @@ SensorEntry::~SensorEntry() {} std::string SensorEntry::getDescription() const { std::string msg; - msg += StringPrintf("SensorEntry(deviceId=%d, source=0x%08x, sensorType=0x%08x, " + msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, " "accuracy=0x%08x, hwTimestamp=%" PRId64, - deviceId, source, sensorType, accuracy, hwTimestamp); + deviceId, inputEventSourceToString(source).c_str(), + ftl::enum_string(sensorType).c_str(), accuracy, hwTimestamp); if (!GetBoolProperty("ro.debuggable", false)) { for (size_t i = 0; i < values.size(); i++) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 695258759b..c9397c350b 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -506,10 +506,6 @@ bool isConnectionResponsive(const Connection& connection) { return true; } -bool isFromSource(uint32_t source, uint32_t test) { - return (source & test) == test; -} - vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) { const vec2 transformedXy = transform.transform(x, y); const vec2 transformedOrigin = transform.transform(0, 0); @@ -1753,7 +1749,7 @@ void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) // pile up. ALOGW("Canceling events for %s because it is unresponsive", connection->inputChannel->getName().c_str()); - if (connection->status == Connection::STATUS_NORMAL) { + if (connection->status == Connection::Status::NORMAL) { CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "application not responding"); synthesizeCancelationEventsForConnectionLocked(connection, options); @@ -2107,7 +2103,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( const std::vector<Monitor> newGestureMonitors = isDown ? selectResponsiveMonitorsLocked( getValueByKey(mGestureMonitorsByDisplay, displayId)) - : std::vector<Monitor>{}; + : tempTouchState.gestureMonitors; if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { ALOGI("Dropping event because there is no touchable window or gesture monitor at " @@ -2143,9 +2139,14 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( pointerIds.markBit(pointerId); } tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + } else if (tempTouchState.windows.empty()) { + // If no window is touched, set split to true. This will allow the next pointer down to + // be delivered to a new window which supports split touch. + tempTouchState.split = true; + } + if (isDown) { + tempTouchState.addGestureMonitors(newGestureMonitors); } - - tempTouchState.addGestureMonitors(newGestureMonitors); } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ @@ -2829,10 +2830,11 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // Skip this event if the connection status is not normal. // We don't want to enqueue additional outbound events if the connection is broken. - if (connection->status != Connection::STATUS_NORMAL) { + if (connection->status != Connection::Status::NORMAL) { if (DEBUG_DISPATCH_CYCLE) { ALOGD("channel '%s' ~ Dropping event because the channel status is %s", - connection->getInputChannelName().c_str(), connection->getStatusLabel()); + connection->getInputChannelName().c_str(), + ftl::enum_string(connection->status).c_str()); } return; } @@ -3146,7 +3148,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str()); } - while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { + while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; const std::chrono::nanoseconds timeout = @@ -3234,8 +3236,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry); status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq, focusEntry.id, - focusEntry.hasFocus, - mInTouchMode); + focusEntry.hasFocus); break; } @@ -3368,8 +3369,8 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str(), seq, toString(handled)); } - if (connection->status == Connection::STATUS_BROKEN || - connection->status == Connection::STATUS_ZOMBIE) { + if (connection->status == Connection::Status::BROKEN || + connection->status == Connection::Status::ZOMBIE) { return; } @@ -3396,8 +3397,8 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, // The connection appears to be unrecoverably broken. // Ignore already broken or zombie connections. - if (connection->status == Connection::STATUS_NORMAL) { - connection->status = Connection::STATUS_BROKEN; + if (connection->status == Connection::Status::NORMAL) { + connection->status = Connection::Status::BROKEN; if (notify) { // Notify other system components. @@ -3405,7 +3406,6 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str()); auto command = [this, connection]() REQUIRES(mLock) { - if (connection->status == Connection::STATUS_ZOMBIE) return; scoped_unlock unlock(mLock); mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken()); }; @@ -3541,7 +3541,7 @@ void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const sp<Connection>& connection, const CancelationOptions& options) { - if (connection->status == Connection::STATUS_BROKEN) { + if (connection->status == Connection::Status::BROKEN) { return; } @@ -3614,7 +3614,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( const sp<Connection>& connection) { - if (connection->status == Connection::STATUS_BROKEN) { + if (connection->status == Connection::Status::BROKEN) { return; } @@ -4694,8 +4694,10 @@ void InputDispatcher::setInputWindowsLocked( if (wallpaper != nullptr) { sp<Connection> wallpaperConnection = getConnectionLocked(wallpaper->getToken()); - synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, - options); + if (wallpaperConnection != nullptr) { + synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, + options); + } } } } @@ -5291,7 +5293,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { "status=%s, monitor=%s, responsive=%s\n", connection->inputChannel->getFd().get(), connection->getInputChannelName().c_str(), - connection->getWindowName().c_str(), connection->getStatusLabel(), + connection->getWindowName().c_str(), + ftl::enum_string(connection->status).c_str(), toString(connection->monitor), toString(connection->responsive)); if (!connection->outboundQueue.empty()) { @@ -5464,7 +5467,7 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection nsecs_t currentTime = now(); abortBrokenDispatchCycleLocked(currentTime, connection, notify); - connection->status = Connection::STATUS_ZOMBIE; + connection->status = Connection::Status::ZOMBIE; return OK; } @@ -5548,6 +5551,7 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { canceledWindows.c_str()); // Then clear the current touch state so we stop dispatching to them as well. + state.split = false; state.filterNonMonitors(); } return OK; @@ -5683,7 +5687,7 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } } traceWaitQueueLength(*connection); - if (restartEvent && connection->status == Connection::STATUS_NORMAL) { + if (restartEvent && connection->status == Connection::Status::NORMAL) { connection->outboundQueue.push_front(dispatchEntry); traceOutboundQueueLength(*connection); } else { @@ -5981,7 +5985,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& con mLock.lock(); - if (connection->status != Connection::STATUS_NORMAL) { + if (connection->status != Connection::Status::NORMAL) { connection->inputState.removeFallbackKey(originalKeyCode); return false; } diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index b1069497d3..db4228d862 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -30,7 +30,8 @@ namespace android { * fingers * * The pointer controller is responsible for providing synchronization and for tracking - * display orientation changes if needed. + * display orientation changes if needed. It works in the display panel's coordinate space, which + * is the same coordinate space used by InputReader. */ class PointerControllerInterface { protected: diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 15ba45945a..fcb56ef05c 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -188,8 +188,6 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { mOrientation = DISPLAY_ORIENTATION_0; - mDisplayWidth = 0; - mDisplayHeight = 0; const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); @@ -203,8 +201,6 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config->getDisplayViewportByType(ViewportType::INTERNAL); if (internalViewport) { mOrientation = getInverseRotation(internalViewport->orientation); - mDisplayWidth = internalViewport->deviceWidth; - mDisplayHeight = internalViewport->deviceHeight; } } @@ -335,14 +331,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); if (moved) { - float dx = deltaX; - float dy = deltaY; - // Rotate the delta from InputReader's un-rotated coordinate space to - // PointerController's rotated coordinate space that is oriented with the - // viewport. - rotateDelta(getInverseRotation(mOrientation), &dx, &dy); - - mPointerController->move(dx, dy); + mPointerController->move(deltaX, deltaY); } if (buttonsChanged) { @@ -353,10 +342,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { } mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - // Rotate the cursor position that is in PointerController's rotated coordinate space - // to InputReader's un-rotated coordinate space. - rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/, - mDisplayWidth, mDisplayHeight); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 88e947f7d5..9a8ca01294 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -105,8 +105,6 @@ private: VelocityControl mWheelYVelocityControl; int32_t mOrientation; - int32_t mDisplayWidth; - int32_t mDisplayHeight; std::shared_ptr<PointerControllerInterface> mPointerController; diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 8c30e38908..31a3d2e172 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -64,26 +64,6 @@ static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { } } -// Rotates the given point (x, y) by the supplied orientation. The width and height are the -// dimensions of the surface prior to this rotation being applied. -static void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) { - rotateDelta(orientation, &x, &y); - switch (orientation) { - case DISPLAY_ORIENTATION_90: - y += width; - break; - case DISPLAY_ORIENTATION_180: - x += width; - y += height; - break; - case DISPLAY_ORIENTATION_270: - x += height; - break; - default: - break; - } -} - // Returns true if the pointer should be reported as being down given the specified // button states. This determines whether the event is reported as a touch event. static bool isPointerDown(int32_t buttonState) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 3fe6fd130f..913c666a4a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1668,9 +1668,10 @@ void TouchInputMapper::updateTouchSpots() { mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->setButtonState(mCurrentRawState.buttonState); - setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId); + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, + mViewport.displayId); } bool TouchInputMapper::isTouchScreen() { @@ -2410,9 +2411,10 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u } if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) { - setTouchSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId()); + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, + mPointerController->getDisplayId()); } } else { mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); @@ -2562,7 +2564,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u // 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. - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); PointerProperties pointerProperties; pointerProperties.clear(); @@ -2819,12 +2822,13 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Move the pointer using a relative motion. // When using spots, the click will occur at the position of the anchor // spot and all other spots will move there. - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG; mPointerGesture.currentGestureIdBits.clear(); @@ -2850,7 +2854,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) && lastFingerCount == 1) { if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { #if DEBUG_GESTURES @@ -2918,7 +2923,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER; if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) { if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG; @@ -2952,7 +2958,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Move the pointer using a relative motion. // When using spots, the hover or drag will occur at the position of the anchor spot. - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } @@ -2974,7 +2980,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi down = false; } - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); @@ -3047,9 +3054,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mCurrentRawState.rawPointerData .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, &mPointerGesture.referenceTouchY); - auto [x, y] = getMouseCursorPosition(); - mPointerGesture.referenceGestureX = x; - mPointerGesture.referenceGestureY = y; + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); } // Clear the reference deltas for fingers not yet included in the reference calculation. @@ -3387,13 +3393,15 @@ void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uin if (!mCurrentCookedState.stylusIdBits.isEmpty()) { uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; - setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(), - mCurrentCookedState.cookedPointerData.pointerCoords[index].getY()); + mPointerController + ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(), + mCurrentCookedState.cookedPointerData.pointerCoords[index].getY()); hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); down = !hovering; - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[index]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3434,7 +3442,7 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); mPointerVelocityControl.move(when, &deltaX, &deltaY); - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } @@ -3442,7 +3450,8 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint down = isPointerDown(mCurrentRawState.buttonState); hovering = !down; - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3482,7 +3491,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin } int32_t displayId = mPointerController->getDisplayId(); - auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition(); + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); if (mPointerSimple.down && !down) { mPointerSimple.down = false; @@ -3648,9 +3658,7 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t p float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mDeviceMode == DeviceMode::POINTER) { - auto [x, y] = getMouseCursorPosition(); - xCursorPosition = x; - yCursorPosition = y; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); } const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); @@ -3999,56 +4007,4 @@ std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() { return std::nullopt; } -void TouchInputMapper::moveMouseCursor(float dx, float dy) const { - // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate - // space that is oriented with the viewport. - rotateDelta(mViewport.orientation, &dx, &dy); - - mPointerController->move(dx, dy); -} - -std::pair<float, float> TouchInputMapper::getMouseCursorPosition() const { - float x = 0; - float y = 0; - mPointerController->getPosition(&x, &y); - - if (!mViewport.isValid()) return {x, y}; - - // Convert from PointerController's rotated coordinate space that is oriented with the viewport - // to InputReader's un-rotated coordinate space. - const int32_t orientation = getInverseRotation(mViewport.orientation); - rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight); - return {x, y}; -} - -void TouchInputMapper::setMouseCursorPosition(float x, float y) const { - // Convert from InputReader's un-rotated coordinate space to PointerController's rotated - // coordinate space that is oriented with the viewport. - rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); - - mPointerController->setPosition(x, y); -} - -void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, - BitSet32 spotIdBits, int32_t displayId) { - std::array<PointerCoords, MAX_POINTERS> outSpotCoords{}; - - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { - const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()]; - float x = spotCoords[index].getX(); - float y = spotCoords[index].getY(); - float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - - // Convert from InputReader's un-rotated coordinate space to PointerController's rotated - // coordinate space. - rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); - - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x); - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - } - - mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId); -} - } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 496491b62d..9b020a609a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -803,14 +803,6 @@ private: const char* modeToString(DeviceMode deviceMode); void rotateAndScale(float& x, float& y) const; - - // Wrapper methods for interfacing with PointerController. These are used to convert points - // between the coordinate spaces used by InputReader and PointerController, if they differ. - void moveMouseCursor(float dx, float dy) const; - std::pair<float, float> getMouseCursorPosition() const; - void setMouseCursorPosition(float x, float y) const; - void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, - BitSet32 spotIdBits, int32_t displayId); }; } // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index d8fd16c5e6..39c52628d4 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -219,21 +219,21 @@ public: template <class T> T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage, std::unique_lock<std::mutex>& lock) REQUIRES(mLock) { - const std::chrono::time_point start = std::chrono::steady_clock::now(); - std::chrono::duration timeToWait = timeout + 100ms; // provide some slack - // If there is an ANR, Dispatcher won't be idle because there are still events // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle // before checking if ANR was called. // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need // to provide it some time to act. 100ms seems reasonable. - mNotifyAnr.wait_for(lock, timeToWait, - [&storage]() REQUIRES(mLock) { return !storage.empty(); }); - const std::chrono::duration waited = std::chrono::steady_clock::now() - start; - if (storage.empty()) { + std::chrono::duration timeToWait = timeout + 100ms; // provide some slack + const std::chrono::time_point start = std::chrono::steady_clock::now(); + std::optional<T> token = + getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr); + if (!token.has_value()) { ADD_FAILURE() << "Did not receive the ANR callback"; return {}; } + + const std::chrono::duration waited = std::chrono::steady_clock::now() - start; // Ensure that the ANR didn't get raised too early. We can't be too strict here because // the dispatcher started counting before this function was called if (std::chrono::abs(timeout - waited) > 100ms) { @@ -243,9 +243,24 @@ public: << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count() << "ms instead"; } - T token = storage.front(); + return *token; + } + + template <class T> + std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout, + std::queue<T>& storage, + std::unique_lock<std::mutex>& lock, + std::condition_variable& condition) + REQUIRES(mLock) { + condition.wait_for(lock, timeout, + [&storage]() REQUIRES(mLock) { return !storage.empty(); }); + if (storage.empty()) { + ADD_FAILURE() << "Did not receive the expected callback"; + return std::nullopt; + } + T item = storage.front(); storage.pop(); - return token; + return std::make_optional(item); } void assertNotifyAnrWasNotCalled() { @@ -303,6 +318,16 @@ public: mNotifyDropWindowWasCalled = false; } + void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + std::optional<sp<IBinder>> receivedToken = + getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock, + mNotifyInputChannelBroken); + ASSERT_TRUE(receivedToken.has_value()); + ASSERT_EQ(token, *receivedToken); + } + private: std::mutex mLock; std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock); @@ -321,6 +346,8 @@ private: std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock); std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock); std::condition_variable mNotifyAnr; + std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock); + std::condition_variable mNotifyInputChannelBroken; sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock); bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; @@ -361,7 +388,11 @@ private: mNotifyAnr.notify_all(); } - void notifyInputChannelBroken(const sp<IBinder>&) override {} + void notifyInputChannelBroken(const sp<IBinder>& connectionToken) override { + std::scoped_lock lock(mLock); + mBrokenInputChannels.push(connectionToken); + mNotifyInputChannelBroken.notify_all(); + } void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {} @@ -842,7 +873,6 @@ public: FocusEvent* focusEvent = static_cast<FocusEvent*>(event); EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); - EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); } void consumeCaptureEvent(bool hasCapture) { @@ -1176,6 +1206,8 @@ public: mInfo.ownerUid = ownerUid; } + void destroyReceiver() { mInputReceiver = nullptr; } + private: const std::string mName; std::unique_ptr<FakeInputReceiver> mInputReceiver; @@ -1439,6 +1471,23 @@ static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs( return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request); } +/** + * When a window unexpectedly disposes of its input channel, policy should be notified about the + * broken channel. + */ +TEST_F(InputDispatcherTest, WhenInputChannelBreaks_PolicyIsNotified) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Window that breaks its input channel", + ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + // Window closes its channel, but the window remains. + window->destroyReceiver(); + mFakePolicy->assertNotifyInputChannelBrokenWasCalled(window->getInfo()->token); +} + TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = @@ -1579,6 +1628,53 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCance } /** + * Same test as WhenForegroundWindowDisappears_WallpaperTouchIsCanceled above, + * with the following differences: + * After ACTION_DOWN, Wallpaper window hangs up its channel, which forces the dispatcher to + * clean up the connection. + * This later may crash dispatcher during ACTION_CANCEL synthesis, if the dispatcher is not careful. + * Ensure that there's no crash in the dispatcher. + */ +TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> foregroundWindow = + new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + foregroundWindow->setHasWallpaper(true); + sp<FakeWindowHandle> wallpaperWindow = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaperWindow->setType(WindowInfo::Type::WALLPAPER); + constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both foreground window and its wallpaper should receive the touch down + foregroundWindow->consumeMotionDown(); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + foregroundWindow->consumeMotionMove(); + wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // Wallpaper closes its channel, but the window remains. + wallpaperWindow->destroyReceiver(); + mFakePolicy->assertNotifyInputChannelBrokenWasCalled(wallpaperWindow->getInfo()->token); + + // Now the foreground window goes away, but the wallpaper stays, even though its channel + // is no longer valid. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}}); + foregroundWindow->consumeMotionCancel(); +} + +/** * A single window that receives touch (on top), and a wallpaper window underneath it. * The top window gets a multitouch gesture. * Ensure that wallpaper gets the same gesture. @@ -2670,6 +2766,13 @@ public: expectedDisplayId, expectedFlags); } + void consumeMotionPointerDown(int32_t pointerIdx) { + int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN | + (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, ADISPLAY_ID_DEFAULT, + 0 /*expectedFlags*/); + } + MotionEvent* consumeMotion() { InputEvent* event = mInputReceiver->consume(); if (!event) { @@ -2878,6 +2981,91 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { 0 /*expectedFlags*/); } +TEST_F(InputDispatcherTest, GestureMonitor_SplitIfNoWindowTouched) { + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + // Create a non touch modal window that supports split touch + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 100, 100)); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + // First finger down, no window touched. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); + + // Second finger down on window, the window should receive touch down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionPointerDown(1 /* pointerIndex */); +} + +TEST_F(InputDispatcherTest, GestureMonitor_NoSplitAfterPilfer) { + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + // Create a non touch modal window that supports split touch + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 100, 100)); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + // First finger down, no window touched. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); + + // Gesture monitor pilfer the pointers. + mDispatcher->pilferPointers(monitor.getToken()); + + // Second finger down on window, the window should not receive touch down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->assertNoEvents(); + monitor.consumeMotionPointerDown(1 /* pointerIndex */); +} + /** * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to * the device default right away. In the test scenario, we check both the default value, diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 1be5a96f67..b596708a72 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -78,6 +78,11 @@ cc_library_shared { "libsensorprivacy", "libpermission", ], + + pgo: { + sampling: true, + profile_file: "sensorservice/libsensorservice.profdata", + }, } cc_binary { diff --git a/services/sensorservice/ISensorHalWrapper.h b/services/sensorservice/ISensorHalWrapper.h new file mode 100644 index 0000000000..c9e089e449 --- /dev/null +++ b/services/sensorservice/ISensorHalWrapper.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_ISENSOR_HAL_WRAPPER_H +#define ANDROID_ISENSOR_HAL_WRAPPER_H + +#include <hardware/sensors.h> +#include <stdint.h> +#include <sys/types.h> + +#include "SensorService.h" + +namespace android { + +/** + * A wrapper for various types of HAL implementation, e.g. to distinguish HIDL and AIDL versions. + */ +class ISensorHalWrapper { +public: + class ICallback : public ISensorsCallback { + + void onDynamicSensorsConnected( + const std::vector<sensor_t> &dynamicSensorsAdded) = 0; + + void onDynamicSensorsDisconnected( + const std::vector<int32_t> &dynamicSensorHandlesRemoved) = 0; + }; + + /** + * Connects to the underlying sensors HAL. This should also be used for any reconnections + * due to HAL resets. + */ + virtual bool connect(ICallback *callback) = 0; + + /** + * Polls for available sensor events. This could be using the traditional sensors + * polling or from a FMQ. + */ + virtual ssize_t poll(sensors_event_t* buffer, size_t count) = 0; + + /** + * The below functions directly mirrors the sensors HAL definitions. + */ + virtual std::vector<sensor_t> getSensorsList() = 0; + + virtual status_t setOperationMode(SensorService::Mode mode) = 0; + + virtual status_t activate(int32_t sensorHandle, bool enabled) = 0; + + virtual status_t batch(int32_t sensorHandle, int64_t samplingPeriodNs, + int64_t maxReportLatencyNs) = 0; + + virtual status_t flush(int32_t sensorHandle) = 0; + + virtual status_t injectSensorData(const sensors_event_t *event) = 0; + + virtual status_t registerDirectChannel(const sensors_direct_mem_t *memory, + int32_t *channelHandle) = 0; + + virtual void unregisterDirectChannel(int32_t channelHandle) = 0; + + virtual status_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle, + const struct sensors_direct_cfg_t *config) = 0; +} + +} // namespace android + +#endif // ANDROID_ISENSOR_HAL_WRAPPER_H diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 2a4ff653f7..c67acbfbcd 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -23,31 +23,32 @@ #include <android-base/logging.h> #include <android/util/ProtoOutputStream.h> +#include <cutils/atomic.h> #include <frameworks/base/core/proto/android/service/sensor_service.proto.h> #include <sensors/convert.h> -#include <cutils/atomic.h> #include <utils/Errors.h> #include <utils/Singleton.h> -#include <cstddef> #include <chrono> #include <cinttypes> +#include <cstddef> #include <thread> using namespace android::hardware::sensors; using namespace android::hardware::sensors::V1_0; using namespace android::hardware::sensors::V1_0::implementation; +using android::hardware::hidl_vec; +using android::hardware::Return; using android::hardware::sensors::V2_0::EventQueueFlagBits; using android::hardware::sensors::V2_0::WakeLockQueueFlagBits; using android::hardware::sensors::V2_1::ISensorsCallback; -using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo; -using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos; using android::hardware::sensors::V2_1::implementation::convertToNewEvents; +using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos; +using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo; +using android::hardware::sensors::V2_1::implementation::convertToSensor; using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV1_0; using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_0; using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_1; -using android::hardware::hidl_vec; -using android::hardware::Return; using android::SensorDeviceUtils::HidlServiceRegistrationWaiter; using android::util::ProtoOutputStream; @@ -73,7 +74,7 @@ status_t statusFromResult(Result result) { } } -template<typename EnumType> +template <typename EnumType> constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) { return static_cast<typename std::underlying_type<EnumType>::type>(value); } @@ -81,14 +82,13 @@ constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType valu // Used internally by the framework to wake the Event FMQ. These values must start after // the last value of EventQueueFlagBits enum EventQueueFlagBitsInternal : uint32_t { - INTERNAL_WAKE = 1 << 16, + INTERNAL_WAKE = 1 << 16, }; -} // anonymous namespace +} // anonymous namespace void SensorsHalDeathReceivier::serviceDied( - uint64_t /* cookie */, - const wp<::android::hidl::base::V1_0::IBase>& /* service */) { + uint64_t /* cookie */, const wp<::android::hidl::base::V1_0::IBase>& /* service */) { ALOGW("Sensors HAL died, attempting to reconnect."); SensorDevice::getInstance().prepareForReconnect(); } @@ -98,29 +98,36 @@ struct SensorsCallback : public ISensorsCallback { using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo; Return<void> onDynamicSensorsConnected_2_1( - const hidl_vec<SensorInfo> &dynamicSensorsAdded) override { - return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded); + const hidl_vec<SensorInfo>& dynamicSensorsAdded) override { + std::vector<sensor_t> sensors; + for (const V2_1::SensorInfo& info : dynamicSensorsAdded) { + sensor_t sensor; + convertToSensor(info, &sensor); + sensors.push_back(sensor); + } + + SensorDevice::getInstance().onDynamicSensorsConnected(sensors); + return Return<void>(); } Return<void> onDynamicSensorsConnected( - const hidl_vec<V1_0::SensorInfo> &dynamicSensorsAdded) override { - return SensorDevice::getInstance().onDynamicSensorsConnected( - convertToNewSensorInfos(dynamicSensorsAdded)); + const hidl_vec<V1_0::SensorInfo>& dynamicSensorsAdded) override { + return onDynamicSensorsConnected_2_1(convertToNewSensorInfos(dynamicSensorsAdded)); } Return<void> onDynamicSensorsDisconnected( - const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override { - return SensorDevice::getInstance().onDynamicSensorsDisconnected( - dynamicSensorHandlesRemoved); + const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override { + SensorDevice::getInstance().onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved); + return Return<void>(); } }; SensorDevice::SensorDevice() - : mHidlTransportErrors(20), - mRestartWaiter(new HidlServiceRegistrationWaiter()), - mEventQueueFlag(nullptr), - mWakeLockQueueFlag(nullptr), - mReconnecting(false) { + : mHidlTransportErrors(20), + mRestartWaiter(new HidlServiceRegistrationWaiter()), + mEventQueueFlag(nullptr), + mWakeLockQueueFlag(nullptr), + mReconnecting(false) { if (!connectHidlService()) { return; } @@ -132,61 +139,59 @@ SensorDevice::SensorDevice() } void SensorDevice::initializeSensorList() { - checkReturn(mSensors->getSensorsList( - [&](const auto &list) { - const size_t count = list.size(); - - mActivationCount.setCapacity(count); - Info model; - for (size_t i=0 ; i < count; i++) { - sensor_t sensor; - convertToSensor(convertToOldSensorInfo(list[i]), &sensor); - - if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) { - sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor); - - // Some sensors don't have a default resolution and will be left at 0. - // Don't crash in this case since CTS will verify that devices don't go to - // production with a resolution of 0. - if (sensor.resolution != 0) { - float quantizedRange = sensor.maxRange; - SensorDeviceUtils::quantizeValue( - &quantizedRange, sensor.resolution, /*factor=*/ 1); - // Only rewrite maxRange if the requantization produced a "significant" - // change, which is fairly arbitrarily defined as resolution / 8. - // Smaller deltas are permitted, as they may simply be due to floating - // point representation error, etc. - if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) { - ALOGW("%s's max range %.12f is not a multiple of the resolution " - "%.12f - updated to %.12f", sensor.name, sensor.maxRange, - sensor.resolution, quantizedRange); - sensor.maxRange = quantizedRange; - } - } else { - // Don't crash here or the device will go into a crashloop. - ALOGW("%s should have a non-zero resolution", sensor.name); - } - } + checkReturn(mSensors->getSensorsList([&](const auto& list) { + const size_t count = list.size(); - // Check and clamp power if it is 0 (or close) - constexpr float MIN_POWER_MA = 0.001; // 1 microAmp - if (sensor.power < MIN_POWER_MA) { - ALOGI("%s's reported power %f invalid, clamped to %f", - sensor.name, sensor.power, MIN_POWER_MA); - sensor.power = MIN_POWER_MA; + mActivationCount.setCapacity(count); + Info model; + for (size_t i = 0; i < count; i++) { + sensor_t sensor; + convertToSensor(list[i], &sensor); + + if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) { + sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor); + + // Some sensors don't have a default resolution and will be left at 0. + // Don't crash in this case since CTS will verify that devices don't go to + // production with a resolution of 0. + if (sensor.resolution != 0) { + float quantizedRange = sensor.maxRange; + SensorDeviceUtils::quantizeValue(&quantizedRange, sensor.resolution, + /*factor=*/1); + // Only rewrite maxRange if the requantization produced a "significant" + // change, which is fairly arbitrarily defined as resolution / 8. + // Smaller deltas are permitted, as they may simply be due to floating + // point representation error, etc. + if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) { + ALOGW("%s's max range %.12f is not a multiple of the resolution " + "%.12f - updated to %.12f", + sensor.name, sensor.maxRange, sensor.resolution, quantizedRange); + sensor.maxRange = quantizedRange; } - mSensorList.push_back(sensor); + } else { + // Don't crash here or the device will go into a crashloop. + ALOGW("%s should have a non-zero resolution", sensor.name); + } + } + + // Check and clamp power if it is 0 (or close) + constexpr float MIN_POWER_MA = 0.001; // 1 microAmp + if (sensor.power < MIN_POWER_MA) { + ALOGI("%s's reported power %f invalid, clamped to %f", sensor.name, sensor.power, + MIN_POWER_MA); + sensor.power = MIN_POWER_MA; + } + mSensorList.push_back(sensor); - mActivationCount.add(list[i].sensorHandle, model); + mActivationCount.add(list[i].sensorHandle, model); - // Only disable all sensors on HAL 1.0 since HAL 2.0 - // handles this in its initialize method - if (!mSensors->supportsMessageQueues()) { - checkReturn(mSensors->activate(list[i].sensorHandle, - 0 /* enabled */)); - } - } - })); + // Only disable all sensors on HAL 1.0 since HAL 2.0 + // handles this in its initialize method + if (!mSensors->supportsMessageQueues()) { + checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */)); + } + } + })); } SensorDevice::~SensorDevice() { @@ -231,7 +236,7 @@ SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() { // Poke ISensor service. If it has lingering connection from previous generation of // system server, it will kill itself. There is no intention to handle the poll result, // which will be done since the size is 0. - if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) { + if (mSensors->poll(0, [](auto, const auto&, const auto&) {}).isOk()) { // ok to continue connectionStatus = HalConnectionStatus::CONNECTED; break; @@ -278,23 +283,23 @@ SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_1() { SensorDevice::HalConnectionStatus SensorDevice::initializeHidlServiceV2_X() { HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN; - mWakeLockQueue = std::make_unique<WakeLockQueue>( - SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT, - true /* configureEventFlagWord */); + mWakeLockQueue = + std::make_unique<WakeLockQueue>(SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT, + true /* configureEventFlagWord */); hardware::EventFlag::deleteEventFlag(&mEventQueueFlag); - hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), &mEventQueueFlag); + hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), + &mEventQueueFlag); hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag); - hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), - &mWakeLockQueueFlag); + hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), &mWakeLockQueueFlag); - CHECK(mSensors != nullptr && mWakeLockQueue != nullptr && - mEventQueueFlag != nullptr && mWakeLockQueueFlag != nullptr); + CHECK(mSensors != nullptr && mWakeLockQueue != nullptr && mEventQueueFlag != nullptr && + mWakeLockQueueFlag != nullptr); - status_t status = checkReturnAndGetStatus(mSensors->initialize( - *mWakeLockQueue->getDesc(), - new SensorsCallback())); + mCallback = new SensorsCallback(); + status_t status = + checkReturnAndGetStatus(mSensors->initialize(*mWakeLockQueue->getDesc(), mCallback)); if (status != NO_ERROR) { connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT; @@ -326,7 +331,7 @@ void SensorDevice::reconnect() { mActivationCount.clear(); mSensorList.clear(); - if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) { + if (connectHidlService()) { initializeSensorList(); if (sensorHandlesChanged(previousSensorList, mSensorList)) { @@ -375,19 +380,17 @@ bool SensorDevice::sensorHandlesChanged(const std::vector<sensor_t>& oldSensorLi bool SensorDevice::sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor) { bool equivalent = true; if (prevSensor.handle != newSensor.handle || - (strcmp(prevSensor.vendor, newSensor.vendor) != 0) || - (strcmp(prevSensor.stringType, newSensor.stringType) != 0) || - (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) || - (prevSensor.version != newSensor.version) || - (prevSensor.type != newSensor.type) || - (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) || - (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) || - (std::abs(prevSensor.power - newSensor.power) > 0.001f) || - (prevSensor.minDelay != newSensor.minDelay) || - (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) || - (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) || - (prevSensor.maxDelay != newSensor.maxDelay) || - (prevSensor.flags != newSensor.flags)) { + (strcmp(prevSensor.vendor, newSensor.vendor) != 0) || + (strcmp(prevSensor.stringType, newSensor.stringType) != 0) || + (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) || + (prevSensor.version != newSensor.version) || (prevSensor.type != newSensor.type) || + (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) || + (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) || + (std::abs(prevSensor.power - newSensor.power) > 0.001f) || + (prevSensor.minDelay != newSensor.minDelay) || + (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) || + (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) || + (prevSensor.maxDelay != newSensor.maxDelay) || (prevSensor.flags != newSensor.flags)) { equivalent = false; } return equivalent; @@ -405,7 +408,7 @@ void SensorDevice::reactivateSensors(const DefaultKeyedVector<int, Info>& previo for (size_t j = 0; j < info.batchParams.size(); j++) { const BatchParams& batchParams = info.batchParams[j]; status_t res = batchLocked(info.batchParams.keyAt(j), handle, 0 /* flags */, - batchParams.mTSample, batchParams.mTBatch); + batchParams.mTSample, batchParams.mTBatch); if (res == NO_ERROR) { activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */); @@ -433,7 +436,7 @@ std::string SensorDevice::dump() const { mSensorList.size(), mActivationCount.size(), mDisabledClients.size()); Mutex::Autolock _l(mLock); - for (const auto & s : mSensorList) { + for (const auto& s : mSensorList) { int32_t handle = s.handle; const Info& info = mActivationCount.valueFor(handle); if (info.numActiveClients() == 0) continue; @@ -444,8 +447,9 @@ std::string SensorDevice::dump() const { for (size_t j = 0; j < info.batchParams.size(); j++) { const BatchParams& params = info.batchParams[j]; result.appendFormat("%.1f%s%s", params.mTSample / 1e6f, - isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "", - (j < info.batchParams.size() - 1) ? ", " : ""); + isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" + : "", + (j < info.batchParams.size() - 1) ? ", " : ""); } result.appendFormat("}, selected = %.2f ms; ", info.bestBatchParams.mTSample / 1e6f); @@ -453,8 +457,9 @@ std::string SensorDevice::dump() const { for (size_t j = 0; j < info.batchParams.size(); j++) { const BatchParams& params = info.batchParams[j]; result.appendFormat("%.1f%s%s", params.mTBatch / 1e6f, - isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "", - (j < info.batchParams.size() - 1) ? ", " : ""); + isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" + : "", + (j < info.batchParams.size() - 1) ? ", " : ""); } result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f); } @@ -472,29 +477,29 @@ std::string SensorDevice::dump() const { void SensorDevice::dump(ProtoOutputStream* proto) const { using namespace service::SensorDeviceProto; if (mSensors == nullptr) { - proto->write(INITIALIZED , false); + proto->write(INITIALIZED, false); return; } - proto->write(INITIALIZED , true); - proto->write(TOTAL_SENSORS , int(mSensorList.size())); - proto->write(ACTIVE_SENSORS , int(mActivationCount.size())); + proto->write(INITIALIZED, true); + proto->write(TOTAL_SENSORS, int(mSensorList.size())); + proto->write(ACTIVE_SENSORS, int(mActivationCount.size())); Mutex::Autolock _l(mLock); - for (const auto & s : mSensorList) { + for (const auto& s : mSensorList) { int32_t handle = s.handle; const Info& info = mActivationCount.valueFor(handle); if (info.numActiveClients() == 0) continue; uint64_t token = proto->start(SENSORS); - proto->write(SensorProto::HANDLE , handle); - proto->write(SensorProto::ACTIVE_COUNT , int(info.batchParams.size())); + proto->write(SensorProto::HANDLE, handle); + proto->write(SensorProto::ACTIVE_COUNT, int(info.batchParams.size())); for (size_t j = 0; j < info.batchParams.size(); j++) { const BatchParams& params = info.batchParams[j]; - proto->write(SensorProto::SAMPLING_PERIOD_MS , params.mTSample / 1e6f); - proto->write(SensorProto::BATCHING_PERIOD_MS , params.mTBatch / 1e6f); + proto->write(SensorProto::SAMPLING_PERIOD_MS, params.mTSample / 1e6f); + proto->write(SensorProto::BATCHING_PERIOD_MS, params.mTBatch / 1e6f); } - proto->write(SensorProto::SAMPLING_PERIOD_SELECTED , info.bestBatchParams.mTSample / 1e6f); - proto->write(SensorProto::BATCHING_PERIOD_SELECTED , info.bestBatchParams.mTBatch / 1e6f); + proto->write(SensorProto::SAMPLING_PERIOD_SELECTED, info.bestBatchParams.mTSample / 1e6f); + proto->write(SensorProto::BATCHING_PERIOD_SELECTED, info.bestBatchParams.mTBatch / 1e6f); proto->end(token); } } @@ -531,20 +536,19 @@ ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) { do { auto ret = mSensors->poll( - count, - [&](auto result, - const auto &events, - const auto &dynamicSensorsAdded) { + count, [&](auto result, const auto& events, const auto& dynamicSensorsAdded) { if (result == Result::OK) { convertToSensorEventsAndQuantize(convertToNewEvents(events), - convertToNewSensorInfos(dynamicSensorsAdded), buffer); + convertToNewSensorInfos( + dynamicSensorsAdded), + buffer); err = (ssize_t)events.size(); } else { err = statusFromResult(result); } }); - if (ret.isOk()) { + if (ret.isOk()) { hidlTransportError = false; } else { hidlTransportError = true; @@ -559,7 +563,7 @@ ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) { } } while (hidlTransportError); - if(numHidlTransportErrors > 0) { + if (numHidlTransportErrors > 0) { ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors); HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors); mHidlTransportErrors.add(errLog); @@ -581,7 +585,8 @@ ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead // events is not available, then read() would return no events, possibly introducing // additional latency in delivering events to applications. mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) | - asBaseType(INTERNAL_WAKE), &eventFlagState); + asBaseType(INTERNAL_WAKE), + &eventFlagState); availableEvents = mSensors->getEventQueue()->availableToRead(); if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) { @@ -600,47 +605,39 @@ ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead for (size_t i = 0; i < eventsToRead; i++) { convertToSensorEvent(mEventBuffer[i], &buffer[i]); android::SensorDeviceUtils::quantizeSensorEventValues(&buffer[i], - getResolutionForSensor(buffer[i].sensor)); + getResolutionForSensor( + buffer[i].sensor)); } eventsRead = eventsToRead; } else { - ALOGW("Failed to read %zu events, currently %zu events available", - eventsToRead, availableEvents); + ALOGW("Failed to read %zu events, currently %zu events available", eventsToRead, + availableEvents); } } return eventsRead; } -Return<void> SensorDevice::onDynamicSensorsConnected( - const hidl_vec<SensorInfo> &dynamicSensorsAdded) { +void SensorDevice::onDynamicSensorsConnected(const std::vector<sensor_t>& dynamicSensorsAdded) { std::unique_lock<std::mutex> lock(mDynamicSensorsMutex); // Allocate a sensor_t structure for each dynamic sensor added and insert // it into the dictionary of connected dynamic sensors keyed by handle. for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) { - const SensorInfo &info = dynamicSensorsAdded[i]; + const sensor_t& sensor = dynamicSensorsAdded[i]; - auto it = mConnectedDynamicSensors.find(info.sensorHandle); + auto it = mConnectedDynamicSensors.find(sensor.handle); CHECK(it == mConnectedDynamicSensors.end()); - sensor_t *sensor = new sensor_t(); - convertToSensor(convertToOldSensorInfo(info), sensor); - - mConnectedDynamicSensors.insert( - std::make_pair(sensor->handle, sensor)); + mConnectedDynamicSensors.insert(std::make_pair(sensor.handle, sensor)); } mDynamicSensorsCv.notify_all(); - - return Return<void>(); } -Return<void> SensorDevice::onDynamicSensorsDisconnected( - const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) { - (void) dynamicSensorHandlesRemoved; +void SensorDevice::onDynamicSensorsDisconnected( + const std::vector<int32_t>& /* dynamicSensorHandlesRemoved */) { // TODO: Currently dynamic sensors do not seem to be removed - return Return<void>(); } void SensorDevice::writeWakeLockHandled(uint32_t count) { @@ -653,7 +650,7 @@ void SensorDevice::writeWakeLockHandled(uint32_t count) { } } -void SensorDevice::autoDisable(void *ident, int handle) { +void SensorDevice::autoDisable(void* ident, int handle) { Mutex::Autolock _l(mLock); ssize_t activationIndex = mActivationCount.indexOfKey(handle); if (activationIndex < 0) { @@ -687,15 +684,15 @@ status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { Info& info(mActivationCount.editValueAt(activationIndex)); ALOGD_IF(DEBUG_CONNECTIONS, - "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", - ident, handle, enabled, info.batchParams.size()); + "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", ident, + handle, enabled, info.batchParams.size()); if (enabled) { ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident)); if (isClientDisabledLocked(ident)) { - ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d", - ident, handle); + ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d", ident, + handle); return NO_ERROR; } @@ -714,7 +711,6 @@ status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { // dictionary. auto it = mConnectedDynamicSensors.find(handle); if (it != mConnectedDynamicSensors.end()) { - delete it->second; mConnectedDynamicSensors.erase(it); } @@ -726,11 +722,10 @@ status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { // Call batch for this sensor with the previously calculated best effort // batch_rate and timeout. One of the apps has unregistered for sensor // events, and the best effort batch parameters might have changed. - ALOGD_IF(DEBUG_CONNECTIONS, - "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, handle, - info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch); - checkReturn(mSensors->batch( - handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch)); + ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, + handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch); + checkReturn(mSensors->batch(handle, info.bestBatchParams.mTSample, + info.bestBatchParams.mTBatch)); } } else { // sensor wasn't enabled for this ident @@ -767,12 +762,8 @@ status_t SensorDevice::doActivateHardwareLocked(int handle, bool enabled) { return err; } -status_t SensorDevice::batch( - void* ident, - int handle, - int flags, - int64_t samplingPeriodNs, - int64_t maxBatchReportLatencyNs) { +status_t SensorDevice::batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { if (mSensors == nullptr) return NO_INIT; if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) { @@ -783,7 +774,8 @@ status_t SensorDevice::batch( } ALOGD_IF(DEBUG_CONNECTIONS, - "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64, + "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 + " timeout=%" PRId64, ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); Mutex::Autolock _l(mLock); @@ -807,25 +799,24 @@ status_t SensorDevice::batchLocked(void* ident, int handle, int flags, int64_t s info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs); } - status_t err = updateBatchParamsLocked(handle, info); + status_t err = updateBatchParamsLocked(handle, info); if (err != NO_ERROR) { - ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s", - mSensors.get(), handle, info.bestBatchParams.mTSample, - info.bestBatchParams.mTBatch, strerror(-err)); + ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s", mSensors.get(), + handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch, strerror(-err)); info.removeBatchParamsForIdent(ident); } return err; } -status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) { +status_t SensorDevice::updateBatchParamsLocked(int handle, Info& info) { BatchParams prevBestBatchParams = info.bestBatchParams; // Find the minimum of all timeouts and batch_rates for this sensor. info.selectBatchParams(); ALOGD_IF(DEBUG_CONNECTIONS, - "\t>>> curr_period=%" PRId64 " min_period=%" PRId64 - " curr_timeout=%" PRId64 " min_timeout=%" PRId64, + "\t>>> curr_period=%" PRId64 " min_period=%" PRId64 " curr_timeout=%" PRId64 + " min_timeout=%" PRId64, prevBestBatchParams.mTSample, info.bestBatchParams.mTSample, prevBestBatchParams.mTBatch, info.bestBatchParams.mTBatch); @@ -834,8 +825,8 @@ status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) { if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) { ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch); - err = checkReturnAndGetStatus(mSensors->batch( - handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch)); + err = checkReturnAndGetStatus(mSensors->batch(handle, info.bestBatchParams.mTSample, + info.bestBatchParams.mTBatch)); } return err; @@ -866,8 +857,8 @@ bool SensorDevice::isClientDisabledLocked(void* ident) const { return mDisabledClients.count(ident) > 0; } -std::vector<void *> SensorDevice::getDisabledClientsLocked() const { - std::vector<void *> vec; +std::vector<void*> SensorDevice::getDisabledClientsLocked() const { + std::vector<void*> vec; for (const auto& it : mDisabledClients) { vec.push_back(it.first); } @@ -896,7 +887,7 @@ void SensorDevice::setUidStateForConnection(void* ident, SensorService::UidState addDisabledReasonForIdentLocked(ident, DisabledReason::DISABLED_REASON_UID_IDLE); } - for (size_t i = 0; i< mActivationCount.size(); ++i) { + for (size_t i = 0; i < mActivationCount.size(); ++i) { int handle = mActivationCount.keyAt(i); Info& info = mActivationCount.editValueAt(i); @@ -905,8 +896,7 @@ void SensorDevice::setUidStateForConnection(void* ident, SensorService::UidState bool disable = info.numActiveClients() == 0 && info.isActive; bool enable = info.numActiveClients() > 0 && !info.isActive; - if ((enable || disable) && - doActivateHardwareLocked(handle, enable) == NO_ERROR) { + if ((enable || disable) && doActivateHardwareLocked(handle, enable) == NO_ERROR) { info.isActive = enable; } } @@ -941,22 +931,21 @@ void SensorDevice::enableAllSensors() { if (mSensors == nullptr) return; Mutex::Autolock _l(mLock); - for (void *client : getDisabledClientsLocked()) { - removeDisabledReasonForIdentLocked( - client, DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED); + for (void* client : getDisabledClientsLocked()) { + removeDisabledReasonForIdentLocked(client, + DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED); } - for (size_t i = 0; i< mActivationCount.size(); ++i) { + for (size_t i = 0; i < mActivationCount.size(); ++i) { Info& info = mActivationCount.editValueAt(i); if (info.batchParams.isEmpty()) continue; info.selectBatchParams(); const int sensor_handle = mActivationCount.keyAt(i); ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ", - sensor_handle); - status_t err = checkReturnAndGetStatus(mSensors->batch( - sensor_handle, - info.bestBatchParams.mTSample, - info.bestBatchParams.mTBatch)); + sensor_handle); + status_t err = checkReturnAndGetStatus(mSensors->batch(sensor_handle, + info.bestBatchParams.mTSample, + info.bestBatchParams.mTBatch)); ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err)); if (err == NO_ERROR) { @@ -973,38 +962,36 @@ void SensorDevice::enableAllSensors() { void SensorDevice::disableAllSensors() { if (mSensors == nullptr) return; Mutex::Autolock _l(mLock); - for (size_t i = 0; i< mActivationCount.size(); ++i) { + for (size_t i = 0; i < mActivationCount.size(); ++i) { Info& info = mActivationCount.editValueAt(i); // Check if this sensor has been activated previously and disable it. if (info.batchParams.size() > 0) { - const int sensor_handle = mActivationCount.keyAt(i); - ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ", - sensor_handle); - checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */)); - - // Add all the connections that were registered for this sensor to the disabled - // clients list. - for (size_t j = 0; j < info.batchParams.size(); ++j) { - addDisabledReasonForIdentLocked( - info.batchParams.keyAt(j), DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED); - ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j)); - } - - info.isActive = false; + const int sensor_handle = mActivationCount.keyAt(i); + ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ", + sensor_handle); + checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */)); + + // Add all the connections that were registered for this sensor to the disabled + // clients list. + for (size_t j = 0; j < info.batchParams.size(); ++j) { + addDisabledReasonForIdentLocked(info.batchParams.keyAt(j), + DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED); + ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j)); + } + + info.isActive = false; } } } -status_t SensorDevice::injectSensorData( - const sensors_event_t *injected_sensor_event) { +status_t SensorDevice::injectSensorData(const sensors_event_t* injected_sensor_event) { if (mSensors == nullptr) return NO_INIT; ALOGD_IF(DEBUG_CONNECTIONS, - "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f", - injected_sensor_event->sensor, - injected_sensor_event->timestamp, injected_sensor_event->data[0], - injected_sensor_event->data[1], injected_sensor_event->data[2], - injected_sensor_event->data[3], injected_sensor_event->data[4], - injected_sensor_event->data[5]); + "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f", + injected_sensor_event->sensor, injected_sensor_event->timestamp, + injected_sensor_event->data[0], injected_sensor_event->data[1], + injected_sensor_event->data[2], injected_sensor_event->data[3], + injected_sensor_event->data[4], injected_sensor_event->data[5]); Event ev; V2_1::implementation::convertFromSensorEvent(*injected_sensor_event, &ev); @@ -1014,8 +1001,8 @@ status_t SensorDevice::injectSensorData( status_t SensorDevice::setMode(uint32_t mode) { if (mSensors == nullptr) return NO_INIT; - return checkReturnAndGetStatus(mSensors->setOperationMode( - static_cast<hardware::sensors::V1_0::OperationMode>(mode))); + return checkReturnAndGetStatus( + mSensors->setOperationMode(static_cast<hardware::sensors::V1_0::OperationMode>(mode))); } int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) { @@ -1041,21 +1028,20 @@ int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) format = SharedMemFormat::SENSORS_EVENT; SharedMemInfo mem = { - .type = type, - .format = format, - .size = static_cast<uint32_t>(memory->size), - .memoryHandle = memory->handle, + .type = type, + .format = format, + .size = static_cast<uint32_t>(memory->size), + .memoryHandle = memory->handle, }; int32_t ret; - checkReturn(mSensors->registerDirectChannel(mem, - [&ret](auto result, auto channelHandle) { - if (result == Result::OK) { - ret = channelHandle; - } else { - ret = statusFromResult(result); - } - })); + checkReturn(mSensors->registerDirectChannel(mem, [&ret](auto result, auto channelHandle) { + if (result == Result::OK) { + ret = channelHandle; + } else { + ret = statusFromResult(result); + } + })); return ret; } @@ -1065,13 +1051,13 @@ void SensorDevice::unregisterDirectChannel(int32_t channelHandle) { checkReturn(mSensors->unregisterDirectChannel(channelHandle)); } -int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, - int32_t channelHandle, const struct sensors_direct_cfg_t *config) { +int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle, + const struct sensors_direct_cfg_t* config) { if (mSensors == nullptr) return NO_INIT; Mutex::Autolock _l(mLock); RateLevel rate; - switch(config->rate_level) { + switch (config->rate_level) { case SENSOR_DIRECT_RATE_STOP: rate = RateLevel::STOP; break; @@ -1090,17 +1076,17 @@ int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t ret; checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate, - [&ret, rate] (auto result, auto token) { - if (rate == RateLevel::STOP) { - ret = statusFromResult(result); - } else { - if (result == Result::OK) { - ret = token; - } else { - ret = statusFromResult(result); - } - } - })); + [&ret, rate](auto result, auto token) { + if (rate == RateLevel::STOP) { + ret = statusFromResult(result); + } else { + if (result == Result::OK) { + ret = token; + } else { + ret = statusFromResult(result); + } + } + })); return ret; } @@ -1118,13 +1104,12 @@ int SensorDevice::Info::numActiveClients() const { return num; } -status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int, - int64_t samplingPeriodNs, +status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int, int64_t samplingPeriodNs, int64_t maxBatchReportLatencyNs) { ssize_t index = batchParams.indexOfKey(ident); if (index < 0) { - ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 - " timeout=%" PRId64 ") failed (%s)", + ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 " timeout=%" PRId64 + ") failed (%s)", ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index)); return BAD_INDEX; } @@ -1168,12 +1153,11 @@ bool SensorDevice::isDirectReportSupported() const { return mIsDirectReportSupported; } -void SensorDevice::convertToSensorEvent( - const Event &src, sensors_event_t *dst) { +void SensorDevice::convertToSensorEvent(const Event& src, sensors_event_t* dst) { V2_1::implementation::convertToSensorEvent(src, dst); if (src.sensorType == V2_1::SensorType::DYNAMIC_SENSOR_META) { - const DynamicSensorInfo &dyn = src.u.dynamic; + const DynamicSensorInfo& dyn = src.u.dynamic; dst->dynamic_sensor_meta.connected = dyn.connected; dst->dynamic_sensor_meta.handle = dyn.sensorHandle; @@ -1184,56 +1168,53 @@ void SensorDevice::convertToSensorEvent( // marks it as oneway. auto it = mConnectedDynamicSensors.find(dyn.sensorHandle); if (it == mConnectedDynamicSensors.end()) { - mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT, - [&, dyn]{ - return mConnectedDynamicSensors.find(dyn.sensorHandle) - != mConnectedDynamicSensors.end(); + mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT, [&, dyn] { + return mConnectedDynamicSensors.find(dyn.sensorHandle) != + mConnectedDynamicSensors.end(); }); it = mConnectedDynamicSensors.find(dyn.sensorHandle); CHECK(it != mConnectedDynamicSensors.end()); } - dst->dynamic_sensor_meta.sensor = it->second; + dst->dynamic_sensor_meta.sensor = &it->second; - memcpy(dst->dynamic_sensor_meta.uuid, - dyn.uuid.data(), + memcpy(dst->dynamic_sensor_meta.uuid, dyn.uuid.data(), sizeof(dst->dynamic_sensor_meta.uuid)); } } } -void SensorDevice::convertToSensorEventsAndQuantize( - const hidl_vec<Event> &src, - const hidl_vec<SensorInfo> &dynamicSensorsAdded, - sensors_event_t *dst) { - - if (dynamicSensorsAdded.size() > 0) { - onDynamicSensorsConnected(dynamicSensorsAdded); +void SensorDevice::convertToSensorEventsAndQuantize(const hidl_vec<Event>& src, + const hidl_vec<SensorInfo>& dynamicSensorsAdded, + sensors_event_t* dst) { + if (dynamicSensorsAdded.size() > 0 && mCallback != nullptr) { + mCallback->onDynamicSensorsConnected_2_1(dynamicSensorsAdded); } for (size_t i = 0; i < src.size(); ++i) { V2_1::implementation::convertToSensorEvent(src[i], &dst[i]); android::SensorDeviceUtils::quantizeSensorEventValues(&dst[i], - getResolutionForSensor(dst[i].sensor)); + getResolutionForSensor( + dst[i].sensor)); } } float SensorDevice::getResolutionForSensor(int sensorHandle) { for (size_t i = 0; i < mSensorList.size(); i++) { - if (sensorHandle == mSensorList[i].handle) { - return mSensorList[i].resolution; - } + if (sensorHandle == mSensorList[i].handle) { + return mSensorList[i].resolution; + } } auto it = mConnectedDynamicSensors.find(sensorHandle); if (it != mConnectedDynamicSensors.end()) { - return it->second->resolution; + return it->second.resolution; } return 0; } -void SensorDevice::handleHidlDeath(const std::string & detail) { +void SensorDevice::handleHidlDeath(const std::string& detail) { if (!mSensors->supportsMessageQueues()) { // restart is the only option at present. LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str()); diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index e90a02ff2e..314ddb8758 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -17,14 +17,14 @@ #ifndef ANDROID_SENSOR_DEVICE_H #define ANDROID_SENSOR_DEVICE_H +#include "ISensorsWrapper.h" #include "SensorDeviceUtils.h" #include "SensorService.h" #include "SensorServiceUtils.h" -#include "ISensorsWrapper.h" #include <fmq/MessageQueue.h> -#include <sensor/SensorEventQueue.h> #include <sensor/Sensor.h> +#include <sensor/SensorEventQueue.h> #include <stdint.h> #include <sys/types.h> #include <utils/KeyedVector.h> @@ -33,8 +33,8 @@ #include <utils/Timers.h> #include <algorithm> //std::max std::min -#include <unordered_map> #include <string> +#include <unordered_map> #include <vector> #include "RingBuffer.h" @@ -43,18 +43,18 @@ namespace android { +using Result = ::android::hardware::sensors::V1_0::Result; + // --------------------------------------------------------------------------- class SensorsHalDeathReceivier : public android::hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& service) override; }; -class SensorDevice : public Singleton<SensorDevice>, - public SensorServiceUtil::Dumpable { +class SensorDevice : public Singleton<SensorDevice>, public SensorServiceUtil::Dumpable { public: class HidlTransportErrorLog { - public: - + public: HidlTransportErrorLog() { mTs = 0; mCount = 0; @@ -67,7 +67,7 @@ public: String8 toString() const { String8 result; - struct tm *timeInfo = localtime(&mTs); + struct tm* timeInfo = localtime(&mTs); result.appendFormat("%02d:%02d:%02d :: %d", timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec, mCount); return result; @@ -75,7 +75,7 @@ public: private: time_t mTs; // timestamp of the error - int mCount; // number of transport errors observed + int mCount; // number of transport errors observed }; ~SensorDevice(); @@ -100,29 +100,24 @@ public: status_t setMode(uint32_t mode); bool isDirectReportSupported() const; - int32_t registerDirectChannel(const sensors_direct_mem_t *memory); + int32_t registerDirectChannel(const sensors_direct_mem_t* memory); void unregisterDirectChannel(int32_t channelHandle); - int32_t configureDirectChannel(int32_t sensorHandle, - int32_t channelHandle, const struct sensors_direct_cfg_t *config); + int32_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle, + const struct sensors_direct_cfg_t* config); void disableAllSensors(); void enableAllSensors(); - void autoDisable(void *ident, int handle); + void autoDisable(void* ident, int handle); - status_t injectSensorData(const sensors_event_t *event); - void notifyConnectionDestroyed(void *ident); + status_t injectSensorData(const sensors_event_t* event); + void notifyConnectionDestroyed(void* ident); - using Result = ::android::hardware::sensors::V1_0::Result; - hardware::Return<void> onDynamicSensorsConnected( - const hardware::hidl_vec<hardware::sensors::V2_1::SensorInfo> &dynamicSensorsAdded); - hardware::Return<void> onDynamicSensorsDisconnected( - const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved); + void onDynamicSensorsConnected(const std::vector<sensor_t>& dynamicSensorsAdded); + void onDynamicSensorsDisconnected(const std::vector<int32_t>& dynamicSensorHandlesRemoved); void setUidStateForConnection(void* ident, SensorService::UidState state); - bool isReconnecting() const { - return mReconnecting; - } + bool isReconnecting() const { return mReconnecting; } bool isSensorActive(int handle) const; @@ -133,12 +128,14 @@ public: // Dumpable virtual std::string dump() const override; virtual void dump(util::ProtoOutputStream* proto) const override; + private: friend class Singleton<SensorDevice>; sp<::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase> mSensors; + sp<::android::hardware::sensors::V2_1::ISensorsCallback> mCallback; std::vector<sensor_t> mSensorList; - std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors; + std::unordered_map<int32_t, sensor_t> mConnectedDynamicSensors; // A bug in the Sensors HIDL spec which marks onDynamicSensorsConnected as oneway causes dynamic // meta events and onDynamicSensorsConnected to be received out of order. This mutex + CV are @@ -148,26 +145,26 @@ private: std::condition_variable mDynamicSensorsCv; static constexpr std::chrono::seconds MAX_DYN_SENSOR_WAIT{5}; - static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz - mutable Mutex mLock; // protect mActivationCount[].batchParams + static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz + mutable Mutex mLock; // protect mActivationCount[].batchParams // fixed-size array after construction // Struct to store all the parameters(samplingPeriod, maxBatchReportLatency and flags) from // batch call. For continous mode clients, maxBatchReportLatency is set to zero. struct BatchParams { - nsecs_t mTSample, mTBatch; - BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {} - BatchParams(nsecs_t tSample, nsecs_t tBatch): mTSample(tSample), mTBatch(tBatch) {} - bool operator != (const BatchParams& other) { - return !(mTSample == other.mTSample && mTBatch == other.mTBatch); - } - // Merge another parameter with this one. The updated mTSample will be the min of the two. - // The update mTBatch will be the min of original mTBatch and the apparent batch period - // of the other. the apparent batch is the maximum of mTBatch and mTSample, - void merge(const BatchParams &other) { - mTSample = std::min(mTSample, other.mTSample); - mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample)); - } + nsecs_t mTSample, mTBatch; + BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {} + BatchParams(nsecs_t tSample, nsecs_t tBatch) : mTSample(tSample), mTBatch(tBatch) {} + bool operator!=(const BatchParams& other) { + return !(mTSample == other.mTSample && mTBatch == other.mTBatch); + } + // Merge another parameter with this one. The updated mTSample will be the min of the two. + // The update mTBatch will be the min of original mTBatch and the apparent batch period + // of the other. the apparent batch is the maximum of mTBatch and mTSample, + void merge(const BatchParams& other) { + mTSample = std::min(mTSample, other.mTSample); + mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample)); + } }; // Store batch parameters in the KeyedVector and the optimal batch_rate and timeout in @@ -225,7 +222,7 @@ private: static_assert(DisabledReason::DISABLED_REASON_MAX < sizeof(uint8_t) * CHAR_BIT); // Use this map to determine which client is activated or deactivated. - std::unordered_map<void *, uint8_t> mDisabledClients; + std::unordered_map<void*, uint8_t> mDisabledClients; void addDisabledReasonForIdentLocked(void* ident, DisabledReason reason); void removeDisabledReasonForIdentLocked(void* ident, DisabledReason reason); @@ -239,8 +236,8 @@ private: static bool sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor); enum HalConnectionStatus { - CONNECTED, // Successfully connected to the HAL - DOES_NOT_EXIST, // Could not find the HAL + CONNECTED, // Successfully connected to the HAL + DOES_NOT_EXIST, // Could not find the HAL FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize UNKNOWN, }; @@ -258,32 +255,32 @@ private: status_t updateBatchParamsLocked(int handle, Info& info); status_t doActivateHardwareLocked(int handle, bool enable); - void handleHidlDeath(const std::string &detail); - template<typename T> + void handleHidlDeath(const std::string& detail); + template <typename T> void checkReturn(const Return<T>& ret) { if (!ret.isOk()) { handleHidlDeath(ret.description()); } } + status_t checkReturnAndGetStatus(const Return<Result>& ret); - //TODO(b/67425500): remove waiter after bug is resolved. + // TODO(b/67425500): remove waiter after bug is resolved. sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter; bool isClientDisabled(void* ident) const; bool isClientDisabledLocked(void* ident) const; - std::vector<void *> getDisabledClientsLocked() const; + std::vector<void*> getDisabledClientsLocked() const; bool clientHasNoAccessLocked(void* ident) const; using Event = hardware::sensors::V2_1::Event; using SensorInfo = hardware::sensors::V2_1::SensorInfo; - void convertToSensorEvent(const Event &src, sensors_event_t *dst); + void convertToSensorEvent(const Event& src, sensors_event_t* dst); - void convertToSensorEventsAndQuantize( - const hardware::hidl_vec<Event> &src, - const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded, - sensors_event_t *dst); + void convertToSensorEventsAndQuantize(const hardware::hidl_vec<Event>& src, + const hardware::hidl_vec<SensorInfo>& dynamicSensorsAdded, + sensors_event_t* dst); float getResolutionForSensor(int sensorHandle); diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index b059e61130..9b6d01ab71 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -89,6 +89,52 @@ public: UID_STATE_IDLE, }; + enum Mode { + // The regular operating mode where any application can register/unregister/call flush on + // sensors. + NORMAL = 0, + // This mode is only used for testing purposes. Not all HALs support this mode. In this mode, + // the HAL ignores the sensor data provided by physical sensors and accepts the data that is + // injected from the SensorService as if it were the real sensor data. This mode is primarily + // used for testing various algorithms like vendor provided SensorFusion, Step Counter and + // Step Detector etc. Typically in this mode, there will be a client (a + // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can + // unregister and register for any sensor that supports injection. Registering to sensors + // that do not support injection will give an error. TODO: Allow exactly one + // client to inject sensor data at a time. + DATA_INJECTION = 1, + // This mode is used only for testing sensors. Each sensor can be tested in isolation with + // the required sampling_rate and maxReportLatency parameters without having to think about + // the data rates requested by other applications. End user devices are always expected to be + // in NORMAL mode. When this mode is first activated, all active sensors from all connections + // are disabled. Calling flush() will return an error. In this mode, only the requests from + // selected apps whose package names are allowlisted are allowed (typically CTS apps). Only + // these apps can register/unregister/call flush() on sensors. If SensorService switches to + // NORMAL mode again, all sensors that were previously registered to are activated with the + // corresponding parameters if the application hasn't unregistered for sensors in the mean + // time. NOTE: Non allowlisted app whose sensors were previously deactivated may still + // receive events if a allowlisted app requests data from the same sensor. + RESTRICTED = 2 + + // State Transitions supported. + // RESTRICTED <--- NORMAL ---> DATA_INJECTION + // ---> <--- + + // Shell commands to switch modes in SensorService. + // 1) Put SensorService in RESTRICTED mode with packageName .cts. If it is already in + // restricted mode it is treated as a NO_OP (and packageName is NOT changed). + // + // $ adb shell dumpsys sensorservice restrict .cts. + // + // 2) Put SensorService in DATA_INJECTION mode with packageName .xts. If it is already in + // data_injection mode it is treated as a NO_OP (and packageName is NOT changed). + // + // $ adb shell dumpsys sensorservice data_injection .xts. + // + // 3) Reset sensorservice back to NORMAL mode. + // $ adb shell dumpsys sensorservice enable + }; + class ProximityActiveListener : public virtual RefBase { public: // Note that the callback is invoked from an async thread and can interact with the @@ -276,52 +322,6 @@ private: const int64_t mToken; }; - enum Mode { - // The regular operating mode where any application can register/unregister/call flush on - // sensors. - NORMAL = 0, - // This mode is only used for testing purposes. Not all HALs support this mode. In this mode, - // the HAL ignores the sensor data provided by physical sensors and accepts the data that is - // injected from the SensorService as if it were the real sensor data. This mode is primarily - // used for testing various algorithms like vendor provided SensorFusion, Step Counter and - // Step Detector etc. Typically in this mode, there will be a client (a - // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can - // unregister and register for any sensor that supports injection. Registering to sensors - // that do not support injection will give an error. TODO(aakella) : Allow exactly one - // client to inject sensor data at a time. - DATA_INJECTION = 1, - // This mode is used only for testing sensors. Each sensor can be tested in isolation with - // the required sampling_rate and maxReportLatency parameters without having to think about - // the data rates requested by other applications. End user devices are always expected to be - // in NORMAL mode. When this mode is first activated, all active sensors from all connections - // are disabled. Calling flush() will return an error. In this mode, only the requests from - // selected apps whose package names are whitelisted are allowed (typically CTS apps). Only - // these apps can register/unregister/call flush() on sensors. If SensorService switches to - // NORMAL mode again, all sensors that were previously registered to are activated with the - // corresponding paramaters if the application hasn't unregistered for sensors in the mean - // time. NOTE: Non whitelisted app whose sensors were previously deactivated may still - // receive events if a whitelisted app requests data from the same sensor. - RESTRICTED = 2 - - // State Transitions supported. - // RESTRICTED <--- NORMAL ---> DATA_INJECTION - // ---> <--- - - // Shell commands to switch modes in SensorService. - // 1) Put SensorService in RESTRICTED mode with packageName .cts. If it is already in - // restricted mode it is treated as a NO_OP (and packageName is NOT changed). - // - // $ adb shell dumpsys sensorservice restrict .cts. - // - // 2) Put SensorService in DATA_INJECTION mode with packageName .xts. If it is already in - // data_injection mode it is treated as a NO_OP (and packageName is NOT changed). - // - // $ adb shell dumpsys sensorservice data_injection .xts. - // - // 3) Reset sensorservice back to NORMAL mode. - // $ adb shell dumpsys sensorservice enable - }; - static const char* WAKE_LOCK_NAME; virtual ~SensorService(); diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index fb42cc02eb..5560ed738a 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -78,10 +78,11 @@ cc_defaults { "libframetimeline", "libperfetto_client_experimental", "librenderengine", + "libscheduler", "libserviceutils", + "libshaders", "libtonemap", "libtrace_proto", - "libaidlcommonsupport", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", @@ -144,6 +145,7 @@ cc_library_headers { filegroup { name: "libsurfaceflinger_sources", srcs: [ + "BackgroundExecutor.cpp", "BufferLayer.cpp", "BufferLayerConsumer.cpp", "BufferQueueLayer.cpp", @@ -194,14 +196,16 @@ filegroup { "Scheduler/Timer.cpp", "Scheduler/VSyncDispatchTimerQueue.cpp", "Scheduler/VSyncPredictor.cpp", - "Scheduler/VsyncModulator.cpp", "Scheduler/VSyncReactor.cpp", "Scheduler/VsyncConfiguration.cpp", + "Scheduler/VsyncModulator.cpp", + "Scheduler/VsyncSchedule.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceFlingerDefaultFactory.cpp", "SurfaceInterceptor.cpp", "Tracing/LayerTracing.cpp", + "Tracing/TransactionTracing.cpp", "Tracing/TransactionProtoParser.cpp", "TransactionCallbackInvoker.cpp", "TunnelModeEnabledReporter.cpp", diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp new file mode 100644 index 0000000000..de8e6b380f --- /dev/null +++ b/services/surfaceflinger/BackgroundExecutor.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "BackgroundExecutor" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "BackgroundExecutor.h" + +namespace android { + +ANDROID_SINGLETON_STATIC_INSTANCE(BackgroundExecutor); + +BackgroundExecutor::BackgroundExecutor() : Singleton<BackgroundExecutor>() { + mThread = std::thread([&]() { + bool done = false; + while (!done) { + std::vector<std::function<void()>> tasks; + { + std::unique_lock lock(mMutex); + android::base::ScopedLockAssertion assumeLock(mMutex); + mWorkAvailableCv.wait(lock, + [&]() REQUIRES(mMutex) { return mDone || !mTasks.empty(); }); + tasks = std::move(mTasks); + mTasks.clear(); + done = mDone; + } // unlock mMutex + + for (auto& task : tasks) { + task(); + } + } + }); +} + +BackgroundExecutor::~BackgroundExecutor() { + { + std::scoped_lock lock(mMutex); + mDone = true; + mWorkAvailableCv.notify_all(); + } + if (mThread.joinable()) { + mThread.join(); + } +} + +void BackgroundExecutor::execute(std::function<void()> task) { + std::scoped_lock lock(mMutex); + mTasks.emplace_back(std::move(task)); + mWorkAvailableCv.notify_all(); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/BackgroundExecutor.h b/services/surfaceflinger/BackgroundExecutor.h new file mode 100644 index 0000000000..6db7dda7aa --- /dev/null +++ b/services/surfaceflinger/BackgroundExecutor.h @@ -0,0 +1,43 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/thread_annotations.h> +#include <utils/Singleton.h> +#include <condition_variable> +#include <mutex> +#include <queue> +#include <thread> + +namespace android { + +// Executes tasks off the main thread. +class BackgroundExecutor : public Singleton<BackgroundExecutor> { +public: + BackgroundExecutor(); + ~BackgroundExecutor(); + void execute(std::function<void()>); + +private: + std::mutex mMutex; + std::condition_variable mWorkAvailableCv GUARDED_BY(mMutex); + bool mDone GUARDED_BY(mMutex) = false; + std::vector<std::function<void()>> mTasks GUARDED_BY(mMutex); + std::thread mThread; +}; + +} // namespace android diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 861d4963f7..64ddd687ad 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -401,12 +401,13 @@ void BufferLayer::onPostComposition(const DisplayDevice* display, const Fps refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps(); const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); + + const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate); + const auto gameMode = getGameMode(); + if (presentFence->isValid()) { mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, - refreshRate, renderRate, - frameRateToSetFrameRateVotePayload( - mDrawingState.frameRate), - getGameMode()); + refreshRate, renderRate, vote, gameMode); mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); @@ -417,10 +418,7 @@ void BufferLayer::onPostComposition(const DisplayDevice* display, // timestamp instead. const nsecs_t actualPresentTime = display->getRefreshTimestamp(); mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, - refreshRate, renderRate, - frameRateToSetFrameRateVotePayload( - mDrawingState.frameRate), - getGameMode()); + refreshRate, renderRate, vote, gameMode); mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, actualPresentTime, FrameTracer::FrameEvent::PRESENT_FENCE); @@ -566,15 +564,16 @@ bool BufferLayer::isProtected() const { // hardware.h, instead of using hard-coded values here. #define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) -bool BufferLayer::getOpacityForFormat(uint32_t format) { +bool BufferLayer::getOpacityForFormat(PixelFormat format) { if (HARDWARE_IS_DEVICE_FORMAT(format)) { return true; } switch (format) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_BGRA_8888: - case HAL_PIXEL_FORMAT_RGBA_FP16: - case HAL_PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_BGRA_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_R_8: return false; } // in all other case, we have no blending (also for unknown formats) @@ -798,7 +797,7 @@ void BufferLayer::updateCloneBufferInfo() { wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; WindowInfo tmpInputInfo = mDrawingState.inputInfo; - mDrawingState = clonedFrom->mDrawingState; + cloneDrawingState(clonedFrom.get()); mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index a4c21f4caf..34d11ac579 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -153,7 +153,7 @@ protected: bool onPreComposition(nsecs_t) override; void preparePerFrameCompositionState() override; - static bool getOpacityForFormat(uint32_t format); + static bool getOpacityForFormat(PixelFormat format); // from graphics API const uint32_t mTextureName; diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index dec7cc0806..926aa1dfb2 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -372,8 +372,9 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { // Add this buffer from our internal queue tracker { // Autolock scope const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp; - mFlinger->mScheduler->recordLayerHistory(this, presentTime, - LayerHistory::LayerUpdateType::Buffer); + + using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; + mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer); Mutex::Autolock lock(mQueueItemLock); // Reset the frame number tracker when we receive the first buffer after diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index b4ccb803e9..2fac880065 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -61,7 +61,7 @@ BufferStateLayer::~BufferStateLayer() { // one of the layers, in this case the original layer, needs to handle the deletion. The // original layer and the clone should be removed at the same time so there shouldn't be any // issue with the clone layer trying to use the texture. - if (mBufferInfo.mBuffer != nullptr && !isClone()) { + if (mBufferInfo.mBuffer != nullptr) { callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, mBufferInfo.mFence, @@ -476,8 +476,9 @@ bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime, return static_cast<nsecs_t>(0); }(); - mFlinger->mScheduler->recordLayerHistory(this, presentTime, - LayerHistory::LayerUpdateType::Buffer); + + using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; + mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer); addFrameEvent(mDrawingState.acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h index 4502eee97c..c553fce85d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h @@ -16,6 +16,8 @@ #pragma once +#include <cinttypes> + #include <ui/Size.h> #include <utils/Errors.h> #include <utils/RefBase.h> @@ -47,13 +49,8 @@ public: // before composition takes place. The DisplaySurface can use the // composition type to decide how to manage the flow of buffers between // GPU and HWC for this frame. - enum CompositionType { - COMPOSITION_UNKNOWN = 0, - COMPOSITION_GPU = 1, - COMPOSITION_HWC = 2, - COMPOSITION_MIXED = COMPOSITION_GPU | COMPOSITION_HWC - }; - virtual status_t prepareFrame(CompositionType compositionType) = 0; + enum class CompositionType : uint8_t { Unknown = 0, Gpu = 0b1, Hwc = 0b10, Mixed = Gpu | Hwc }; + virtual status_t prepareFrame(CompositionType) = 0; // Inform the surface that GPU composition is complete for this frame, and // the surface should make sure that HWComposer has the correct buffer for diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index a000661c79..36594ea83c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -200,6 +200,9 @@ struct LayerFECompositionState { // The output-independent frame for the cursor Rect cursorFrame; + // framerate of the layer as measured by LayerHistory + float fps; + virtual ~LayerFECompositionState(); // Debugging diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index 244f8ab88b..c15249d297 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -73,8 +73,9 @@ private: void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&, bool skipLayer); void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*); - void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&, - bool skipLayer); + void writeOutputIndependentPerFrameStateToHWC( + HWC2::Layer*, const LayerFECompositionState&, + Hwc2::IComposerClient::Composition compositionType, bool skipLayer); void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&); void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&); void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&, bool skipLayer); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index cff652755d..92cc484211 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -87,6 +87,13 @@ public: const bool mEnableHolePunch; }; + // Constants not yet backed by a sysprop + // CachedSets that contain no more than this many layers may be considered inactive on the basis + // of FPS. + static constexpr int kNumLayersFpsConsideration = 1; + // Frames/Second threshold below which these CachedSets may be considered inactive. + static constexpr float kFpsActiveThreshold = 1.f; + Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables); void setDisplaySize(ui::Size size) { @@ -127,21 +134,22 @@ private: class Builder { private: std::vector<CachedSet>::const_iterator mStart; - std::vector<size_t> mLengths; + int32_t mNumSets = 0; const CachedSet* mHolePunchCandidate = nullptr; const CachedSet* mBlurringLayer = nullptr; + bool mBuilt = false; public: // Initializes a Builder a CachedSet to start from. // This start iterator must be an iterator for mLayers void init(const std::vector<CachedSet>::const_iterator& start) { mStart = start; - mLengths.push_back(start->getLayerCount()); + mNumSets = 1; } // Appends a new CachedSet to the end of the run // The provided length must be the size of the next sequential CachedSet in layers - void append(size_t length) { mLengths.push_back(length); } + void increment() { mNumSets++; } // Sets the hole punch candidate for the Run. void setHolePunchCandidate(const CachedSet* holePunchCandidate) { @@ -154,19 +162,36 @@ private: // Builds a Run instance, if a valid Run may be built. std::optional<Run> validateAndBuild() { - if (mLengths.size() == 0) { + const bool built = mBuilt; + mBuilt = true; + if (mNumSets <= 0 || built) { return std::nullopt; } - // Runs of length 1 which are hole punch candidates are allowed if the candidate is - // going to be used. - if (mLengths.size() == 1 && - (!mHolePunchCandidate || !(mHolePunchCandidate->requiresHolePunch()))) { - return std::nullopt; + + const bool requiresHolePunch = + mHolePunchCandidate && mHolePunchCandidate->requiresHolePunch(); + + if (!requiresHolePunch) { + // If we don't require a hole punch, then treat solid color layers at the front + // to be "cheap", so remove them from the candidate cached set. + while (mNumSets > 1 && mStart->getLayerCount() == 1 && + mStart->getFirstLayer().getBuffer() == nullptr) { + mStart++; + mNumSets--; + } + + // Only allow for single cached sets if a hole punch is required. If we're here, + // then we don't require a hole punch, so don't build a run. + if (mNumSets <= 1) { + return std::nullopt; + } } return Run(mStart, - std::reduce(mLengths.cbegin(), mLengths.cend(), 0u, - [](size_t left, size_t right) { return left + right; }), + std::reduce(mStart, mStart + mNumSets, 0u, + [](size_t length, const CachedSet& set) { + return length + set.getLayerCount(); + }), mHolePunchCandidate, mBlurringLayer); } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h index 523752719d..7397c19837 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h @@ -245,6 +245,7 @@ public: bool isProtected() const { return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent; } + float getFps() const { return getOutputLayer()->getLayerFE().getCompositionState()->fps; } void dump(std::string& result) const; std::optional<std::string> compare(const LayerState& other) const; diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index e958549569..cf761835a6 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -347,6 +347,10 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t auto requestedCompositionType = outputIndependentState->compositionType; + if (requestedCompositionType == hal::Composition::SOLID_COLOR && state.overrideInfo.buffer) { + requestedCompositionType = hal::Composition::DEVICE; + } + // TODO(b/181172795): We now update geometry for all flattened layers. We should update it // only when the geometry actually changes const bool isOverridden = @@ -359,13 +363,15 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t } writeOutputDependentPerFrameStateToHWC(hwcLayer.get()); - writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState, skipLayer); + writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState, + requestedCompositionType, skipLayer); writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough, skipLayer); - // Always set the layer color after setting the composition type. - writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState); + if (requestedCompositionType == hal::Composition::SOLID_COLOR) { + writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState); + } editState().hwc->stateOverridden = isOverridden; editState().hwc->layerSkipped = skipLayer; @@ -477,7 +483,7 @@ void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) void OutputLayer::writeOutputIndependentPerFrameStateToHWC( HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState, - bool skipLayer) { + hal::Composition compositionType, bool skipLayer) { switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) { case hal::Error::NONE: break; @@ -501,7 +507,7 @@ void OutputLayer::writeOutputIndependentPerFrameStateToHWC( } // Content-specific per-frame state - switch (outputIndependentState.compositionType) { + switch (compositionType) { case hal::Composition::SOLID_COLOR: // For compatibility, should be written AFTER the composition type. break; @@ -521,10 +527,6 @@ void OutputLayer::writeOutputIndependentPerFrameStateToHWC( void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) { - if (outputIndependentState.compositionType != hal::Composition::SOLID_COLOR) { - return; - } - hal::Color color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)), static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)), static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)), diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp index ef50870615..a19d23febf 100644 --- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp @@ -127,19 +127,18 @@ status_t RenderSurface::beginFrame(bool mustRecompose) { } void RenderSurface::prepareFrame(bool usesClientComposition, bool usesDeviceComposition) { - DisplaySurface::CompositionType compositionType; - if (usesClientComposition && usesDeviceComposition) { - compositionType = DisplaySurface::COMPOSITION_MIXED; - } else if (usesClientComposition) { - compositionType = DisplaySurface::COMPOSITION_GPU; - } else if (usesDeviceComposition) { - compositionType = DisplaySurface::COMPOSITION_HWC; - } else { + const auto compositionType = [=] { + using CompositionType = DisplaySurface::CompositionType; + + if (usesClientComposition && usesDeviceComposition) return CompositionType::Mixed; + if (usesClientComposition) return CompositionType::Gpu; + if (usesDeviceComposition) return CompositionType::Hwc; + // Nothing to do -- when turning the screen off we get a frame like // this. Call it a HWC frame since we won't be doing any GPU work but // will do a prepare/set cycle. - compositionType = DisplaySurface::COMPOSITION_HWC; - } + return CompositionType::Hwc; + }(); if (status_t result = mDisplaySurface->prepareFrame(compositionType); result != NO_ERROR) { ALOGE("updateCompositionType failed for %s: %d (%s)", mDisplay.getName().c_str(), result, diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index ec52e59ab0..ff96d94670 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -25,6 +25,7 @@ #include <math/HashCombine.h> #include <renderengine/DisplaySettings.h> #include <renderengine/RenderEngine.h> +#include <ui/DebugUtils.h> #include <utils/Trace.h> #include <utils/Trace.h> @@ -391,20 +392,29 @@ void CachedSet::dump(std::string& result) const { if (mLayers.size() == 1) { base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str()); - base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get()); - base::StringAppendF(&result, " Protected [%s]", + if (auto* buffer = mLayers[0].getBuffer().get()) { + base::StringAppendF(&result, " Buffer %p", buffer); + base::StringAppendF(&result, " Format %s", + decodePixelFormat(buffer->getPixelFormat()).c_str()); + } + base::StringAppendF(&result, " Protected [%s]\n", mLayers[0].getState()->isProtected() ? "true" : "false"); } else { - result.append(" Cached set of:"); + result.append(" Cached set of:\n"); for (const Layer& layer : mLayers) { - base::StringAppendF(&result, "\n Layer [%s]", layer.getName().c_str()); - base::StringAppendF(&result, "\n Protected [%s]", + base::StringAppendF(&result, " Layer [%s]\n", layer.getName().c_str()); + if (auto* buffer = layer.getBuffer().get()) { + base::StringAppendF(&result, " Buffer %p", buffer); + base::StringAppendF(&result, " Format[%s]", + decodePixelFormat(buffer->getPixelFormat()).c_str()); + } + base::StringAppendF(&result, " Protected [%s]\n", layer.getState()->isProtected() ? "true" : "false"); } } - base::StringAppendF(&result, "\n Creation cost: %zd", getCreationCost()); - base::StringAppendF(&result, "\n Display cost: %zd\n", getDisplayCost()); + base::StringAppendF(&result, " Creation cost: %zd\n", getCreationCost()); + base::StringAppendF(&result, " Display cost: %zd\n", getDisplayCost()); } } // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 2272099c77..c14effccc3 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -409,25 +409,29 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { bool runHasFirstLayer = false; for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) { - const bool layerIsInactive = - now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout; + bool layerIsInactive = now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout; const bool layerHasBlur = currentSet->hasBlurBehind(); + // Layers should also be considered inactive whenever their framerate is lower than 1fps. + if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) { + auto layerFps = currentSet->getFirstLayer().getState()->getFps(); + if (layerFps > 0 && layerFps <= kFpsActiveThreshold) { + ATRACE_FORMAT("layer is considered inactive due to low FPS [%s] %f", + currentSet->getFirstLayer().getName().c_str(), layerFps); + layerIsInactive = true; + } + } + if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) && !currentSet->hasUnsupportedDataspace()) { if (isPartOfRun) { - builder.append(currentSet->getLayerCount()); + builder.increment(); } else { - // Runs can't start with a non-buffer layer - if (currentSet->getFirstLayer().getBuffer() == nullptr) { - ALOGV("[%s] Skipping initial non-buffer layer", __func__); - } else { - builder.init(currentSet); - if (firstLayer) { - runHasFirstLayer = true; - } - isPartOfRun = true; + builder.init(currentSet); + if (firstLayer) { + runHasFirstLayer = true; } + isPartOfRun = true; } } else if (isPartOfRun) { builder.setHolePunchCandidate(&(*currentSet)); diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index fbc20898ec..31b131ad74 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -1109,6 +1109,22 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) { /*zIsOverridden*/ false, /*isPeekingThrough*/ false); } +TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresent) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR; + includeOverrideInfo(); + + expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform, + kOverrideBlendMode, kOverrideAlpha); + expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion, + kOverrideSurfaceDamage); + expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE); + EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false)); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) { mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE; mOutputLayer.editState().hwc->stateOverridden = true; diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp index 431cc93514..7c8e41b53e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp @@ -201,28 +201,28 @@ TEST_F(RenderSurfaceTest, beginFrameAppliesChange) { */ TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) { - EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED)) + EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Mixed)) .WillOnce(Return(NO_ERROR)); mSurface.prepareFrame(true, true); } TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) { - EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU)) + EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Gpu)) .WillOnce(Return(NO_ERROR)); mSurface.prepareFrame(true, false); } TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) { - EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC)) + EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Hwc)) .WillOnce(Return(NO_ERROR)); mSurface.prepareFrame(false, true); } TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) { - EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC)) + EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Hwc)) .WillOnce(Return(NO_ERROR)); mSurface.prepareFrame(false, false); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 9b0a75fd47..35d051ebea 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -224,6 +224,22 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) { mFlattener->renderCachedSets(mOutputState, std::nullopt); } +TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) { + mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2; + mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold; + + auto& layerState1 = mTestLayers[0]->layerState; + auto& layerState2 = mTestLayers[1]->layerState; + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + }; + + initializeFlattener(layers); + expectAllLayersFlattened(layers); +} + TEST_F(FlattenerTest, flattenLayers_basicFlatten) { auto& layerState1 = mTestLayers[0]->layerState; auto& layerState2 = mTestLayers[1]->layerState; @@ -746,6 +762,76 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { EXPECT_EQ(nullptr, peekThroughLayer1); } +TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) { + mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); + mTestLayers[0]->layerFECompositionState.color = half4(255.f, 0.f, 0.f, 255.f); + mTestLayers[0]->layerFECompositionState.buffer = nullptr; + + // An opaque static background + auto& layerState0 = mTestLayers[0]->layerState; + const auto& overrideBuffer0 = layerState0->getOutputLayer()->getState().overrideInfo.buffer; + + // a rounded updating layer + auto& layerState1 = mTestLayers[1]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + + EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); + + std::vector<LayerFE::LayerSettings> clientCompositionList = { + LayerFE::LayerSettings{}, + }; + clientCompositionList[0].source.buffer.buffer = std::make_shared< + renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer, + mRenderEngine, + renderengine::ExternalTexture::Usage::READABLE); + EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_)) + .WillOnce(Return(clientCompositionList)); + + const std::vector<const LayerState*> layers = { + layerState0.get(), + layerState1.get(), + }; + + initializeFlattener(layers); + + // layer 1 satisfies every condition in CachedSet::requiresHolePunch() + mTime += 200ms; + layerState1->resetFramesSinceBufferUpdate(); // it is updating + + initializeOverrideBuffer(layers); + // Expect no cache invalidation the first time (there's no cache yet) + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + + // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the + // exception that there would be a hole punch above it. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + mFlattener->renderCachedSets(mOutputState, std::nullopt); + + // We've rendered a CachedSet, but we haven't merged it in. + EXPECT_EQ(nullptr, overrideBuffer0); + + // This time we merge the CachedSet in and we should still have only two sets. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mOutputState, std::nullopt); + + EXPECT_NE(nullptr, overrideBuffer0); // got overridden + EXPECT_EQ(nullptr, overrideBuffer1); // did not + + // expect 0's peek though layer to be 1's output layer + const auto* peekThroughLayer0 = + layerState0->getOutputLayer()->getState().overrideInfo.peekThroughLayer; + const auto* peekThroughLayer1 = + layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer; + EXPECT_EQ(&mTestLayers[1]->outputLayer, peekThroughLayer0); + EXPECT_EQ(nullptr, peekThroughLayer1); +} + TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { auto& layerState1 = mTestLayers[0]->layerState; @@ -1191,5 +1277,61 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) { EXPECT_EQ(nullptr, overrideBuffer3); } +TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) { + 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; + auto& layerState3 = mTestLayers[2]->layerState; + const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer; + auto& layerState4 = mTestLayers[3]->layerState; + const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer; + + // Rewrite the first two layers to just be a solid color. + mTestLayers[0]->layerFECompositionState.color = half4(255.f, 0.f, 0.f, 255.f); + mTestLayers[0]->layerFECompositionState.buffer = nullptr; + mTestLayers[1]->layerFECompositionState.color = half4(0.f, 255.f, 0.f, 255.f); + mTestLayers[1]->layerFECompositionState.buffer = nullptr; + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + layerState3.get(), + layerState4.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( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + mFlattener->renderCachedSets(mOutputState, std::nullopt); + + // We've rendered a CachedSet, but we haven't merged it in. + EXPECT_EQ(nullptr, overrideBuffer1); + EXPECT_EQ(nullptr, overrideBuffer2); + EXPECT_EQ(nullptr, overrideBuffer3); + EXPECT_EQ(nullptr, overrideBuffer4); + + // 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); + + EXPECT_EQ(nullptr, overrideBuffer1); + EXPECT_EQ(nullptr, overrideBuffer2); + EXPECT_EQ(overrideBuffer3, overrideBuffer4); + EXPECT_NE(nullptr, overrideBuffer4); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index fd09ae4066..76bbe2c58f 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -483,7 +483,7 @@ bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) { std::scoped_lock lock(mActiveModeLock); if (mDesiredActiveModeChanged) { // If a mode change is pending, just cache the latest request in mDesiredActiveMode - const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event; + const auto prevConfig = mDesiredActiveMode.event; mDesiredActiveMode = info; mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig; return false; @@ -508,7 +508,7 @@ std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode void DisplayDevice::clearDesiredActiveModeState() { std::scoped_lock lock(mActiveModeLock); - mDesiredActiveMode.event = Scheduler::ModeEvent::None; + mDesiredActiveMode.event = scheduler::DisplayModeEvent::None; mDesiredActiveModeChanged = false; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 4b9718f608..324145ef47 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -190,7 +190,7 @@ public: struct ActiveModeInfo { DisplayModePtr mode; - scheduler::RefreshRateConfigEvent event = scheduler::RefreshRateConfigEvent::None; + scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None; bool operator!=(const ActiveModeInfo& other) const { return mode != other.mode || event != other.event; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index ba57be5d65..200bb65b29 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -41,6 +41,8 @@ using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::PowerMode; using aidl::android::hardware::graphics::composer3::VirtualDisplay; +using aidl::android::hardware::graphics::composer3::command::CommandResultPayload; + using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode; using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType; using AidlDisplayIdentification = @@ -48,8 +50,6 @@ using AidlDisplayIdentification = using AidlDisplayContentSample = aidl::android::hardware::graphics::composer3::DisplayContentSample; using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute; using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability; -using AidlExecuteCommandsStatus = - aidl::android::hardware::graphics::composer3::ExecuteCommandsStatus; using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities; using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata; using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey; @@ -174,6 +174,14 @@ IComposerClient::LayerGenericMetadataKey translate(AidlLayerGenericMetadataKey x }; } +template <> +IComposerClient::ClientTargetProperty translate(ClientTargetProperty x) { + return IComposerClient::ClientTargetProperty{ + .pixelFormat = translate<PixelFormat>(x.pixelFormat), + .dataspace = translate<Dataspace>(x.dataspace), + }; +} + mat4 makeMat4(std::vector<float> in) { return mat4(static_cast<const float*>(in.data())); } @@ -226,7 +234,7 @@ bool AidlComposer::isDeclared(const std::string& serviceName) { return AServiceManager_isDeclared(instance(serviceName).c_str()); } -AidlComposer::AidlComposer(const std::string& serviceName) : mWriter(kWriterInitialSize) { +AidlComposer::AidlComposer(const std::string& serviceName) { // This only waits if the service is actually declared mAidlComposer = AidlIComposer::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(instance(serviceName).c_str()))); @@ -327,8 +335,7 @@ Error AidlComposer::destroyVirtualDisplay(Display display) { } Error AidlComposer::acceptDisplayChanges(Display display) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.acceptDisplayChanges(); + mWriter.acceptDisplayChanges(translate<int64_t>(display)); return Error::NONE; } @@ -369,7 +376,12 @@ Error AidlComposer::getActiveConfig(Display display, Config* outConfig) { Error AidlComposer::getChangedCompositionTypes( Display display, std::vector<Layer>* outLayers, std::vector<IComposerClient::Composition>* outTypes) { - mReader.takeChangedCompositionTypes(display, outLayers, outTypes); + std::vector<int64_t> layers; + std::vector<Composition> types; + mReader.takeChangedCompositionTypes(translate<int64_t>(display), &layers, &types); + + *outLayers = translate<Layer>(layers); + *outTypes = translate<IComposerClient::Composition>(types); return Error::NONE; } @@ -422,7 +434,10 @@ Error AidlComposer::getDisplayName(Display display, std::string* outName) { Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers, std::vector<uint32_t>* outLayerRequestMasks) { - mReader.takeDisplayRequests(display, outDisplayRequestMask, outLayers, outLayerRequestMasks); + std::vector<int64_t> layers; + mReader.takeDisplayRequests(translate<int64_t>(display), outDisplayRequestMask, &layers, + outLayerRequestMasks); + *outLayers = translate<Layer>(layers); return Error::NONE; } @@ -456,21 +471,22 @@ Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTyp Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) { - mReader.takeReleaseFences(display, outLayers, outReleaseFences); + std::vector<int64_t> layers; + mReader.takeReleaseFences(translate<int64_t>(display), &layers, outReleaseFences); + *outLayers = translate<Layer>(layers); return Error::NONE; } Error AidlComposer::presentDisplay(Display display, int* outPresentFence) { ATRACE_NAME("HwcPresentDisplay"); - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.presentDisplay(); + mWriter.presentDisplay(translate<int64_t>(display)); Error error = execute(); if (error != Error::NONE) { return error; } - mReader.takePresentFence(display, outPresentFence); + mReader.takePresentFence(translate<int64_t>(display), outPresentFence); return Error::NONE; } @@ -488,14 +504,12 @@ Error AidlComposer::setActiveConfig(Display display, Config config) { Error AidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target, int acquireFence, Dataspace dataspace, const std::vector<IComposerClient::Rect>& damage) { - mWriter.selectDisplay(translate<int64_t>(display)); - const native_handle_t* handle = nullptr; if (target.get()) { handle = target->getNativeBuffer()->handle; } - mWriter.setClientTarget(slot, handle, acquireFence, + mWriter.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence, translate<aidl::android::hardware::graphics::common::Dataspace>( dataspace), translate<AidlRect>(damage)); @@ -515,15 +529,14 @@ Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent r } Error AidlComposer::setColorTransform(Display display, const float* matrix, ColorTransform hint) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.setColorTransform(matrix, translate<AidlColorTransform>(hint)); + mWriter.setColorTransform(translate<int64_t>(display), matrix, + translate<AidlColorTransform>(hint)); return Error::NONE; } Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer, int releaseFence) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.setOutputBuffer(0, buffer, dup(releaseFence)); + mWriter.setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence)); return Error::NONE; } @@ -562,15 +575,14 @@ Error AidlComposer::setClientTargetSlotCount(Display display) { Error AidlComposer::validateDisplay(Display display, uint32_t* outNumTypes, uint32_t* outNumRequests) { ATRACE_NAME("HwcValidateDisplay"); - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.validateDisplay(); + mWriter.validateDisplay(translate<int64_t>(display)); Error error = execute(); if (error != Error::NONE) { return error; } - mReader.hasChanges(display, outNumTypes, outNumRequests); + mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests); return Error::NONE; } @@ -579,207 +591,154 @@ Error AidlComposer::presentOrValidateDisplay(Display display, uint32_t* outNumTy uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) { ATRACE_NAME("HwcPresentOrValidateDisplay"); - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.presentOrvalidateDisplay(); + mWriter.presentOrvalidateDisplay(translate<int64_t>(display)); Error error = execute(); if (error != Error::NONE) { return error; } - mReader.takePresentOrValidateStage(display, state); + mReader.takePresentOrValidateStage(translate<int64_t>(display), state); if (*state == 1) { // Present succeeded - mReader.takePresentFence(display, outPresentFence); + mReader.takePresentFence(translate<int64_t>(display), outPresentFence); } if (*state == 0) { // Validate succeeded. - mReader.hasChanges(display, outNumTypes, outNumRequests); + mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests); } return Error::NONE; } Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerCursorPosition(x, y); + mWriter.setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), x, y); return Error::NONE; } Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot, const sp<GraphicBuffer>& buffer, int acquireFence) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - const native_handle_t* handle = nullptr; if (buffer.get()) { handle = buffer->getNativeBuffer()->handle; } - mWriter.setLayerBuffer(slot, handle, acquireFence); + mWriter.setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, handle, + acquireFence); return Error::NONE; } Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer, const std::vector<IComposerClient::Rect>& damage) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerSurfaceDamage(translate<AidlRect>(damage)); + mWriter.setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(damage)); return Error::NONE; } Error AidlComposer::setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerBlendMode(translate<BlendMode>(mode)); + mWriter.setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer), + translate<BlendMode>(mode)); return Error::NONE; } Error AidlComposer::setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerColor(translate<Color>(color)); + mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), + translate<Color>(color)); return Error::NONE; } Error AidlComposer::setLayerCompositionType(Display display, Layer layer, IComposerClient::Composition type) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerCompositionType(translate<Composition>(type)); + mWriter.setLayerCompositionType(translate<int64_t>(display), translate<int64_t>(layer), + translate<Composition>(type)); return Error::NONE; } Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerDataspace(translate<AidlDataspace>(dataspace)); + mWriter.setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlDataspace>(dataspace)); return Error::NONE; } Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer, const IComposerClient::Rect& frame) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerDisplayFrame(translate<AidlRect>(frame)); + mWriter.setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(frame)); return Error::NONE; } Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerPlaneAlpha(alpha); + mWriter.setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), alpha); return Error::NONE; } Error AidlComposer::setLayerSidebandStream(Display display, Layer layer, const native_handle_t* stream) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerSidebandStream(stream); + mWriter.setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), stream); return Error::NONE; } Error AidlComposer::setLayerSourceCrop(Display display, Layer layer, const IComposerClient::FRect& crop) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerSourceCrop(translate<AidlFRect>(crop)); + mWriter.setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlFRect>(crop)); return Error::NONE; } Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerTransform(translate<AidlTransform>(transform)); + mWriter.setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlTransform>(transform)); return Error::NONE; } Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer, const std::vector<IComposerClient::Rect>& visible) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerVisibleRegion(translate<AidlRect>(visible)); + mWriter.setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(visible)); return Error::NONE; } Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerZOrder(z); + mWriter.setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z); return Error::NONE; } Error AidlComposer::execute() { - // prepare input command queue - bool queueChanged = false; - int32_t commandLength = 0; - std::vector<aidl::android::hardware::common::NativeHandle> commandHandles; - if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) { - mWriter.reset(); - return Error::NO_RESOURCES; - } - - // set up new input command queue if necessary - if (queueChanged) { - const auto status = mAidlComposerClient->setInputCommandQueue(mWriter.getMQDescriptor()); - if (!status.isOk()) { - ALOGE("setInputCommandQueue failed %s", status.getDescription().c_str()); - mWriter.reset(); - return static_cast<Error>(status.getServiceSpecificError()); - } - } - - if (commandLength == 0) { + const auto& commands = mWriter.getPendingCommands(); + if (commands.empty()) { mWriter.reset(); return Error::NONE; } - AidlExecuteCommandsStatus commandStatus; - auto status = - mAidlComposerClient->executeCommands(commandLength, commandHandles, &commandStatus); - // executeCommands can fail because of out-of-fd and we do not want to - // abort() in that case + std::vector<CommandResultPayload> results; + auto status = mAidlComposerClient->executeCommands(commands, &results); if (!status.isOk()) { ALOGE("executeCommands failed %s", status.getDescription().c_str()); return static_cast<Error>(status.getServiceSpecificError()); } - // set up new output command queue if necessary - if (commandStatus.queueChanged) { - ::aidl::android::hardware::common::fmq::MQDescriptor< - int32_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> - outputCommandQueue; - status = mAidlComposerClient->getOutputCommandQueue(&outputCommandQueue); - if (!status.isOk()) { - ALOGE("getOutputCommandQueue failed %s", status.getDescription().c_str()); - return static_cast<Error>(status.getServiceSpecificError()); + mReader.parse(results); + const auto commandErrors = mReader.takeErrors(); + Error error = Error::NONE; + for (const auto& cmdErr : commandErrors) { + const auto index = static_cast<size_t>(cmdErr.commandIndex); + if (index < 0 || index >= commands.size()) { + ALOGE("invalid command index %zu", index); + return Error::BAD_PARAMETER; } - mReader.setMQDescriptor(outputCommandQueue); - } - - Error error; - if (mReader.readQueue(commandStatus.length, std::move(commandStatus.handles))) { - error = static_cast<Error>(mReader.parse()); - mReader.reset(); - } else { - error = Error::NO_RESOURCES; - } - - if (error == Error::NONE) { - std::vector<AidlCommandReader::CommandError> commandErrors = mReader.takeErrors(); - - for (const auto& cmdErr : commandErrors) { - auto command = mWriter.getCommand(cmdErr.location); - if (command == Command::VALIDATE_DISPLAY || command == Command::PRESENT_DISPLAY || - command == Command::PRESENT_OR_VALIDATE_DISPLAY) { - error = cmdErr.error; + const auto& command = commands[index]; + if (command.getTag() == command::CommandPayload::Tag::displayCommand) { + const auto& displayCommand = + command.get<command::CommandPayload::Tag::displayCommand>(); + if (displayCommand.validateDisplay || displayCommand.presentDisplay || + displayCommand.presentOrValidateDisplay) { + error = translate<Error>(cmdErr.errorCode); } else { - ALOGW("command 0x%x generated error %d", command, cmdErr.error); + ALOGW("command '%s' generated error %" PRId32, command.toString().c_str(), + cmdErr.errorCode); } } } @@ -792,9 +751,8 @@ Error AidlComposer::execute() { Error AidlComposer::setLayerPerFrameMetadata( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerPerFrameMetadata(translate<AidlPerFrameMetadata>(perFrameMetadatas)); + mWriter.setLayerPerFrameMetadata(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlPerFrameMetadata>(perFrameMetadatas)); return Error::NONE; } @@ -855,9 +813,7 @@ Error AidlComposer::getDisplayIdentificationData(Display display, uint8_t* outPo } Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerColorTransform(matrix); + mWriter.setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), matrix); return Error::NONE; } @@ -921,9 +877,8 @@ Error AidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrame Error AidlComposer::setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerPerFrameMetadataBlobs(translate<AidlPerFrameMetadataBlob>(metadata)); + mWriter.setLayerPerFrameMetadataBlobs(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlPerFrameMetadataBlob>(metadata)); return Error::NONE; } @@ -1032,9 +987,8 @@ V2_4::Error AidlComposer::setContentType(Display display, V2_4::Error AidlComposer::setLayerGenericMetadata(Display display, Layer layer, const std::string& key, bool mandatory, const std::vector<uint8_t>& value) { - mWriter.selectDisplay(translate<int64_t>(display)); - mWriter.selectLayer(translate<int64_t>(layer)); - mWriter.setLayerGenericMetadata(key, mandatory, value); + mWriter.setLayerGenericMetadata(translate<int64_t>(display), translate<int64_t>(layer), key, + mandatory, value); return V2_4::Error::NONE; } @@ -1052,320 +1006,11 @@ V2_4::Error AidlComposer::getLayerGenericMetadataKeys( Error AidlComposer::getClientTargetProperty( Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) { - mReader.takeClientTargetProperty(display, outClientTargetProperty); + ClientTargetProperty property; + mReader.takeClientTargetProperty(translate<int64_t>(display), &property); + *outClientTargetProperty = translate<IComposerClient::ClientTargetProperty>(property); return Error::NONE; } -AidlCommandReader::~AidlCommandReader() { - resetData(); -} - -int AidlCommandReader::parse() { - resetData(); - - Command command; - uint16_t length = 0; - - while (!isEmpty()) { - if (!beginCommand(&command, &length)) { - break; - } - - bool parsed = false; - switch (command) { - case Command::SELECT_DISPLAY: - parsed = parseSelectDisplay(length); - break; - case Command::SET_ERROR: - parsed = parseSetError(length); - break; - case Command::SET_CHANGED_COMPOSITION_TYPES: - parsed = parseSetChangedCompositionTypes(length); - break; - case Command::SET_DISPLAY_REQUESTS: - parsed = parseSetDisplayRequests(length); - break; - case Command::SET_PRESENT_FENCE: - parsed = parseSetPresentFence(length); - break; - case Command::SET_RELEASE_FENCES: - parsed = parseSetReleaseFences(length); - break; - case Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT: - parsed = parseSetPresentOrValidateDisplayResult(length); - break; - case Command::SET_CLIENT_TARGET_PROPERTY: - parsed = parseSetClientTargetProperty(length); - break; - default: - parsed = false; - break; - } - - endCommand(); - - if (!parsed) { - ALOGE("failed to parse command 0x%x length %" PRIu16, command, length); - break; - } - } - - return isEmpty() ? 0 : AidlIComposerClient::EX_NO_RESOURCES; -} - -bool AidlCommandReader::parseSelectDisplay(uint16_t length) { - if (length != CommandWriterBase::kSelectDisplayLength) { - return false; - } - - mCurrentReturnData = &mReturnData[read64()]; - - return true; -} - -bool AidlCommandReader::parseSetError(uint16_t length) { - if (length != CommandWriterBase::kSetErrorLength) { - return false; - } - - auto location = read(); - auto error = static_cast<Error>(readSigned()); - - mErrors.emplace_back(CommandError{location, error}); - - return true; -} - -bool AidlCommandReader::parseSetChangedCompositionTypes(uint16_t length) { - // (layer id [64bit], composition type [32bit]) pairs - static constexpr int kCommandWords = 3; - - if (length % kCommandWords != 0 || !mCurrentReturnData) { - return false; - } - - uint32_t count = length / kCommandWords; - mCurrentReturnData->changedLayers.reserve(count); - mCurrentReturnData->compositionTypes.reserve(count); - while (count > 0) { - auto layer = read64(); - auto type = static_cast<IComposerClient::Composition>(readSigned()); - - mCurrentReturnData->changedLayers.push_back(layer); - mCurrentReturnData->compositionTypes.push_back(type); - - count--; - } - - return true; -} - -bool AidlCommandReader::parseSetDisplayRequests(uint16_t length) { - // display requests [32 bit] followed by - // (layer id [64bit], layer requests [32bit]) pairs - static constexpr int kDisplayRequestsWords = 1; - static constexpr int kCommandWords = 3; - if (length % kCommandWords != kDisplayRequestsWords || !mCurrentReturnData) { - return false; - } - - mCurrentReturnData->displayRequests = read(); - - uint32_t count = (length - kDisplayRequestsWords) / kCommandWords; - mCurrentReturnData->requestedLayers.reserve(count); - mCurrentReturnData->requestMasks.reserve(count); - while (count > 0) { - auto layer = read64(); - auto layerRequestMask = read(); - - mCurrentReturnData->requestedLayers.push_back(layer); - mCurrentReturnData->requestMasks.push_back(layerRequestMask); - - count--; - } - - return true; -} - -bool AidlCommandReader::parseSetPresentFence(uint16_t length) { - if (length != CommandWriterBase::kSetPresentFenceLength || !mCurrentReturnData) { - return false; - } - - if (mCurrentReturnData->presentFence >= 0) { - close(mCurrentReturnData->presentFence); - } - mCurrentReturnData->presentFence = readFence(); - - return true; -} - -bool AidlCommandReader::parseSetReleaseFences(uint16_t length) { - // (layer id [64bit], release fence index [32bit]) pairs - static constexpr int kCommandWords = 3; - - if (length % kCommandWords != 0 || !mCurrentReturnData) { - return false; - } - - uint32_t count = length / kCommandWords; - mCurrentReturnData->releasedLayers.reserve(count); - mCurrentReturnData->releaseFences.reserve(count); - while (count > 0) { - auto layer = read64(); - auto fence = readFence(); - - mCurrentReturnData->releasedLayers.push_back(layer); - mCurrentReturnData->releaseFences.push_back(fence); - - count--; - } - - return true; -} - -bool AidlCommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length) { - if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) { - return false; - } - mCurrentReturnData->presentOrValidateState = read(); - return true; -} - -bool AidlCommandReader::parseSetClientTargetProperty(uint16_t length) { - if (length != CommandWriterBase::kSetClientTargetPropertyLength || !mCurrentReturnData) { - return false; - } - mCurrentReturnData->clientTargetProperty.pixelFormat = static_cast<PixelFormat>(readSigned()); - mCurrentReturnData->clientTargetProperty.dataspace = static_cast<Dataspace>(readSigned()); - return true; -} - -void AidlCommandReader::resetData() { - mErrors.clear(); - - for (auto& data : mReturnData) { - if (data.second.presentFence >= 0) { - close(data.second.presentFence); - } - for (auto fence : data.second.releaseFences) { - if (fence >= 0) { - close(fence); - } - } - } - - mReturnData.clear(); - mCurrentReturnData = nullptr; -} - -std::vector<AidlCommandReader::CommandError> AidlCommandReader::takeErrors() { - return std::move(mErrors); -} - -bool AidlCommandReader::hasChanges(Display display, uint32_t* outNumChangedCompositionTypes, - uint32_t* outNumLayerRequestMasks) const { - auto found = mReturnData.find(display); - if (found == mReturnData.end()) { - *outNumChangedCompositionTypes = 0; - *outNumLayerRequestMasks = 0; - return false; - } - - const ReturnData& data = found->second; - - *outNumChangedCompositionTypes = static_cast<uint32_t>(data.compositionTypes.size()); - *outNumLayerRequestMasks = static_cast<uint32_t>(data.requestMasks.size()); - - return !(data.compositionTypes.empty() && data.requestMasks.empty()); -} - -void AidlCommandReader::takeChangedCompositionTypes( - Display display, std::vector<Layer>* outLayers, - std::vector<IComposerClient::Composition>* outTypes) { - auto found = mReturnData.find(display); - if (found == mReturnData.end()) { - outLayers->clear(); - outTypes->clear(); - return; - } - - ReturnData& data = found->second; - - *outLayers = std::move(data.changedLayers); - *outTypes = std::move(data.compositionTypes); -} - -void AidlCommandReader::takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask, - std::vector<Layer>* outLayers, - std::vector<uint32_t>* outLayerRequestMasks) { - auto found = mReturnData.find(display); - if (found == mReturnData.end()) { - *outDisplayRequestMask = 0; - outLayers->clear(); - outLayerRequestMasks->clear(); - return; - } - - ReturnData& data = found->second; - - *outDisplayRequestMask = data.displayRequests; - *outLayers = std::move(data.requestedLayers); - *outLayerRequestMasks = std::move(data.requestMasks); -} - -void AidlCommandReader::takeReleaseFences(Display display, std::vector<Layer>* outLayers, - std::vector<int>* outReleaseFences) { - auto found = mReturnData.find(display); - if (found == mReturnData.end()) { - outLayers->clear(); - outReleaseFences->clear(); - return; - } - - ReturnData& data = found->second; - - *outLayers = std::move(data.releasedLayers); - *outReleaseFences = std::move(data.releaseFences); -} - -void AidlCommandReader::takePresentFence(Display display, int* outPresentFence) { - auto found = mReturnData.find(display); - if (found == mReturnData.end()) { - *outPresentFence = -1; - return; - } - - ReturnData& data = found->second; - - *outPresentFence = data.presentFence; - data.presentFence = -1; -} - -void AidlCommandReader::takePresentOrValidateStage(Display display, uint32_t* state) { - auto found = mReturnData.find(display); - if (found == mReturnData.end()) { - *state = static_cast<uint32_t>(-1); - return; - } - ReturnData& data = found->second; - *state = data.presentOrValidateState; -} - -void AidlCommandReader::takeClientTargetProperty( - Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) { - auto found = mReturnData.find(display); - - // If not found, return the default values. - if (found == mReturnData.end()) { - outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888; - outClientTargetProperty->dataspace = Dataspace::UNKNOWN; - return; - } - - ReturnData& data = found->second; - *outClientTargetProperty = data.clientTargetProperty; -} - } // namespace Hwc2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index a6d95000d2..418a8cc18b 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -43,95 +43,10 @@ namespace android::Hwc2 { using AidlCommandWriterBase = aidl::android::hardware::graphics::composer3::CommandWriterBase; using AidlCommandReaderBase = aidl::android::hardware::graphics::composer3::CommandReaderBase; +using aidl::android::hardware::graphics::composer3::command::CommandResultPayload; class AidlIComposerCallbackWrapper; -class AidlCommandReader : public AidlCommandReaderBase { -public: - ~AidlCommandReader(); - - // Parse and execute commands from the command queue. The commands are - // actually return values from the server and will be saved in ReturnData. - int parse(); - - // Get and clear saved errors. - struct CommandError { - uint32_t location; - Error error; - }; - std::vector<CommandError> takeErrors(); - - bool hasChanges(Display display, uint32_t* outNumChangedCompositionTypes, - uint32_t* outNumLayerRequestMasks) const; - - // Get and clear saved changed composition types. - void takeChangedCompositionTypes(Display display, std::vector<Layer>* outLayers, - std::vector<IComposerClient::Composition>* outTypes); - - // Get and clear saved display requests. - void takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask, - std::vector<Layer>* outLayers, - std::vector<uint32_t>* outLayerRequestMasks); - - // Get and clear saved release fences. - void takeReleaseFences(Display display, std::vector<Layer>* outLayers, - std::vector<int>* outReleaseFences); - - // Get and clear saved present fence. - void takePresentFence(Display display, int* outPresentFence); - - // Get what stage succeeded during PresentOrValidate: Present or Validate - void takePresentOrValidateStage(Display display, uint32_t* state); - - // Get the client target properties requested by hardware composer. - void takeClientTargetProperty(Display display, - IComposerClient::ClientTargetProperty* outClientTargetProperty); - -private: - void resetData(); - - bool parseSelectDisplay(uint16_t length); - bool parseSetError(uint16_t length); - bool parseSetChangedCompositionTypes(uint16_t length); - bool parseSetDisplayRequests(uint16_t length); - bool parseSetPresentFence(uint16_t length); - bool parseSetReleaseFences(uint16_t length); - bool parseSetPresentOrValidateDisplayResult(uint16_t length); - bool parseSetClientTargetProperty(uint16_t length); - - struct ReturnData { - uint32_t displayRequests = 0; - - std::vector<Layer> changedLayers; - std::vector<IComposerClient::Composition> compositionTypes; - - std::vector<Layer> requestedLayers; - std::vector<uint32_t> requestMasks; - - int presentFence = -1; - - std::vector<Layer> releasedLayers; - std::vector<int> releaseFences; - - uint32_t presentOrValidateState; - - // Composer 2.4 implementation can return a client target property - // structure to indicate the client target properties that hardware - // composer requests. The composer client must change the client target - // properties to match this request. - IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888, - Dataspace::UNKNOWN}; - }; - - std::vector<CommandError> mErrors; - std::unordered_map<Display, ReturnData> mReturnData; - - // When SELECT_DISPLAY is parsed, this is updated to point to the - // display's return data in mReturnData. We use it to avoid repeated - // map lookups. - ReturnData* mCurrentReturnData; -}; - // Composer is a wrapper to IComposer, a proxy to server-side composer. class AidlComposer final : public Hwc2::Composer { public: @@ -284,13 +199,6 @@ public: IComposerClient::ClientTargetProperty* outClientTargetProperty) override; private: - class AidlCommandWriter : public AidlCommandWriterBase { - public: - explicit AidlCommandWriter(uint32_t initialMaxSize) - : AidlCommandWriterBase(initialMaxSize) {} - ~AidlCommandWriter() override {} - }; - // Many public functions above simply write a command into the command // queue to batch the calls. validateDisplay and presentDisplay will call // this function to execute the command queue. @@ -306,8 +214,8 @@ private: // 1. Tightly coupling this cache to the max size of BufferQueue // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h) static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1; - AidlCommandWriter mWriter; - AidlCommandReader mReader; + AidlCommandWriterBase mWriter; + AidlCommandReaderBase mReader; // Aidl interface using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer; diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h index 5de622b318..0ab9605fca 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayMode.h +++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h @@ -16,9 +16,9 @@ #pragma once -#include "DisplayHardware/Hal.h" -#include "Fps.h" -#include "Scheduler/StrongTyping.h" +#include <cstddef> +#include <memory> +#include <vector> #include <android-base/stringprintf.h> #include <android/configuration.h> @@ -27,9 +27,10 @@ #include <ui/Size.h> #include <utils/Timers.h> -#include <cstddef> -#include <memory> -#include <vector> +#include <scheduler/Fps.h> + +#include "DisplayHardware/Hal.h" +#include "Scheduler/StrongTyping.h" namespace android { @@ -161,4 +162,4 @@ inline std::string to_string(const DisplayMode& mode) { mode.getDpiY(), mode.getGroup()); } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 82a9ae2578..b4fb51f9d5 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -21,20 +21,18 @@ // #define LOG_NDEBUG 0 #include "VirtualDisplaySurface.h" -#include <inttypes.h> +#include <cinttypes> #include "HWComposer.h" #include "SurfaceFlinger.h" +#include <ftl/Flags.h> +#include <ftl/enum.h> #include <gui/BufferItem.h> #include <gui/BufferQueue.h> #include <gui/IProducerListener.h> #include <system/window.h> -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - #define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \ mDisplayName.c_str(), ##__VA_ARGS__) #define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \ @@ -42,20 +40,11 @@ namespace android { #define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \ mDisplayName.c_str(), ##__VA_ARGS__) -static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::CompositionType type) { - switch (type) { - case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN: - return "UNKNOWN"; - case compositionengine::DisplaySurface::COMPOSITION_GPU: - return "GPU"; - case compositionengine::DisplaySurface::COMPOSITION_HWC: - return "HWC"; - case compositionengine::DisplaySurface::COMPOSITION_MIXED: - return "MIXED"; - default: - return "<INVALID>"; - } -} +#define UNSUPPORTED() \ + VDS_LOGE("%s: Invalid operation on virtual display", __func__); \ + return INVALID_OPERATION + +namespace android { VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId, const sp<IGraphicBufferProducer>& sink, @@ -76,14 +65,10 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId d mQueueBufferOutput(), mSinkBufferWidth(0), mSinkBufferHeight(0), - mCompositionType(COMPOSITION_UNKNOWN), mFbFence(Fence::NO_FENCE), mOutputFence(Fence::NO_FENCE), mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT), mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT), - mDbgState(DBG_STATE_IDLE), - mDbgLastCompositionType(COMPOSITION_UNKNOWN), - mMustRecompose(false), mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv) { mSource[SOURCE_SINK] = sink; mSource[SOURCE_SCRATCH] = bqProducer; @@ -131,9 +116,9 @@ status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) { mMustRecompose = mustRecompose; - VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE, - "Unexpected beginFrame() in %s state", dbgStateStr()); - mDbgState = DBG_STATE_BEGUN; + VDS_LOGW_IF(mDebugState != DebugState::Idle, "Unexpected %s in %s state", __func__, + ftl::enum_string(mDebugState).c_str()); + mDebugState = DebugState::Begun; return refreshOutputBuffer(); } @@ -143,12 +128,12 @@ status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { return NO_ERROR; } - VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN, - "Unexpected prepareFrame() in %s state", dbgStateStr()); - mDbgState = DBG_STATE_PREPARED; + VDS_LOGW_IF(mDebugState != DebugState::Begun, "Unexpected %s in %s state", __func__, + ftl::enum_string(mDebugState).c_str()); + mDebugState = DebugState::Prepared; mCompositionType = compositionType; - if (mForceHwcCopy && mCompositionType == COMPOSITION_GPU) { + if (mForceHwcCopy && mCompositionType == CompositionType::Gpu) { // Some hardware can do RGB->YUV conversion more efficiently in hardware // controlled by HWC than in hardware controlled by the video encoder. // Forcing GPU-composed frames to go through an extra copy by the HWC @@ -157,16 +142,16 @@ status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { // // On the other hand, when the consumer prefers RGB or can consume RGB // inexpensively, this forces an unnecessary copy. - mCompositionType = COMPOSITION_MIXED; + mCompositionType = CompositionType::Mixed; } - if (mCompositionType != mDbgLastCompositionType) { - VDS_LOGV("prepareFrame: composition type changed to %s", - dbgCompositionTypeStr(mCompositionType)); - mDbgLastCompositionType = mCompositionType; + if (mCompositionType != mDebugLastCompositionType) { + VDS_LOGV("%s: composition type changed to %s", __func__, + toString(mCompositionType).c_str()); + mDebugLastCompositionType = mCompositionType; } - if (mCompositionType != COMPOSITION_GPU && + if (mCompositionType != CompositionType::Gpu && (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) { // We must have just switched from GPU-only to MIXED or HWC // composition. Stop using the format and usage requested by the GPU @@ -191,33 +176,32 @@ status_t VirtualDisplaySurface::advanceFrame() { return NO_ERROR; } - if (mCompositionType == COMPOSITION_HWC) { - VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, - "Unexpected advanceFrame() in %s state on HWC frame", - dbgStateStr()); + if (mCompositionType == CompositionType::Hwc) { + VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state on HWC frame", + __func__, ftl::enum_string(mDebugState).c_str()); } else { - VDS_LOGW_IF(mDbgState != DBG_STATE_GPU_DONE, - "Unexpected advanceFrame() in %s state on GPU/MIXED frame", dbgStateStr()); + VDS_LOGW_IF(mDebugState != DebugState::GpuDone, + "Unexpected %s in %s state on GPU/MIXED frame", __func__, + ftl::enum_string(mDebugState).c_str()); } - mDbgState = DBG_STATE_HWC; + mDebugState = DebugState::Hwc; if (mOutputProducerSlot < 0 || - (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) { + (mCompositionType != CompositionType::Hwc && mFbProducerSlot < 0)) { // Last chance bailout if something bad happened earlier. For example, // in a graphics API configuration, if the sink disappears then dequeueBuffer // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger // will soldier on. So we end up here without a buffer. There should // be lots of scary messages in the log just before this. - VDS_LOGE("advanceFrame: no buffer, bailing out"); + VDS_LOGE("%s: no buffer, bailing out", __func__); return NO_MEMORY; } sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr); sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot]; - VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)", - mFbProducerSlot, fbBuffer.get(), - mOutputProducerSlot, outBuffer.get()); + VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(), + mOutputProducerSlot, outBuffer.get()); const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); LOG_FATAL_IF(!halDisplayId); @@ -245,16 +229,16 @@ void VirtualDisplaySurface::onFrameCommitted() { return; } - VDS_LOGW_IF(mDbgState != DBG_STATE_HWC, - "Unexpected onFrameCommitted() in %s state", dbgStateStr()); - mDbgState = DBG_STATE_IDLE; + VDS_LOGW_IF(mDebugState != DebugState::Hwc, "Unexpected %s in %s state", __func__, + ftl::enum_string(mDebugState).c_str()); + mDebugState = DebugState::Idle; sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId); - if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) { + if (mCompositionType == CompositionType::Mixed && mFbProducerSlot >= 0) { // release the scratch buffer back to the pool Mutex::Autolock lock(mMutex); int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot); - VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot); + VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot); addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], retireFence); releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]); @@ -263,7 +247,7 @@ void VirtualDisplaySurface::onFrameCommitted() { if (mOutputProducerSlot >= 0) { int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot); QueueBufferOutput qbo; - VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot); + VDS_LOGV("%s: queue sink sslot=%d", __func__, sslot); if (mMustRecompose) { status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot, QueueBufferInput( @@ -308,8 +292,8 @@ status_t VirtualDisplaySurface::requestBuffer(int pslot, return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf); } - VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected requestBuffer pslot=%d in %s state", pslot, - dbgStateStr()); + VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s pslot=%d in %s state", __func__, + pslot, ftl::enum_string(mDebugState).c_str()); *outBuf = mProducerBuffers[pslot]; return NO_ERROR; @@ -334,8 +318,8 @@ status_t VirtualDisplaySurface::dequeueBuffer(Source source, if (result < 0) return result; int pslot = mapSource2ProducerSlot(source, *sslot); - VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d", - dbgSourceStr(source), *sslot, pslot, result); + VDS_LOGV("%s(%s): sslot=%d pslot=%d result=%d", __func__, ftl::enum_string(source).c_str(), + *sslot, pslot, result); uint64_t sourceBit = static_cast<uint64_t>(source) << pslot; // reset producer slot reallocation flag @@ -363,10 +347,9 @@ status_t VirtualDisplaySurface::dequeueBuffer(Source source, mSource[source]->cancelBuffer(*sslot, *fence); return result; } - VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64, - dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(), - mProducerBuffers[pslot]->getPixelFormat(), - mProducerBuffers[pslot]->getUsage()); + VDS_LOGV("%s(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64, __func__, + ftl::enum_string(source).c_str(), pslot, mProducerBuffers[pslot].get(), + mProducerBuffers[pslot]->getPixelFormat(), mProducerBuffers[pslot]->getUsage()); // propagate reallocation to VDS consumer mProducerSlotNeedReallocation |= 1ULL << pslot; @@ -384,11 +367,11 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, uint outTimestamps); } - VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, - "Unexpected dequeueBuffer() in %s state", dbgStateStr()); - mDbgState = DBG_STATE_GPU; + VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state", __func__, + ftl::enum_string(mDebugState).c_str()); + mDebugState = DebugState::Gpu; - VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#" PRIx64, w, h, format, usage); + VDS_LOGV("%s %dx%d fmt=%d usage=%#" PRIx64, __func__, w, h, format, usage); status_t result = NO_ERROR; Source source = fbSourceForCompositionType(mCompositionType); @@ -401,7 +384,7 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, uint // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger // will soldier on. So we end up here without a buffer. There should // be lots of scary messages in the log just before this. - VDS_LOGE("dequeueBuffer: no buffer, bailing out"); + VDS_LOGE("%s: no buffer, bailing out", __func__); return NO_MEMORY; } @@ -417,12 +400,11 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, uint (format != 0 && format != buf->getPixelFormat()) || (w != 0 && w != mSinkBufferWidth) || (h != 0 && h != mSinkBufferHeight)) { - VDS_LOGV("dequeueBuffer: dequeueing new output buffer: " - "want %dx%d fmt=%d use=%#" PRIx64 ", " - "have %dx%d fmt=%d use=%#" PRIx64, - w, h, format, usage, - mSinkBufferWidth, mSinkBufferHeight, - buf->getPixelFormat(), buf->getUsage()); + VDS_LOGV("%s: dequeueing new output buffer: " + "want %dx%d fmt=%d use=%#" PRIx64 ", " + "have %dx%d fmt=%d use=%#" PRIx64, + __func__, w, h, format, usage, mSinkBufferWidth, mSinkBufferHeight, + buf->getPixelFormat(), buf->getUsage()); mOutputFormat = format; mOutputUsage = usage; result = refreshOutputBuffer(); @@ -452,21 +434,16 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, uint return result; } -status_t VirtualDisplaySurface::detachBuffer(int /* slot */) { - VDS_LOGE("detachBuffer is not available for VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::detachBuffer(int) { + UNSUPPORTED(); } -status_t VirtualDisplaySurface::detachNextBuffer( - sp<GraphicBuffer>* /* outBuffer */, sp<Fence>* /* outFence */) { - VDS_LOGE("detachNextBuffer is not available for VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::detachNextBuffer(sp<GraphicBuffer>*, sp<Fence>*) { + UNSUPPORTED(); } -status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */, - const sp<GraphicBuffer>& /* buffer */) { - VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::attachBuffer(int*, const sp<GraphicBuffer>&) { + UNSUPPORTED(); } status_t VirtualDisplaySurface::queueBuffer(int pslot, @@ -475,14 +452,14 @@ status_t VirtualDisplaySurface::queueBuffer(int pslot, return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output); } - VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected queueBuffer(pslot=%d) in %s state", pslot, - dbgStateStr()); - mDbgState = DBG_STATE_GPU_DONE; + VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__, + pslot, ftl::enum_string(mDebugState).c_str()); + mDebugState = DebugState::GpuDone; - VDS_LOGV("queueBuffer pslot=%d", pslot); + VDS_LOGV("%s pslot=%d", __func__, pslot); status_t result; - if (mCompositionType == COMPOSITION_MIXED) { + if (mCompositionType == CompositionType::Mixed) { // Queue the buffer back into the scratch pool QueueBufferOutput scratchQBO; int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot); @@ -498,15 +475,15 @@ status_t VirtualDisplaySurface::queueBuffer(int pslot, if (result != NO_ERROR) return result; VDS_LOGW_IF(item.mSlot != sslot, - "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d", - item.mSlot, sslot); + "%s: acquired sslot %d from SCRATCH after queueing sslot %d", __func__, + item.mSlot, sslot); mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mSlot); mFbFence = mSlots[item.mSlot].mFence; } else { - LOG_FATAL_IF(mCompositionType != COMPOSITION_GPU, - "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(), - dbgCompositionTypeStr(mCompositionType)); + LOG_FATAL_IF(mCompositionType != CompositionType::Gpu, + "Unexpected %s in state %s for composition type %s", __func__, + ftl::enum_string(mDebugState).c_str(), toString(mCompositionType).c_str()); // Extract the GPU release fence for HWC to acquire int64_t timestamp; @@ -533,9 +510,9 @@ status_t VirtualDisplaySurface::cancelBuffer(int pslot, return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence); } - VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected cancelBuffer(pslot=%d) in %s state", pslot, - dbgStateStr()); - VDS_LOGV("cancelBuffer pslot=%d", pslot); + VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__, + pslot, ftl::enum_string(mDebugState).c_str()); + VDS_LOGV("%s pslot=%d", __func__, pslot); Source source = fbSourceForCompositionType(mCompositionType); return mSource[source]->cancelBuffer( mapProducer2SourceSlot(source, pslot), fence); @@ -573,8 +550,8 @@ status_t VirtualDisplaySurface::disconnect(int api, DisconnectMode mode) { return mSource[SOURCE_SINK]->disconnect(api, mode); } -status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) { - return INVALID_OPERATION; +status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>&) { + UNSUPPORTED(); } void VirtualDisplaySurface::allocateBuffers(uint32_t /* width */, @@ -586,40 +563,32 @@ status_t VirtualDisplaySurface::allowAllocation(bool /* allow */) { return INVALID_OPERATION; } -status_t VirtualDisplaySurface::setGenerationNumber(uint32_t /* generation */) { - ALOGE("setGenerationNumber not supported on VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::setGenerationNumber(uint32_t) { + UNSUPPORTED(); } String8 VirtualDisplaySurface::getConsumerName() const { return String8("VirtualDisplaySurface"); } -status_t VirtualDisplaySurface::setSharedBufferMode(bool /*sharedBufferMode*/) { - ALOGE("setSharedBufferMode not supported on VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::setSharedBufferMode(bool) { + UNSUPPORTED(); } -status_t VirtualDisplaySurface::setAutoRefresh(bool /*autoRefresh*/) { - ALOGE("setAutoRefresh not supported on VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::setAutoRefresh(bool) { + UNSUPPORTED(); } -status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t /* timeout */) { - ALOGE("setDequeueTimeout not supported on VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t) { + UNSUPPORTED(); } -status_t VirtualDisplaySurface::getLastQueuedBuffer( - sp<GraphicBuffer>* /*outBuffer*/, sp<Fence>* /*outFence*/, - float[16] /* outTransformMatrix*/) { - ALOGE("getLastQueuedBuffer not supported on VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::getLastQueuedBuffer(sp<GraphicBuffer>*, sp<Fence>*, float[16]) { + UNSUPPORTED(); } -status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const { - ALOGE("getUniqueId not supported on VirtualDisplaySurface"); - return INVALID_OPERATION; +status_t VirtualDisplaySurface::getUniqueId(uint64_t*) const { + UNSUPPORTED(); } status_t VirtualDisplaySurface::getConsumerUsage(uint64_t* outUsage) const { @@ -633,7 +602,7 @@ void VirtualDisplaySurface::updateQueueBufferOutput( } void VirtualDisplaySurface::resetPerFrameState() { - mCompositionType = COMPOSITION_UNKNOWN; + mCompositionType = CompositionType::Unknown; mFbFence = Fence::NO_FENCE; mOutputFence = Fence::NO_FENCE; mOutputProducerSlot = -1; @@ -682,39 +651,16 @@ int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) { return mapSource2ProducerSlot(source, pslot); } -VirtualDisplaySurface::Source -VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) { - return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK; +auto VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) -> Source { + return type == CompositionType::Mixed ? SOURCE_SCRATCH : SOURCE_SINK; } -const char* VirtualDisplaySurface::dbgStateStr() const { - switch (mDbgState) { - case DBG_STATE_IDLE: - return "IDLE"; - case DBG_STATE_PREPARED: - return "PREPARED"; - case DBG_STATE_GPU: - return "GPU"; - case DBG_STATE_GPU_DONE: - return "GPU_DONE"; - case DBG_STATE_HWC: - return "HWC"; - default: - return "INVALID"; - } -} - -const char* VirtualDisplaySurface::dbgSourceStr(Source s) { - switch (s) { - case SOURCE_SINK: return "SINK"; - case SOURCE_SCRATCH: return "SCRATCH"; - default: return "INVALID"; - } +std::string VirtualDisplaySurface::toString(CompositionType type) { + using namespace std::literals; + return type == CompositionType::Unknown ? "Unknown"s : Flags(type).string(); } -// --------------------------------------------------------------------------- } // namespace android -// --------------------------------------------------------------------------- // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index bbb6306920..77207134b9 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H -#define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H +#pragma once #include <optional> #include <string> @@ -28,9 +27,7 @@ #include "DisplayIdentification.h" -// --------------------------------------------------------------------------- namespace android { -// --------------------------------------------------------------------------- class HWComposer; class IProducerListener; @@ -94,7 +91,13 @@ public: virtual const sp<Fence>& getClientTargetAcquireFence() const override; private: - enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1}; + enum Source : size_t { + SOURCE_SINK = 0, + SOURCE_SCRATCH = 1, + + ftl_first = SOURCE_SINK, + ftl_last = SOURCE_SCRATCH, + }; virtual ~VirtualDisplaySurface(); @@ -133,6 +136,8 @@ private: // Utility methods // static Source fbSourceForCompositionType(CompositionType); + static std::string toString(CompositionType); + status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*); void updateQueueBufferOutput(QueueBufferOutput&&); void resetPerFrameState(); @@ -197,7 +202,7 @@ private: // Composition type and graphics buffer source for the current frame. // Valid after prepareFrame(), cleared in onFrameCommitted. - CompositionType mCompositionType; + CompositionType mCompositionType = CompositionType::Unknown; // mFbFence is the fence HWC should wait for before reading the framebuffer // target buffer. @@ -219,47 +224,42 @@ private: // +-----------+-------------------+-------------+ // | State | Event || Next State | // +-----------+-------------------+-------------+ - // | IDLE | beginFrame || BEGUN | - // | BEGUN | prepareFrame || PREPARED | - // | PREPARED | dequeueBuffer [1] || GPU | - // | PREPARED | advanceFrame [2] || HWC | - // | GPU | queueBuffer || GPU_DONE | - // | GPU_DONE | advanceFrame || HWC | - // | HWC | onFrameCommitted || IDLE | + // | Idle | beginFrame || Begun | + // | Begun | prepareFrame || Prepared | + // | Prepared | dequeueBuffer [1] || Gpu | + // | Prepared | advanceFrame [2] || Hwc | + // | Gpu | queueBuffer || GpuDone | + // | GpuDone | advanceFrame || Hwc | + // | Hwc | onFrameCommitted || Idle | // +-----------+-------------------++------------+ - // [1] COMPOSITION_GPU and COMPOSITION_MIXED frames. - // [2] COMPOSITION_HWC frames. + // [1] CompositionType::Gpu and CompositionType::Mixed frames. + // [2] CompositionType::Hwc frames. // - enum DbgState { + enum class DebugState { // no buffer dequeued, don't know anything about the next frame - DBG_STATE_IDLE, + Idle, // output buffer dequeued, framebuffer source not yet known - DBG_STATE_BEGUN, + Begun, // output buffer dequeued, framebuffer source known but not provided // to GPU yet. - DBG_STATE_PREPARED, + Prepared, // GPU driver has a buffer dequeued - DBG_STATE_GPU, + Gpu, // GPU driver has queued the buffer, we haven't sent it to HWC yet - DBG_STATE_GPU_DONE, + GpuDone, // HWC has the buffer for this frame - DBG_STATE_HWC, - }; - DbgState mDbgState; - CompositionType mDbgLastCompositionType; + Hwc, - const char* dbgStateStr() const; - static const char* dbgSourceStr(Source s); + ftl_last = Hwc + }; + DebugState mDebugState = DebugState::Idle; + CompositionType mDebugLastCompositionType = CompositionType::Unknown; - bool mMustRecompose; + bool mMustRecompose = false; compositionengine::impl::HwcBufferCache mHwcBufferCache; bool mForceHwcCopy; }; -// --------------------------------------------------------------------------- } // namespace android -// --------------------------------------------------------------------------- - -#endif // ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp index 10a58333f9..2d4ec04cfc 100644 --- a/services/surfaceflinger/FrameTimeline/Android.bp +++ b/services/surfaceflinger/FrameTimeline/Android.bp @@ -13,6 +13,9 @@ cc_library_static { srcs: [ "FrameTimeline.cpp", ], + header_libs: [ + "libscheduler_headers", + ], shared_libs: [ "android.hardware.graphics.composer@2.4", "libbase", diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index c294ff2695..0c4e1120fc 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -304,7 +304,7 @@ SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t own frametimeline::TimelineItem&& predictions, std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds, - TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode) + TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode gameMode) : mToken(frameTimelineInfo.vsyncId), mInputEventId(frameTimelineInfo.inputEventId), mOwnerPid(ownerPid), @@ -778,7 +778,7 @@ void FrameTimeline::registerDataSource() { std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId, - std::string layerName, std::string debugName, bool isBuffer, int32_t gameMode) { + std::string layerName, std::string debugName, bool isBuffer, GameMode gameMode) { ATRACE_CALL(); if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId, diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 139f91f3bc..36d629077d 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -16,10 +16,17 @@ #pragma once -#include <../Fps.h> -#include <../TimeStats/TimeStats.h> +#include <atomic> +#include <chrono> +#include <deque> +#include <memory> +#include <mutex> +#include <optional> +#include <string> + #include <gui/ISurfaceComposer.h> #include <gui/JankInfo.h> +#include <gui/LayerMetadata.h> #include <perfetto/trace/android/frame_timeline_event.pbzero.h> #include <perfetto/tracing.h> #include <ui/FenceTime.h> @@ -28,8 +35,9 @@ #include <utils/Timers.h> #include <utils/Vector.h> -#include <deque> -#include <mutex> +#include <scheduler/Fps.h> + +#include "../TimeStats/TimeStats.h" namespace android::frametimeline { @@ -154,7 +162,7 @@ public: int32_t layerId, std::string layerName, std::string debugName, PredictionState predictionState, TimelineItem&& predictions, std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds, - TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode); + TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode); ~SurfaceFrame() = default; // Returns std::nullopt if the frame hasn't been classified yet. @@ -260,7 +268,7 @@ private: // buffer(animations) bool mIsBuffer; // GameMode from the layer. Used in metrics. - int32_t mGameMode = 0; + GameMode mGameMode = GameMode::Unsupported; }; /* @@ -281,7 +289,7 @@ public: virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken( const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId, std::string layerName, std::string debugName, bool isBuffer, - int32_t gameMode) = 0; + GameMode) = 0; // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be // composited into one display frame. @@ -441,7 +449,7 @@ public: std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken( const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId, std::string layerName, std::string debugName, bool isBuffer, - int32_t gameMode) override; + GameMode) override; void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 968a49d526..4606746d4b 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -36,6 +36,7 @@ #include <cutils/compiler.h> #include <cutils/native_handle.h> #include <cutils/properties.h> +#include <ftl/enum.h> #include <gui/BufferItem.h> #include <gui/LayerDebugInfo.h> #include <gui/Surface.h> @@ -417,6 +418,8 @@ void Layer::prepareBasicGeometryCompositionState() { compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); compositionState->alpha = alpha; + compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; + compositionState->blurRegions = drawingState.blurRegions; compositionState->stretchEffect = getStretchEffect(); } @@ -481,6 +484,11 @@ void Layer::preparePerFrameCompositionState() { // If there are no visible region changes, we still need to update blur parameters. compositionState->blurRegions = drawingState.blurRegions; compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; + + // Layer framerate is used in caching decisions. + // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in + // LayerFECompositionState where it would be visible to Flattener. + compositionState->fps = mFlinger->getLayerFramerate(systemTime(), getSequence()); } void Layer::prepareCursorCompositionState() { @@ -931,7 +939,6 @@ bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) { setTransactionFlags(eTransactionNeeded); return true; } - bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix, bool allowNonRectPreservingTransforms) { ui::Transform t; @@ -1293,8 +1300,8 @@ bool Layer::setFrameRateForLayerTree(FrameRate frameRate) { mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); - mFlinger->mScheduler->recordLayerHistory(this, systemTime(), - LayerHistory::LayerUpdateType::SetFrameRate); + using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; + mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate); return true; } @@ -1411,19 +1418,6 @@ void Layer::miniDumpHeader(std::string& result) { result.append("\n"); } -std::string Layer::frameRateCompatibilityString(Layer::FrameRateCompatibility compatibility) { - switch (compatibility) { - case FrameRateCompatibility::Default: - return "Default"; - case FrameRateCompatibility::ExactOrMultiple: - return "ExactOrMultiple"; - case FrameRateCompatibility::NoVote: - return "NoVote"; - case FrameRateCompatibility::Exact: - return "Exact"; - } -} - void Layer::miniDump(std::string& result, const DisplayDevice& display) const { const auto outputLayer = findOutputLayerForDisplay(&display); if (!outputLayer) { @@ -1462,8 +1456,8 @@ void Layer::miniDump(std::string& result, const DisplayDevice& display) const { const auto frameRate = getFrameRateForLayerTree(); if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) { StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(), - frameRateCompatibilityString(frameRate.type).c_str(), - toString(frameRate.seamlessness).c_str()); + ftl::enum_string(frameRate.type).c_str(), + ftl::enum_string(frameRate.seamlessness).c_str()); } else { result.append(41, ' '); } @@ -1546,11 +1540,10 @@ size_t Layer::getChildrenCount() const { return count; } -void Layer::setGameModeForTree(int parentGameMode) { - int gameMode = parentGameMode; - auto& currentState = getDrawingState(); +void Layer::setGameModeForTree(GameMode gameMode) { + const auto& currentState = getDrawingState(); if (currentState.metadata.has(METADATA_GAME_MODE)) { - gameMode = currentState.metadata.getInt32(METADATA_GAME_MODE, 0); + gameMode = static_cast<GameMode>(currentState.metadata.getInt32(METADATA_GAME_MODE, 0)); } setGameMode(gameMode); for (const sp<Layer>& child : mCurrentChildren) { @@ -1576,7 +1569,7 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { const auto removeResult = mCurrentChildren.remove(layer); updateTreeHasFrameRateVote(); - layer->setGameModeForTree(0); + layer->setGameModeForTree(GameMode::Unsupported); layer->updateTreeHasFrameRateVote(); return removeResult; @@ -2182,6 +2175,8 @@ void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTra info.frameRight = 0; info.frameBottom = 0; info.transform.reset(); + info.touchableRegion = Region(); + info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE; return; } @@ -2429,8 +2424,7 @@ Region Layer::getVisibleRegion(const DisplayDevice* display) const { } void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) { - // copy drawing state from cloned layer - mDrawingState = clonedFrom->mDrawingState; + cloneDrawingState(clonedFrom.get()); mClonedFrom = clonedFrom; } @@ -2465,7 +2459,7 @@ void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayer // since we may be able to pull out other children that are still alive. if (isClonedFromAlive()) { sp<Layer> clonedFrom = getClonedFrom(); - mDrawingState = clonedFrom->mDrawingState; + cloneDrawingState(clonedFrom.get()); clonedLayersMap.emplace(clonedFrom, this); } @@ -2628,15 +2622,21 @@ bool Layer::setDropInputMode(gui::DropInputMode mode) { return true; } +void Layer::cloneDrawingState(const Layer* from) { + mDrawingState = from->mDrawingState; + // Skip callback info since they are not applicable for cloned layers. + mDrawingState.releaseBufferListener = nullptr; + mDrawingState.callbackHandles = {}; +} + // --------------------------------------------------------------------------- std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) { - return stream << "{rate=" << rate.rate - << " type=" << Layer::frameRateCompatibilityString(rate.type) - << " seamlessness=" << toString(rate.seamlessness) << "}"; + return stream << "{rate=" << rate.rate << " type=" << ftl::enum_string(rate.type) + << " seamlessness=" << ftl::enum_string(rate.seamlessness) << '}'; } -}; // namespace android +} // namespace android #if defined(__gl_h_) #error "don't include gl/gl.h in this file" diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index bda1c28d27..3f4d48be63 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -18,7 +18,6 @@ #pragma once #include <android/gui/DropInputMode.h> -#include <compositionengine/LayerFE.h> #include <gui/BufferQueue.h> #include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> @@ -39,6 +38,10 @@ #include <utils/RefBase.h> #include <utils/Timers.h> +#include <compositionengine/LayerFE.h> +#include <scheduler/Fps.h> +#include <scheduler/Seamlessness.h> + #include <chrono> #include <cstdint> #include <list> @@ -49,13 +52,11 @@ #include "ClientCache.h" #include "DisplayHardware/ComposerHal.h" #include "DisplayHardware/HWComposer.h" -#include "Fps.h" #include "FrameTracker.h" #include "LayerVector.h" #include "MonitoredProducer.h" #include "RenderArea.h" #include "Scheduler/LayerInfo.h" -#include "Scheduler/Seamlessness.h" #include "SurfaceFlinger.h" #include "Tracing/LayerTracing.h" #include "TransactionCallbackInvoker.h" @@ -327,7 +328,6 @@ public: static bool isLayerFocusedBasedOnPriority(int32_t priority); static void miniDumpHeader(std::string& result); - static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility); // Provide unique string for each class type in the Layer hierarchy virtual const char* getType() const = 0; @@ -853,12 +853,12 @@ public: */ bool hasInputInfo() const; - // Sets the parent's gameMode for this layer and all its children. Parent's gameMode is applied - // only to layers that do not have the GAME_MODE_METADATA set by WMShell. Any layer(along with - // its children) that has the metadata set will use the gameMode from the metadata. - void setGameModeForTree(int32_t parentGameMode); - void setGameMode(int32_t gameMode) { mGameMode = gameMode; }; - int32_t getGameMode() const { return mGameMode; } + // Sets the GameMode for the tree rooted at this layer. A layer in the tree inherits this + // GameMode unless it (or an ancestor) has GAME_MODE_METADATA. + void setGameModeForTree(GameMode); + + void setGameMode(GameMode gameMode) { mGameMode = gameMode; } + GameMode getGameMode() const { return mGameMode; } virtual uid_t getOwnerUid() const { return mOwnerUid; } @@ -922,6 +922,7 @@ protected: bool isClone() { return mClonedFrom != nullptr; } bool isClonedFromAlive() { return getClonedFrom() != nullptr; } + void cloneDrawingState(const Layer* from); void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); void updateClonedChildren(const sp<Layer>& mirrorRoot, std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); @@ -1109,9 +1110,8 @@ private: // shadow radius is the set shadow radius, otherwise its the parent's shadow radius. float mEffectiveShadowRadius = 0.f; - // Game mode for the layer. Set by WindowManagerShell, game mode is used in - // metrics(SurfaceFlingerStats). - int32_t mGameMode = 0; + // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats. + GameMode mGameMode = GameMode::Unsupported; // A list of regions on this layer that should have blurs. const std::vector<BlurRegion> getBlurRegions() const; diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 2502d66a67..81c1566d71 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -203,12 +203,13 @@ bool RefreshRateOverlay::createLayer() { return false; } - SurfaceComposerClient::Transaction t; - t.setFrameRate(mSurfaceControl, 0.0f, - static_cast<int8_t>(Layer::FrameRateCompatibility::NoVote), - static_cast<int8_t>(scheduler::Seamlessness::OnlySeamless)); - t.setLayer(mSurfaceControl, INT32_MAX - 2); - t.apply(); + SurfaceComposerClient::Transaction() + .setFrameRate(mSurfaceControl, 0.0f, + static_cast<int8_t>(Layer::FrameRateCompatibility::NoVote), + static_cast<int8_t>(scheduler::Seamlessness::OnlySeamless)) + .setLayer(mSurfaceControl, INT32_MAX - 2) + .setTrustedOverlay(mSurfaceControl, true) + .apply(); return true; } @@ -276,7 +277,7 @@ void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) { t.apply(); } -void RefreshRateOverlay::changeRefreshRate(const Fps& fps) { +void RefreshRateOverlay::changeRefreshRate(Fps fps) { mCurrentFps = fps.getIntValue(); auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame]; SurfaceComposerClient::Transaction t; diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index 65d446c751..381df37903 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -16,6 +16,10 @@ #pragma once +#include <SkCanvas.h> +#include <SkColor.h> +#include <unordered_map> + #include <math/vec4.h> #include <renderengine/RenderEngine.h> #include <ui/LayerStack.h> @@ -23,11 +27,7 @@ #include <ui/Size.h> #include <utils/StrongPointer.h> -#include <SkCanvas.h> -#include <SkColor.h> -#include <unordered_map> - -#include "Fps.h" +#include <scheduler/Fps.h> namespace android { @@ -45,7 +45,7 @@ public: void setLayerStack(ui::LayerStack); void setViewport(ui::Size); - void changeRefreshRate(const Fps&); + void changeRefreshRate(Fps); void animate(); private: diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 32585dd9ac..da8c3e062c 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -30,7 +30,6 @@ #include <compositionengine/impl/OutputCompositionState.h> #include <cutils/properties.h> #include <ftl/future.h> -#include <gui/IRegionSamplingListener.h> #include <gui/SyncScreenCaptureListener.h> #include <ui/DisplayStatInfo.h> #include <utils/Trace.h> diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h index f715309b2e..686b4b1e1f 100644 --- a/services/surfaceflinger/RegionSamplingThread.h +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -17,6 +17,7 @@ #pragma once #include <android-base/thread_annotations.h> +#include <android/gui/IRegionSamplingListener.h> #include <binder/IBinder.h> #include <renderengine/ExternalTexture.h> #include <ui/GraphicBuffer.h> @@ -34,12 +35,13 @@ namespace android { -class IRegionSamplingListener; class Layer; class Scheduler; class SurfaceFlinger; struct SamplingOffsetCallback; +using gui::IRegionSamplingListener; + float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& area); diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp new file mode 100644 index 0000000000..409d09858f --- /dev/null +++ b/services/surfaceflinger/Scheduler/Android.bp @@ -0,0 +1,35 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_defaults { + name: "libscheduler_defaults", + defaults: ["surfaceflinger_defaults"], + cflags: [ + "-DLOG_TAG=\"Scheduler\"", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", + ], + shared_libs: [ + "libbase", + "libutils", + ], +} + +cc_library_headers { + name: "libscheduler_headers", + defaults: ["libscheduler_defaults"], + export_include_dirs: ["include"], +} + +cc_library_static { + name: "libscheduler", + defaults: ["libscheduler_defaults"], + srcs: [], + local_include_dirs: ["include"], + export_include_dirs: ["include"], +} diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 455289ff96..627c49a853 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -170,20 +170,21 @@ void EventThreadConnection::onFirstRef() { mEventThread->registerDisplayEventConnection(this); } -status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) { +binder::Status EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) { outChannel->setReceiveFd(mChannel.moveReceiveFd()); outChannel->setSendFd(base::unique_fd(dup(mChannel.getSendFd()))); - return NO_ERROR; + return binder::Status::ok(); } -status_t EventThreadConnection::setVsyncRate(uint32_t rate) { - mEventThread->setVsyncRate(rate, this); - return NO_ERROR; +binder::Status EventThreadConnection::setVsyncRate(int rate) { + mEventThread->setVsyncRate(static_cast<uint32_t>(rate), this); + return binder::Status::ok(); } -void EventThreadConnection::requestNextVsync() { +binder::Status EventThreadConnection::requestNextVsync() { ATRACE_NAME("requestNextVsync"); mEventThread->requestNextVsync(this); + return binder::Status::ok(); } status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) { diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index de435708a6..fa9af098f6 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -17,8 +17,8 @@ #pragma once #include <android-base/thread_annotations.h> +#include <android/gui/BnDisplayEventConnection.h> #include <gui/DisplayEventReceiver.h> -#include <gui/IDisplayEventConnection.h> #include <private/gui/BitTube.h> #include <sys/types.h> #include <utils/Errors.h> @@ -80,7 +80,7 @@ public: virtual void dump(std::string& result) const = 0; }; -class EventThreadConnection : public BnDisplayEventConnection { +class EventThreadConnection : public gui::BnDisplayEventConnection { public: EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback, ISurfaceComposer::EventRegistrationFlags eventRegistration = {}); @@ -88,9 +88,9 @@ public: virtual status_t postEvent(const DisplayEventReceiver::Event& event); - status_t stealReceiveChannel(gui::BitTube* outChannel) override; - status_t setVsyncRate(uint32_t rate) override; - void requestNextVsync() override; // asynchronous + binder::Status stealReceiveChannel(gui::BitTube* outChannel) override; + binder::Status setVsyncRate(int rate) override; + binder::Status requestNextVsync() override; // asynchronous // Called in response to requestNextVsync. const ResyncCallback resyncCallback; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 84e3548b6e..74a2ca786c 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -84,42 +84,38 @@ LayerHistory::~LayerHistory() = default; void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) { std::lock_guard lock(mLock); - for (const auto& info : mLayerInfos) { - LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str()); - } + LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first != + LayerHistory::layerStatus::NotFound, + "%s already registered", layer->getName().c_str()); auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type); - mLayerInfos.emplace_back(layer, std::move(info)); + + // The layer can be placed on either map, it is assumed that partitionLayers() will be called + // to correct them. + mInactiveLayerInfos.insert({layer->getSequence(), std::make_pair(layer, std::move(info))}); } void LayerHistory::deregisterLayer(Layer* layer) { std::lock_guard lock(mLock); - - const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(), - [layer](const auto& pair) { return pair.first == layer; }); - LOG_ALWAYS_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer); - - const size_t i = static_cast<size_t>(it - mLayerInfos.begin()); - if (i < mActiveLayersEnd) { - mActiveLayersEnd--; + if (!mActiveLayerInfos.erase(layer->getSequence())) { + if (!mInactiveLayerInfos.erase(layer->getSequence())) { + LOG_ALWAYS_FATAL("%s: unknown layer %p", __FUNCTION__, layer); + } } - const size_t last = mLayerInfos.size() - 1; - std::swap(mLayerInfos[i], mLayerInfos[last]); - mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last)); } void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) { std::lock_guard lock(mLock); + auto id = layer->getSequence(); - const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(), - [layer](const auto& pair) { return pair.first == layer; }); - if (it == mLayerInfos.end()) { + auto [found, layerPair] = findLayer(id); + if (found == LayerHistory::layerStatus::NotFound) { // Offscreen layer ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str()); return; } - const auto& info = it->second; + const auto& info = layerPair->second; const auto layerProps = LayerInfo::LayerProps{ .visible = layer->isVisible(), .bounds = layer->getBounds(), @@ -131,9 +127,10 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now, info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps); // Activate layer if inactive. - if (const auto end = activeLayers().end(); it >= end) { - std::iter_swap(it, end); - mActiveLayersEnd++; + if (found == LayerHistory::layerStatus::LayerInInactiveMap) { + mActiveLayerInfos.insert( + {id, std::make_pair(layerPair->first, std::move(layerPair->second))}); + mInactiveLayerInfos.erase(id); } } @@ -145,7 +142,8 @@ LayerHistory::Summary LayerHistory::summarize(const RefreshRateConfigs& refreshR partitionLayers(now); - for (const auto& [layer, info] : activeLayers()) { + for (const auto& [key, value] : mActiveLayerInfos) { + auto& info = value.second; const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority(); const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority); ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority, @@ -179,12 +177,29 @@ LayerHistory::Summary LayerHistory::summarize(const RefreshRateConfigs& refreshR void LayerHistory::partitionLayers(nsecs_t now) { const nsecs_t threshold = getActiveLayerThreshold(now); - // Collect expired and inactive layers after active layers. - size_t i = 0; - while (i < mActiveLayersEnd) { - auto& [layerUnsafe, info] = mLayerInfos[i]; + // iterate over inactive map + LayerInfos::iterator it = mInactiveLayerInfos.begin(); + while (it != mInactiveLayerInfos.end()) { + auto& [layerUnsafe, info] = it->second; + if (isLayerActive(*info, threshold)) { + // move this to the active map + + mActiveLayerInfos.insert({it->first, std::move(it->second)}); + it = mInactiveLayerInfos.erase(it); + } else { + if (CC_UNLIKELY(mTraceEnabled)) { + trace(*info, LayerHistory::LayerVoteType::NoVote, 0); + } + info->onLayerInactive(now); + it++; + } + } + + // iterate over active map + it = mActiveLayerInfos.begin(); + while (it != mActiveLayerInfos.end()) { + auto& [layerUnsafe, info] = it->second; if (isLayerActive(*info, threshold)) { - i++; // Set layer vote if set const auto frameRate = info->getSetFrameRateVote(); const auto voteType = [&]() { @@ -206,30 +221,68 @@ void LayerHistory::partitionLayers(nsecs_t now) { } else { info->resetLayerVote(); } - continue; - } - if (CC_UNLIKELY(mTraceEnabled)) { - trace(*info, LayerHistory::LayerVoteType::NoVote, 0); + it++; + } else { + if (CC_UNLIKELY(mTraceEnabled)) { + trace(*info, LayerHistory::LayerVoteType::NoVote, 0); + } + info->onLayerInactive(now); + // move this to the inactive map + mInactiveLayerInfos.insert({it->first, std::move(it->second)}); + it = mActiveLayerInfos.erase(it); } - - info->onLayerInactive(now); - std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]); } } void LayerHistory::clear() { std::lock_guard lock(mLock); - - for (const auto& [layer, info] : activeLayers()) { - info->clearHistory(systemTime()); + for (const auto& [key, value] : mActiveLayerInfos) { + value.second->clearHistory(systemTime()); } } std::string LayerHistory::dump() const { std::lock_guard lock(mLock); - return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(), - mActiveLayersEnd); + return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", + mActiveLayerInfos.size() + mInactiveLayerInfos.size(), + mActiveLayerInfos.size()); +} + +float LayerHistory::getLayerFramerate(nsecs_t now, int32_t id) const { + std::lock_guard lock(mLock); + auto [found, layerPair] = findLayer(id); + if (found != LayerHistory::layerStatus::NotFound) { + return layerPair->second->getFps(now).getValue(); + } + return 0.f; +} + +std::pair<LayerHistory::layerStatus, LayerHistory::LayerPair*> LayerHistory::findLayer(int32_t id) { + // the layer could be in either the active or inactive map, try both + auto it = mActiveLayerInfos.find(id); + if (it != mActiveLayerInfos.end()) { + return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second)); + } + it = mInactiveLayerInfos.find(id); + if (it != mInactiveLayerInfos.end()) { + return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second)); + } + return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr); +} + +std::pair<LayerHistory::layerStatus, const LayerHistory::LayerPair*> LayerHistory::findLayer( + int32_t id) const { + // the layer could be in either the active or inactive map, try both + auto it = mActiveLayerInfos.find(id); + if (it != mActiveLayerInfos.end()) { + return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second)); + } + it = mInactiveLayerInfos.find(id); + if (it != mInactiveLayerInfos.end()) { + return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second)); + } + return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr); } } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index 92236f560a..cc55700503 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -20,6 +20,7 @@ #include <utils/RefBase.h> #include <utils/Timers.h> +#include <map> #include <memory> #include <mutex> #include <string> @@ -31,11 +32,9 @@ namespace android { class Layer; -class TestableScheduler; namespace scheduler { -class LayerHistoryTest; class LayerInfo; class LayerHistory { @@ -74,34 +73,43 @@ public: void deregisterLayer(Layer*); std::string dump() const; + // return the frames per second of the layer with the given sequence id. + float getLayerFramerate(nsecs_t now, int32_t id) const; + private: - friend LayerHistoryTest; - friend TestableScheduler; + friend class LayerHistoryTest; + friend class TestableScheduler; using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>; - using LayerInfos = std::vector<LayerPair>; + // keyed by id as returned from Layer::getSequence() + using LayerInfos = std::unordered_map<int32_t, LayerPair>; - struct ActiveLayers { - LayerInfos& infos; - const size_t index; + // Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive + // layers to mInactiveLayerInfos. + // worst case time complexity is O(2 * inactive + active) + void partitionLayers(nsecs_t now) REQUIRES(mLock); - auto begin() { return infos.begin(); } - auto end() { return begin() + static_cast<long>(index); } + enum class layerStatus { + NotFound, + LayerInActiveMap, + LayerInInactiveMap, }; - ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; } - - // Iterates over layers in a single pass, swapping pairs such that active layers precede - // inactive layers, and inactive layers precede expired layers. Removes expired layers by - // truncating after inactive layers. - void partitionLayers(nsecs_t now) REQUIRES(mLock); + // looks up a layer by sequence id in both layerInfo maps. + // The first element indicates if and where the item was found + std::pair<layerStatus, LayerHistory::LayerPair*> findLayer(int32_t id) REQUIRES(mLock); + std::pair<layerStatus, const LayerHistory::LayerPair*> findLayer(int32_t id) const + REQUIRES(mLock); mutable std::mutex mLock; - // Partitioned such that active layers precede inactive layers. For fast lookup, the few active - // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache. - LayerInfos mLayerInfos GUARDED_BY(mLock); - size_t mActiveLayersEnd GUARDED_BY(mLock) = 0; + // Partitioned into two maps to facility two kinds of retrieval: + // 1. retrieval of a layer by id (attempt lookup in both maps) + // 2. retrieval of all active layers (iterate that map) + // The partitioning is allowed to become out of date but calling partitionLayers refreshes the + // validity of each map. + LayerInfos mActiveLayerInfos GUARDED_BY(mLock); + LayerInfos mInactiveLayerInfos GUARDED_BY(mLock); uint32_t mDisplayArea = 0; diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 314526a99d..943615c45c 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -28,6 +28,7 @@ #include <cutils/compiler.h> #include <cutils/trace.h> +#include <ftl/enum.h> #undef LOG_TAG #define LOG_TAG "LayerInfo" @@ -74,12 +75,16 @@ bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const { } bool LayerInfo::isFrequent(nsecs_t now) const { + using fps_approx_ops::operator>=; // If we know nothing about this layer we consider it as frequent as it might be the start // of an animation. if (mFrameTimes.size() < kFrequentLayerWindowSize) { return true; } + return getFps(now) >= kMinFpsForFrequentLayer; +} +Fps LayerInfo::getFps(nsecs_t now) const { // Find the first active frame auto it = mFrameTimes.begin(); for (; it != mFrameTimes.end(); ++it) { @@ -90,14 +95,12 @@ bool LayerInfo::isFrequent(nsecs_t now) const { const auto numFrames = std::distance(it, mFrameTimes.end()); if (numFrames < kFrequentLayerWindowSize) { - return false; + return Fps(); } - using fps_approx_ops::operator>=; - // Layer is considered frequent if the average frame rate is higher than the threshold const auto totalTime = mFrameTimes.back().queueTime - it->queueTime; - return Fps::fromPeriodNsecs(totalTime / (numFrames - 1)) >= kMinFpsForFrequentLayer; + return Fps::fromPeriodNsecs(totalTime / (numFrames - 1)); } bool LayerInfo::isAnimating(nsecs_t now) const { @@ -235,7 +238,7 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& ref if (!isFrequent(now)) { ALOGV("%s is infrequent", mName.c_str()); mLastRefreshRate.animatingOrInfrequent = true; - // Infrequent layers vote for minimal refresh rate for + // Infrequent layers vote for mininal refresh rate for // battery saving purposes and also to prevent b/135718869. return {LayerHistory::LayerVoteType::Min, Fps()}; } @@ -257,10 +260,10 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& ref return {LayerHistory::LayerVoteType::Max, Fps()}; } -const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const { +const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const { if (mTraceTags.count(type) == 0) { - const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type); - mTraceTags.emplace(type, tag); + auto tag = "LFPS " + mName + " " + ftl::enum_string(type); + mTraceTags.emplace(type, std::move(tag)); } return mTraceTags.at(type).c_str(); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 92abbae532..2d88a4fb9d 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -16,15 +16,19 @@ #pragma once +#include <chrono> +#include <deque> +#include <optional> +#include <string> +#include <unordered_map> + #include <ui/Transform.h> #include <utils/Timers.h> -#include <chrono> -#include <deque> +#include <scheduler/Seamlessness.h> #include "LayerHistory.h" #include "RefreshRateConfigs.h" -#include "Scheduler/Seamlessness.h" #include "SchedulerUtils.h" namespace android { @@ -78,6 +82,8 @@ public: NoVote, // Layer doesn't have any requirements for the refresh rate and // should not be considered when the display refresh rate is determined. + + ftl_last = NoVote }; // Encapsulates the frame rate and compatibility of the layer. This information will be used @@ -172,6 +178,9 @@ public: // Returns a C string for tracing a vote const char* getTraceTag(LayerHistory::LayerVoteType type) const; + // Return the framerate of this layer. + Fps getFps(nsecs_t now) const; + void onLayerInactive(nsecs_t now) { // Mark mFrameTimeValidSince to now to ignore all previous frame times. // We are not deleting the old frame to keep track of whether we should treat the first diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index dd69d60580..9532e26a9c 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -22,7 +22,7 @@ #include <utility> #include <android-base/thread_annotations.h> -#include <gui/IDisplayEventConnection.h> +#include <android/gui/IDisplayEventConnection.h> #include <private/gui/BitTube.h> #include <utils/Looper.h> #include <utils/Timers.h> diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index aabd88a435..71d563130e 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -21,23 +21,27 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wextra" -#include "RefreshRateConfigs.h" +#include <chrono> +#include <cmath> + #include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <ftl/enum.h> #include <utils/Trace.h> -#include <chrono> -#include <cmath> + #include "../SurfaceFlingerProperties.h" +#include "RefreshRateConfigs.h" #undef LOG_TAG #define LOG_TAG "RefreshRateConfigs" namespace android::scheduler { namespace { + std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) { return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(), - RefreshRateConfigs::layerVoteTypeString(layer.vote).c_str(), weight, - toString(layer.seamlessness).c_str(), + ftl::enum_string(layer.vote).c_str(), weight, + ftl::enum_string(layer.seamlessness).c_str(), to_string(layer.desiredRefreshRate).c_str()); } @@ -74,25 +78,6 @@ std::string RefreshRate::toString() const { mode->getWidth(), mode->getHeight(), getModeGroup()); } -std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) { - switch (vote) { - case LayerVoteType::NoVote: - return "NoVote"; - case LayerVoteType::Min: - return "Min"; - case LayerVoteType::Max: - return "Max"; - case LayerVoteType::Heuristic: - return "Heuristic"; - case LayerVoteType::ExplicitDefault: - return "ExplicitDefault"; - case LayerVoteType::ExplicitExactOrMultiple: - return "ExplicitExactOrMultiple"; - case LayerVoteType::ExplicitExact: - return "ExplicitExact"; - } -} - std::string RefreshRateConfigs::Policy::toString() const { return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d" ", primary range: %s, app request range: %s", @@ -405,7 +390,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( for (const auto& layer : layers) { ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(), - layerVoteTypeString(layer.vote).c_str(), layer.weight, + ftl::enum_string(layer.vote).c_str(), layer.weight, layer.desiredRefreshRate.getValue()); if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) { continue; @@ -744,7 +729,6 @@ void RefreshRateConfigs::initializeIdleTimer() { [getCallback] { if (const auto callback = getCallback()) callback->onExpired(); }); - mIdleTimer->start(); } } diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 53472efae9..4bbdab6bbe 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -16,31 +16,32 @@ #pragma once -#include <android-base/stringprintf.h> -#include <gui/DisplayEventReceiver.h> - #include <algorithm> #include <numeric> #include <optional> #include <type_traits> +#include <android-base/stringprintf.h> +#include <gui/DisplayEventReceiver.h> + +#include <scheduler/Fps.h> +#include <scheduler/Seamlessness.h> + #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/HWComposer.h" -#include "Fps.h" #include "Scheduler/OneShotTimer.h" #include "Scheduler/SchedulerUtils.h" -#include "Scheduler/Seamlessness.h" #include "Scheduler/StrongTyping.h" namespace android::scheduler { using namespace std::chrono_literals; -enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 }; +enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 }; -inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateConfigEvent rhs) { - using T = std::underlying_type_t<RefreshRateConfigEvent>; - return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs)); +inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) { + using T = std::underlying_type_t<DisplayModeEvent>; + return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs)); } using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; @@ -205,6 +206,7 @@ public: ExplicitExact, // Specific refresh rate that was provided by the app with // Exact compatibility + ftl_last = ExplicitExact }; // Captures the layer requirements for a refresh rate. This will be used to determine the @@ -284,9 +286,6 @@ public: // Stores the current modeId the device operates at void setCurrentModeId(DisplayModeId) EXCLUDES(mLock); - // Returns a string that represents the layer vote type - static std::string layerVoteTypeString(LayerVoteType vote); - // Returns a known frame rate that is the closest to frameRate Fps findClosestKnownFrameRate(Fps frameRate) const; @@ -355,10 +354,22 @@ public: std::function<void()> kernelTimerExpired) { std::scoped_lock lock(mIdleTimerCallbacksMutex); mIdleTimerCallbacks.emplace(); - mIdleTimerCallbacks->platform.onReset = platformTimerReset; - mIdleTimerCallbacks->platform.onExpired = platformTimerExpired; - mIdleTimerCallbacks->kernel.onReset = kernelTimerReset; - mIdleTimerCallbacks->kernel.onExpired = kernelTimerExpired; + mIdleTimerCallbacks->platform.onReset = std::move(platformTimerReset); + mIdleTimerCallbacks->platform.onExpired = std::move(platformTimerExpired); + mIdleTimerCallbacks->kernel.onReset = std::move(kernelTimerReset); + mIdleTimerCallbacks->kernel.onExpired = std::move(kernelTimerExpired); + } + + void startIdleTimer() { + if (mIdleTimer) { + mIdleTimer->start(); + } + } + + void stopIdleTimer() { + if (mIdleTimer) { + mIdleTimer->stop(); + } } void resetIdleTimer(bool kernelOnly) { diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h index 80aa96fd63..23ebb061e4 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -23,7 +23,8 @@ #include <ftl/small_map.h> #include <utils/Timers.h> -#include "Fps.h" +#include <scheduler/Fps.h> + #include "Scheduler/SchedulerUtils.h" #include "TimeStats/TimeStats.h" diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 4d72798086..cbe4552b0b 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -46,11 +46,8 @@ #include "OneShotTimer.h" #include "SchedulerUtils.h" #include "SurfaceFlingerProperties.h" -#include "Timer.h" -#include "VSyncDispatchTimerQueue.h" #include "VSyncPredictor.h" #include "VSyncReactor.h" -#include "VsyncController.h" #define RETURN_IF_INVALID_HANDLE(handle, ...) \ do { \ @@ -60,68 +57,14 @@ } \ } while (false) -using namespace std::string_literals; +namespace android::scheduler { -namespace android { - -using gui::WindowInfo; - -namespace { - -std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() { - // TODO(b/144707443): Tune constants. - constexpr int kDefaultRate = 60; - constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1); - constexpr nsecs_t idealPeriod = - std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count(); - constexpr size_t vsyncTimestampHistorySize = 20; - constexpr size_t minimumSamplesForPrediction = 6; - constexpr uint32_t discardOutlierPercent = 20; - return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize, - minimumSamplesForPrediction, - discardOutlierPercent); -} - -std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) { - // TODO(b/144707443): Tune constants. - constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms; - constexpr std::chrono::nanoseconds timerSlack = 500us; - return std::make_unique< - scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker, - timerSlack.count(), vsyncMoveThreshold.count()); -} - -const char* toContentDetectionString(bool useContentDetection) { - return useContentDetection ? "on" : "off"; -} - -} // namespace - -class PredictedVsyncTracer { -public: - PredictedVsyncTracer(scheduler::VSyncDispatch& dispatch) - : mRegistration(dispatch, std::bind(&PredictedVsyncTracer::callback, this), - "PredictedVsyncTracer") { - scheduleRegistration(); - } - -private: - TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0}; - scheduler::VSyncCallbackRegistration mRegistration; - - void scheduleRegistration() { mRegistration.schedule({0, 0, 0}); } - - void callback() { - mParity = !mParity; - scheduleRegistration(); - } -}; - -Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, Options options) - : impl::MessageQueue(compositor), mOptions(options), mSchedulerCallback(callback) {} +Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features) + : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {} void Scheduler::startTimers() { using namespace sysprop; + using namespace std::string_literals; if (const int64_t millis = set_touch_timer_ms(0); millis > 0) { // Touch events are coming to SF every 100ms, so the timer needs to be higher than that @@ -154,27 +97,14 @@ void Scheduler::run() { } } -void Scheduler::createVsyncSchedule(bool supportKernelTimer) { - auto clock = std::make_unique<scheduler::SystemClock>(); - auto tracker = createVSyncTracker(); - auto dispatch = createVSyncDispatch(*tracker); - - // TODO(b/144707443): Tune constants. - constexpr size_t pendingFenceLimit = 20; - auto controller = - std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit, - supportKernelTimer); - mVsyncSchedule = {std::move(controller), std::move(tracker), std::move(dispatch)}; - - if (base::GetBoolProperty("debug.sf.show_predicted_vsync", false)) { - mPredictedVsyncTracer = std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch); - } +void Scheduler::createVsyncSchedule(FeatureFlags features) { + mVsyncSchedule.emplace(features); } std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( const char* name, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, bool traceVsync) { - return std::make_unique<scheduler::DispSyncSource>(*mVsyncSchedule.dispatch, workDuration, + return std::make_unique<scheduler::DispSyncSource>(getVsyncDispatch(), workDuration, readyDuration, traceVsync, name); } @@ -210,7 +140,7 @@ bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const { return true; } - return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate); + return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp, *frameRate); } impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const { @@ -245,7 +175,7 @@ impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction( }; } -Scheduler::ConnectionHandle Scheduler::createConnection( +ConnectionHandle Scheduler::createConnection( const char* connectionName, frametimeline::TokenManager* tokenManager, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, impl::EventThread::InterceptVSyncsCallback interceptCallback) { @@ -259,7 +189,7 @@ Scheduler::ConnectionHandle Scheduler::createConnection( return createConnection(std::move(eventThread)); } -Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) { +ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) { const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++}; ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id); @@ -346,24 +276,24 @@ void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDis void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { { - std::lock_guard<std::mutex> lock(mFeatureStateLock); + std::lock_guard<std::mutex> lock(mPolicyLock); // Cache the last reported modes for primary display. - mFeatures.cachedModeChangedParams = {handle, mode}; + mPolicy.cachedModeChangedParams = {handle, mode}; // Invalidate content based refresh rate selection so it could be calculated // again for the new refresh rate. - mFeatures.contentRequirements.clear(); + mPolicy.contentRequirements.clear(); } onNonPrimaryDisplayModeChanged(handle, mode); } void Scheduler::dispatchCachedReportedMode() { // Check optional fields first. - if (!mFeatures.mode) { + if (!mPolicy.mode) { ALOGW("No mode ID found, not dispatching cached mode."); return; } - if (!mFeatures.cachedModeChangedParams.has_value()) { + if (!mPolicy.cachedModeChangedParams) { ALOGW("No mode changed params found, not dispatching cached mode."); return; } @@ -372,18 +302,18 @@ void Scheduler::dispatchCachedReportedMode() { // 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 (std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mFeatures.mode) { + mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mPolicy.mode) { return; } // If there is no change from cached mode, there is no need to dispatch an event - if (mFeatures.mode == mFeatures.cachedModeChangedParams->mode) { + if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) { return; } - mFeatures.cachedModeChangedParams->mode = mFeatures.mode; - onNonPrimaryDisplayModeChanged(mFeatures.cachedModeChangedParams->handle, - mFeatures.cachedModeChangedParams->mode); + mPolicy.cachedModeChangedParams->mode = mPolicy.mode; + onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle, + mPolicy.cachedModeChangedParams->mode); } void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { @@ -424,12 +354,12 @@ void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds wo } DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) { - const auto vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now); - const auto vsyncPeriod = mVsyncSchedule.tracker->currentPeriod(); + const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(now); + const auto vsyncPeriod = mVsyncSchedule->getTracker().currentPeriod(); return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod}; } -Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) { +ConnectionHandle Scheduler::enableVSyncInjection(bool enable) { if (mInjectVSyncs == enable) { return {}; } @@ -470,7 +400,7 @@ bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t dea void Scheduler::enableHardwareVsync() { std::lock_guard<std::mutex> lock(mHWVsyncLock); if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { - mVsyncSchedule.tracker->resetModel(); + mVsyncSchedule->getTracker().resetModel(); mSchedulerCallback.setVsyncEnabled(true); mPrimaryHWVsyncEnabled = true; } @@ -523,10 +453,10 @@ void Scheduler::resync() { void Scheduler::setVsyncPeriod(nsecs_t period) { std::lock_guard<std::mutex> lock(mHWVsyncLock); - mVsyncSchedule.controller->startPeriodTransition(period); + mVsyncSchedule->getController().startPeriodTransition(period); if (!mPrimaryHWVsyncEnabled) { - mVsyncSchedule.tracker->resetModel(); + mVsyncSchedule->getTracker().resetModel(); mSchedulerCallback.setVsyncEnabled(true); mPrimaryHWVsyncEnabled = true; } @@ -539,8 +469,9 @@ void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsy { // Scope for the lock std::lock_guard<std::mutex> lock(mHWVsyncLock); if (mPrimaryHWVsyncEnabled) { - needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod, - periodFlushed); + needsHwVsync = + mVsyncSchedule->getController().addHwVsyncTimestamp(timestamp, hwcVsyncPeriod, + periodFlushed); } } @@ -551,24 +482,23 @@ void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsy } } -void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { - if (mVsyncSchedule.controller->addPresentFence(fenceTime)) { +void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) { + if (mVsyncSchedule->getController().addPresentFence(std::move(fence))) { enableHardwareVsync(); } else { disableHardwareVsync(false); } } -void Scheduler::setIgnorePresentFences(bool ignore) { - mVsyncSchedule.controller->setIgnorePresentFences(ignore); -} - void Scheduler::registerLayer(Layer* layer) { + using WindowType = gui::WindowInfo::Type; + scheduler::LayerHistory::LayerVoteType voteType; - if (!mOptions.useContentDetection || layer->getWindowType() == WindowInfo::Type::STATUS_BAR) { + if (!mFeatures.test(Feature::kContentDetection) || + layer->getWindowType() == WindowType::STATUS_BAR) { voteType = scheduler::LayerHistory::LayerVoteType::NoVote; - } else if (layer->getWindowType() == WindowInfo::Type::WALLPAPER) { + } else if (layer->getWindowType() == WindowType::WALLPAPER) { // Running Wallpaper at Min is considered as part of content detection. voteType = scheduler::LayerHistory::LayerVoteType::Min; } else { @@ -615,13 +545,13 @@ void Scheduler::chooseRefreshRateForContent() { bool frameRateChanged; bool frameRateOverridesChanged; { - std::lock_guard<std::mutex> lock(mFeatureStateLock); - mFeatures.contentRequirements = summary; + std::lock_guard<std::mutex> lock(mPolicyLock); + mPolicy.contentRequirements = summary; newMode = calculateRefreshRateModeId(&consideredSignals); frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps()); - if (mFeatures.mode == newMode) { + if (mPolicy.mode == newMode) { // We don't need to change the display mode, but we might need to send an event // about a mode change, since it was suppressed due to a previous idleConsidered if (!consideredSignals.idle) { @@ -629,15 +559,16 @@ void Scheduler::chooseRefreshRateForContent() { } frameRateChanged = false; } else { - mFeatures.mode = newMode; + mPolicy.mode = newMode; frameRateChanged = true; } } if (frameRateChanged) { - auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId()); + const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId()); + mSchedulerCallback.changeRefreshRate(newRefreshRate, - consideredSignals.idle ? ModeEvent::None - : ModeEvent::Changed); + consideredSignals.idle ? DisplayModeEvent::None + : DisplayModeEvent::Changed); } if (frameRateOverridesChanged) { mSchedulerCallback.triggerOnFrameRateOverridesChanged(); @@ -660,8 +591,8 @@ void Scheduler::onTouchHint() { void Scheduler::setDisplayPowerState(bool normal) { { - std::lock_guard<std::mutex> lock(mFeatureStateLock); - mFeatures.isDisplayPowerStateNormal = normal; + std::lock_guard<std::mutex> lock(mPolicyLock); + mPolicy.isDisplayPowerStateNormal = normal; } if (mDisplayPowerTimer) { @@ -703,7 +634,7 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { } void Scheduler::idleTimerCallback(TimerState state) { - handleTimerStateChanged(&mFeatures.idleTimer, state); + handleTimerStateChanged(&mPolicy.idleTimer, state); ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state)); } @@ -713,14 +644,14 @@ void Scheduler::touchTimerCallback(TimerState state) { // Clear layer history to get fresh FPS detection. // NOTE: Instead of checking all the layers, we should be checking the layer // that is currently on top. b/142507166 will give us this capability. - if (handleTimerStateChanged(&mFeatures.touch, touch)) { + if (handleTimerStateChanged(&mPolicy.touch, touch)) { mLayerHistory.clear(); } ATRACE_INT("TouchState", static_cast<int>(touch)); } void Scheduler::displayPowerTimerCallback(TimerState state) { - handleTimerStateChanged(&mFeatures.displayPowerTimer, state); + handleTimerStateChanged(&mPolicy.displayPowerTimer, state); ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); } @@ -730,7 +661,7 @@ void Scheduler::dump(std::string& result) const { StringAppendF(&result, "+ Touch timer: %s\n", mTouchTimer ? mTouchTimer->dump().c_str() : "off"); StringAppendF(&result, "+ Content detection: %s %s\n\n", - toContentDetectionString(mOptions.useContentDetection), + mFeatures.test(Feature::kContentDetection) ? "on" : "off", mLayerHistory.dump().c_str()); { @@ -756,13 +687,8 @@ void Scheduler::dump(std::string& result) const { } } -void Scheduler::dumpVsync(std::string& s) const { - using base::StringAppendF; - - StringAppendF(&s, "VSyncReactor:\n"); - mVsyncSchedule.controller->dump(s); - StringAppendF(&s, "VSyncDispatch:\n"); - mVsyncSchedule.dispatch->dump(s); +void Scheduler::dumpVsync(std::string& out) const { + mVsyncSchedule->dump(out); } bool Scheduler::updateFrameRateOverrides( @@ -774,7 +700,7 @@ bool Scheduler::updateFrameRateOverrides( if (!consideredSignals.idle) { const auto frameRateOverrides = - refreshRateConfigs->getFrameRateOverrides(mFeatures.contentRequirements, + refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements, displayRefreshRate, consideredSignals); std::lock_guard lock(mFrameRateOverridesLock); if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(), @@ -797,31 +723,30 @@ bool Scheduler::handleTimerStateChanged(T* currentState, T newState) { scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; const auto refreshRateConfigs = holdRefreshRateConfigs(); { - std::lock_guard<std::mutex> lock(mFeatureStateLock); + std::lock_guard<std::mutex> lock(mPolicyLock); if (*currentState == newState) { return false; } *currentState = newState; newMode = calculateRefreshRateModeId(&consideredSignals); frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps()); - if (mFeatures.mode == newMode) { + if (mPolicy.mode == newMode) { // We don't need to change the display mode, but we might need to send an event // about a mode change, since it was suppressed due to a previous idleConsidered if (!consideredSignals.idle) { dispatchCachedReportedMode(); } } else { - mFeatures.mode = newMode; + mPolicy.mode = newMode; refreshRateChanged = true; } } if (refreshRateChanged) { - const RefreshRate& newRefreshRate = - refreshRateConfigs->getRefreshRateFromModeId(newMode->getId()); + const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId()); mSchedulerCallback.changeRefreshRate(newRefreshRate, - consideredSignals.idle ? ModeEvent::None - : ModeEvent::Changed); + consideredSignals.idle ? DisplayModeEvent::None + : DisplayModeEvent::Changed); } if (frameRateOverridesChanged) { mSchedulerCallback.triggerOnFrameRateOverridesChanged(); @@ -838,27 +763,26 @@ DisplayModePtr Scheduler::calculateRefreshRateModeId( // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. if (mDisplayPowerTimer && - (!mFeatures.isDisplayPowerStateNormal || - mFeatures.displayPowerTimer == TimerState::Reset)) { + (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) { return refreshRateConfigs->getMaxRefreshRateByPolicy().getMode(); } - const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active; - const bool idle = mFeatures.idleTimer == TimerState::Expired; + const bool touchActive = mTouchTimer && mPolicy.touch == TouchState::Active; + const bool idle = mPolicy.idleTimer == TimerState::Expired; return refreshRateConfigs - ->getBestRefreshRate(mFeatures.contentRequirements, - {.touch = touchActive, .idle = idle}, consideredSignals) + ->getBestRefreshRate(mPolicy.contentRequirements, {.touch = touchActive, .idle = idle}, + consideredSignals) .getMode(); } DisplayModePtr Scheduler::getPreferredDisplayMode() { - std::lock_guard<std::mutex> lock(mFeatureStateLock); + std::lock_guard<std::mutex> lock(mPolicyLock); // Make sure that the default mode ID is first updated, before returned. - if (mFeatures.mode) { - mFeatures.mode = calculateRefreshRateModeId(); + if (mPolicy.mode) { + mPolicy.mode = calculateRefreshRateModeId(); } - return mFeatures.mode; + return mPolicy.mode; } void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) { @@ -915,8 +839,8 @@ void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverrid std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom( nsecs_t expectedPresentTime) const { const auto presentTime = std::chrono::nanoseconds(expectedPresentTime); - const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule.tracker->currentPeriod()); + const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule->getTracker().currentPeriod()); return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod); } -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index f3c5b353e0..818f1edb41 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -31,40 +31,37 @@ #include <ui/GraphicTypes.h> #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" +#include <scheduler/Features.h> + #include "EventThread.h" #include "LayerHistory.h" #include "MessageQueue.h" #include "OneShotTimer.h" #include "RefreshRateConfigs.h" #include "SchedulerUtils.h" +#include "VsyncSchedule.h" namespace android { -using namespace std::chrono_literals; -using scheduler::LayerHistory; - class FenceTime; class InjectVSyncSource; -class PredictedVsyncTracer; - -namespace scheduler { -class VsyncController; -class VSyncDispatch; -class VSyncTracker; -} // namespace scheduler namespace frametimeline { class TokenManager; } // namespace frametimeline +namespace scheduler { + struct ISchedulerCallback { // Indicates frame activity, i.e. whether commit and/or composite is taking place. enum class FrameHint { kNone, kActive }; + using RefreshRate = RefreshRateConfigs::RefreshRate; + using DisplayModeEvent = scheduler::DisplayModeEvent; + virtual void scheduleComposite(FrameHint) = 0; virtual void setVsyncEnabled(bool) = 0; - virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&, - scheduler::RefreshRateConfigEvent) = 0; + virtual void changeRefreshRate(const RefreshRate&, DisplayModeEvent) = 0; virtual void kernelTimerChanged(bool expired) = 0; virtual void triggerOnFrameRateOverridesChanged() = 0; @@ -76,18 +73,10 @@ class Scheduler : impl::MessageQueue { using Impl = impl::MessageQueue; public: - using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; - using ModeEvent = scheduler::RefreshRateConfigEvent; - - struct Options { - // Whether to use content detection at all. - bool useContentDetection; - }; - - Scheduler(ICompositor&, ISchedulerCallback&, Options); + Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags); ~Scheduler(); - void createVsyncSchedule(bool supportKernelIdleTimer); + void createVsyncSchedule(FeatureFlags); void startTimers(); void run(); @@ -107,7 +96,6 @@ public: return std::move(future); } - using ConnectionHandle = scheduler::ConnectionHandle; ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, @@ -119,7 +107,7 @@ public: sp<EventThreadConnection> getEventConnection(ConnectionHandle); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); - void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mFeatureStateLock); + void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock); void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr); void onScreenAcquired(ConnectionHandle); void onScreenReleased(ConnectionHandle); @@ -152,8 +140,7 @@ public: // VsyncController detected that the vsync period changed, and false otherwise. void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, bool* periodFlushed); - void addPresentFence(const std::shared_ptr<FenceTime>&); - void setIgnorePresentFences(bool ignore); + void addPresentFence(std::shared_ptr<FenceTime>); // Layers are registered on creation, and unregistered when the weak reference expires. void registerLayer(Layer*); @@ -172,7 +159,7 @@ public: void setDisplayPowerState(bool normal); - scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; } + VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); } // Returns true if a given vsync timestamp is considered valid vsync // for a given uid @@ -211,19 +198,34 @@ public: std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateConfigsLock, mFrameRateOverridesLock); - void setRefreshRateConfigs(std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs) + void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> refreshRateConfigs) EXCLUDES(mRefreshRateConfigsLock) { - std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs = std::move(refreshRateConfigs); - mRefreshRateConfigs->setIdleTimerCallbacks( - [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Reset); }, - [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Expired); }, - [this] { - std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Reset); - }, - [this] { - std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Expired); - }); + // We need to stop the idle timer on the previous RefreshRateConfigs instance + // and cleanup the scheduler's state before we switch to the other RefreshRateConfigs. + { + std::scoped_lock lock(mRefreshRateConfigsLock); + if (mRefreshRateConfigs) mRefreshRateConfigs->stopIdleTimer(); + } + { + std::scoped_lock lock(mPolicyLock); + mPolicy = {}; + } + { + std::scoped_lock lock(mRefreshRateConfigsLock); + mRefreshRateConfigs = std::move(refreshRateConfigs); + mRefreshRateConfigs->setIdleTimerCallbacks( + [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Reset); }, + [this] { + std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Expired); + }, + [this] { + std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Reset); + }, + [this] { + std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Expired); + }); + mRefreshRateConfigs->startIdleTimer(); + } } nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) { @@ -231,23 +233,20 @@ public: return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); } + // Returns the framerate of the layer with the given sequence ID + float getLayerFramerate(nsecs_t now, int32_t id) const { + return mLayerHistory.getLayerFramerate(now, id); + } + private: friend class TestableScheduler; using FrameHint = ISchedulerCallback::FrameHint; - // In order to make sure that the features don't override themselves, we need a state machine - // to keep track which feature requested the config change. enum class ContentDetectionState { Off, On }; enum class TimerState { Reset, Expired }; enum class TouchState { Inactive, Active }; - struct VsyncSchedule { - std::unique_ptr<scheduler::VsyncController> controller; - std::unique_ptr<scheduler::VSyncTracker> tracker; - std::unique_ptr<scheduler::VSyncDispatch> dispatch; - }; - // Create a connection on the given EventThread. ConnectionHandle createConnection(std::unique_ptr<EventThread>); sp<EventThreadConnection> createConnectionInternal( @@ -269,19 +268,17 @@ private: // selection were initialized, prioritizes them, and calculates the DisplayModeId // for the suggested refresh rate. DisplayModePtr calculateRefreshRateModeId( - scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) - REQUIRES(mFeatureStateLock); + RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mPolicyLock); - void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock) EXCLUDES(mRefreshRateConfigsLock); - bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, - Fps displayRefreshRate) REQUIRES(mFeatureStateLock) - EXCLUDES(mFrameRateOverridesLock); + void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock); + bool updateFrameRateOverrides(RefreshRateConfigs::GlobalSignals, Fps displayRefreshRate) + REQUIRES(mPolicyLock) EXCLUDES(mFrameRateOverridesLock); impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const EXCLUDES(mRefreshRateConfigsLock); impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const; - std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const + std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) { std::scoped_lock lock(mRefreshRateConfigsLock); return mRefreshRateConfigs; @@ -307,66 +304,63 @@ private: std::atomic<nsecs_t> mLastResyncTime = 0; - const Options mOptions; - VsyncSchedule mVsyncSchedule; + const FeatureFlags mFeatures; + std::optional<VsyncSchedule> mVsyncSchedule; // Used to choose refresh rate if content detection is enabled. LayerHistory mLayerHistory; // Timer used to monitor touch events. - std::optional<scheduler::OneShotTimer> mTouchTimer; + std::optional<OneShotTimer> mTouchTimer; // Timer used to monitor display power mode. - std::optional<scheduler::OneShotTimer> mDisplayPowerTimer; + std::optional<OneShotTimer> mDisplayPowerTimer; ISchedulerCallback& mSchedulerCallback; - // In order to make sure that the features don't override themselves, we need a state machine - // to keep track which feature requested the config change. - mutable std::mutex mFeatureStateLock; + mutable std::mutex mPolicyLock; struct { + // Policy for choosing the display mode. + LayerHistory::Summary contentRequirements; TimerState idleTimer = TimerState::Reset; TouchState touch = TouchState::Inactive; TimerState displayPowerTimer = TimerState::Expired; + bool isDisplayPowerStateNormal = true; + // Chosen display mode. DisplayModePtr mode; - LayerHistory::Summary contentRequirements; - - bool isDisplayPowerStateNormal = true; - // Used to cache the last parameters of onPrimaryDisplayModeChanged struct ModeChangedParams { ConnectionHandle handle; DisplayModePtr mode; }; + // Parameters for latest dispatch of mode change event. std::optional<ModeChangedParams> cachedModeChangedParams; - } mFeatures GUARDED_BY(mFeatureStateLock); + } mPolicy GUARDED_BY(mPolicyLock); mutable std::mutex mRefreshRateConfigsLock; - std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs - GUARDED_BY(mRefreshRateConfigsLock); + std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock); std::mutex mVsyncTimelineLock; std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline GUARDED_BY(mVsyncTimelineLock); static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms; - std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer; - // The frame rate override lists need their own mutex as they are being read // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks mutable std::mutex mFrameRateOverridesLock; // mappings between a UID and a preferred refresh rate that this app would // run at. - scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent + RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent GUARDED_BY(mFrameRateOverridesLock); - scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor + RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor GUARDED_BY(mFrameRateOverridesLock); // Keeps track of whether the screen is acquired for debug std::atomic<bool> mScreenAcquired = false; }; +} // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp index c9c2d84a2a..22c3a709de 100644 --- a/services/surfaceflinger/Scheduler/Timer.cpp +++ b/services/surfaceflinger/Scheduler/Timer.cpp @@ -17,14 +17,18 @@ #undef LOG_TAG #define LOG_TAG "SchedulerTimer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <android-base/stringprintf.h> -#include <log/log.h> + +#include <chrono> +#include <cstdint> + #include <sys/epoll.h> #include <sys/timerfd.h> #include <sys/unistd.h> + +#include <android-base/stringprintf.h> +#include <ftl/enum.h> +#include <log/log.h> #include <utils/Trace.h> -#include <chrono> -#include <cstdint> #include "SchedulerUtils.h" #include "Timer.h" @@ -215,26 +219,9 @@ void Timer::setDebugState(DebugState state) { mDebugState = state; } -const char* Timer::strDebugState(DebugState state) const { - switch (state) { - case DebugState::Reset: - return "Reset"; - case DebugState::Running: - return "Running"; - case DebugState::Waiting: - return "Waiting"; - case DebugState::Reading: - return "Reading"; - case DebugState::InCallback: - return "InCallback"; - case DebugState::Terminated: - return "Terminated"; - } -} - void Timer::dump(std::string& result) const { std::lock_guard lock(mMutex); - StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState)); + StringAppendF(&result, "\t\tDebugState: %s\n", ftl::enum_string(mDebugState).c_str()); } } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h index 69ce079437..628d800bc7 100644 --- a/services/surfaceflinger/Scheduler/Timer.h +++ b/services/surfaceflinger/Scheduler/Timer.h @@ -37,11 +37,20 @@ public: void dump(std::string& result) const final; private: - enum class DebugState { Reset, Running, Waiting, Reading, InCallback, Terminated }; + enum class DebugState { + Reset, + Running, + Waiting, + Reading, + InCallback, + Terminated, + + ftl_last = Terminated + }; + void reset(); void cleanup(); void setDebugState(DebugState state) EXCLUDES(mMutex); - const char* strDebugState(DebugState state) const; int mTimerFd = -1; int mEpollFd = -1; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index ee973f718a..1c9de1c452 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -47,7 +47,7 @@ VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, VSyncReactor::~VSyncReactor() = default; -bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) { +bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) { if (!fence) { return false; } @@ -80,7 +80,7 @@ bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fe if (mPendingLimit == mUnfiredFences.size()) { mUnfiredFences.erase(mUnfiredFences.begin()); } - mUnfiredFences.push_back(fence); + mUnfiredFences.push_back(std::move(fence)); } else { timestampAccepted &= mTracker.addVsyncTimestamp(signalTime); } diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h index 449d4c3bee..a9d536be28 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.h +++ b/services/surfaceflinger/Scheduler/VSyncReactor.h @@ -37,7 +37,7 @@ public: bool supportKernelIdleTimer); ~VSyncReactor(); - bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final; + bool addPresentFence(std::shared_ptr<FenceTime>) final; void setIgnorePresentFences(bool ignore) final; void startPeriodTransition(nsecs_t period) final; diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 95750ad5cc..76315d2b5b 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -17,7 +17,9 @@ #pragma once #include <utils/Timers.h> -#include "Fps.h" + +#include <scheduler/Fps.h> + #include "VSyncDispatch.h" namespace android::scheduler { diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h index 844751270e..02ebd70272 100644 --- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h +++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h @@ -23,7 +23,8 @@ #include <ftl/small_map.h> #include <utils/Timers.h> -#include "Fps.h" +#include <scheduler/Fps.h> + #include "VsyncModulator.h" namespace android::scheduler { diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h index 0f0df222f4..59f65372a9 100644 --- a/services/surfaceflinger/Scheduler/VsyncController.h +++ b/services/surfaceflinger/Scheduler/VsyncController.h @@ -17,19 +17,15 @@ #pragma once #include <cstddef> +#include <memory> +#include <ui/FenceTime.h> #include <utils/Mutex.h> #include <utils/RefBase.h> #include <utils/Timers.h> -#include <ui/FenceTime.h> - -#include <memory> - namespace android::scheduler { -class FenceTime; - class VsyncController { public: virtual ~VsyncController(); @@ -43,7 +39,7 @@ public: * an accurate prediction, * False otherwise */ - virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0; + virtual bool addPresentFence(std::shared_ptr<FenceTime>) = 0; /* * Adds a hw sync timestamp to the model. The controller will use the timestamp diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp new file mode 100644 index 0000000000..77d1223aeb --- /dev/null +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <scheduler/Fps.h> + +#include "VsyncSchedule.h" + +#include "Timer.h" +#include "VSyncDispatchTimerQueue.h" +#include "VSyncPredictor.h" +#include "VSyncReactor.h" + +#include "../TracedOrdinal.h" + +namespace android::scheduler { + +class VsyncSchedule::PredictedVsyncTracer { + // Invoked from the thread of the VsyncDispatch owned by this VsyncSchedule. + constexpr auto makeVsyncCallback() { + return [this](nsecs_t, nsecs_t, nsecs_t) { + mParity = !mParity; + schedule(); + }; + } + +public: + explicit PredictedVsyncTracer(VsyncDispatch& dispatch) + : mRegistration(dispatch, makeVsyncCallback(), __func__) { + schedule(); + } + +private: + void schedule() { mRegistration.schedule({0, 0, 0}); } + + TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0}; + VSyncCallbackRegistration mRegistration; +}; + +VsyncSchedule::VsyncSchedule(FeatureFlags features) + : mTracker(createTracker()), + mDispatch(createDispatch(*mTracker)), + mController(createController(*mTracker, features)) { + if (features.test(Feature::kTracePredictedVsync)) { + mTracer = std::make_unique<PredictedVsyncTracer>(*mDispatch); + } +} + +VsyncSchedule::VsyncSchedule(TrackerPtr tracker, DispatchPtr dispatch, ControllerPtr controller) + : mTracker(std::move(tracker)), + mDispatch(std::move(dispatch)), + mController(std::move(controller)) {} + +VsyncSchedule::VsyncSchedule(VsyncSchedule&&) = default; +VsyncSchedule::~VsyncSchedule() = default; + +void VsyncSchedule::dump(std::string& out) const { + out.append("VsyncController:\n"); + mController->dump(out); + + out.append("VsyncDispatch:\n"); + mDispatch->dump(out); +} + +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker() { + // 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); +} + +VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(VsyncTracker& 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, + kGroupDispatchWithin.count(), + kSnapToSameVsyncWithin.count()); +} + +VsyncSchedule::ControllerPtr VsyncSchedule::createController(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, + kMaxPendingFences, hasKernelIdleTimer); + + reactor->setIgnorePresentFences(!features.test(Feature::kPresentFences)); + return reactor; +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h new file mode 100644 index 0000000000..0d9b114875 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -0,0 +1,75 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <string> + +#include <scheduler/Features.h> + +namespace android::scheduler { + +// TODO(b/185535769): Rename classes, and remove aliases. +class VSyncDispatch; +class VSyncTracker; + +class VsyncController; +using VsyncDispatch = VSyncDispatch; +using VsyncTracker = VSyncTracker; + +// Schedule that synchronizes to hardware VSYNC of a physical display. +class VsyncSchedule { +public: + explicit VsyncSchedule(FeatureFlags); + VsyncSchedule(VsyncSchedule&&); + ~VsyncSchedule(); + + // TODO(b/185535769): Hide behind API. + const VsyncTracker& getTracker() const { return *mTracker; } + VsyncTracker& getTracker() { return *mTracker; } + VsyncController& getController() { return *mController; } + + // TODO(b/185535769): Remove once VsyncSchedule owns all registrations. + VsyncDispatch& getDispatch() { return *mDispatch; } + + void dump(std::string&) const; + +private: + friend class TestableScheduler; + + using TrackerPtr = std::unique_ptr<VsyncTracker>; + using DispatchPtr = std::unique_ptr<VsyncDispatch>; + using ControllerPtr = std::unique_ptr<VsyncController>; + + // For tests. + VsyncSchedule(TrackerPtr, DispatchPtr, ControllerPtr); + + static TrackerPtr createTracker(); + static DispatchPtr createDispatch(VsyncTracker&); + static ControllerPtr createController(VsyncTracker&, FeatureFlags); + + class PredictedVsyncTracer; + using TracerPtr = std::unique_ptr<PredictedVsyncTracer>; + + // Effectively const except in move constructor. + TrackerPtr mTracker; + DispatchPtr mDispatch; + ControllerPtr mController; + TracerPtr mTracer; +}; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h new file mode 100644 index 0000000000..0e96678420 --- /dev/null +++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h @@ -0,0 +1,34 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <ftl/Flags.h> + +#include <cstdint> + +namespace android::scheduler { + +enum class Feature : std::uint8_t { + kPresentFences = 0b1, + kKernelIdleTimer = 0b10, + kContentDetection = 0b100, + kTracePredictedVsync = 0b1000, +}; + +using FeatureFlags = Flags<Feature>; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h index 639b3e5ed8..639b3e5ed8 100644 --- a/services/surfaceflinger/Fps.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h diff --git a/services/surfaceflinger/Scheduler/Seamlessness.h b/services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h index 3e42a4db37..93bf726731 100644 --- a/services/surfaceflinger/Scheduler/Seamlessness.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h @@ -16,11 +16,11 @@ #pragma once -#include <cstring> #include <ostream> -namespace android { -namespace scheduler { +#include <ftl/enum.h> + +namespace android::scheduler { // The seamlessness requirement of a Layer. enum class Seamlessness { @@ -31,24 +31,14 @@ enum class Seamlessness { // Indicates no preference for seamlessness. For such layers the system will // prefer seamless switches, but also non-seamless switches to the group of the // default config are allowed. - Default -}; + Default, -inline std::string toString(Seamlessness seamlessness) { - switch (seamlessness) { - case Seamlessness::OnlySeamless: - return "OnlySeamless"; - case Seamlessness::SeamedAndSeamless: - return "SeamedAndSeamless"; - case Seamlessness::Default: - return "Default"; - } -} + ftl_last = Default +}; // Used by gtest -inline std::ostream& operator<<(std::ostream& os, Seamlessness val) { - return os << toString(val); +inline std::ostream& operator<<(std::ostream& os, Seamlessness s) { + return os << ftl::enum_string(s); } -} // namespace scheduler -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 4c90a04dab..b7327b2faa 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -26,6 +26,7 @@ #include <android-base/properties.h> #include <android/configuration.h> +#include <android/gui/IDisplayEventConnection.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> @@ -51,7 +52,6 @@ #include <ftl/future.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> -#include <gui/IDisplayEventConnection.h> #include <gui/IProducerListener.h> #include <gui/LayerDebugInfo.h> #include <gui/LayerMetadata.h> @@ -93,6 +93,7 @@ #include <type_traits> #include <unordered_map> +#include "BackgroundExecutor.h" #include "BufferLayer.h" #include "BufferQueueLayer.h" #include "BufferStateLayer.h" @@ -169,6 +170,7 @@ using namespace android::sysprop; using android::hardware::power::Boost; using base::StringAppendF; using gui::DisplayInfo; +using gui::IDisplayEventConnection; using gui::IWindowInfosListener; using gui::WindowInfo; using ui::ColorMode; @@ -357,10 +359,11 @@ bool callingThreadHasRotateSurfaceFlingerAccess() { SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) : mFactory(factory), + mPid(getpid()), mInterceptor(mFactory.createSurfaceInterceptor()), mTimeStats(std::make_shared<impl::TimeStats>()), mFrameTracer(mFactory.createFrameTracer()), - mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())), + mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)), mCompositionEngine(mFactory.createCompositionEngine()), mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)), mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()), @@ -489,6 +492,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false)); enableLatchUnsignaledConfig = getLatchUnsignaledConfig(); + + mTransactionTracingEnabled = property_get_bool("debug.sf.enable_transaction_tracing", false); + if (mTransactionTracingEnabled) { + mTransactionTracing.enable(); + } } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { @@ -772,6 +780,25 @@ void SurfaceFlinger::deleteTextureAsync(uint32_t texture) { ATRACE_INT("TexturePoolSize", mTexturePool.size()); } +static std::optional<renderengine::RenderEngine::RenderEngineType> +chooseRenderEngineTypeViaSysProp() { + char prop[PROPERTY_VALUE_MAX]; + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); + + if (strcmp(prop, "gles") == 0) { + return renderengine::RenderEngine::RenderEngineType::GLES; + } else if (strcmp(prop, "threaded") == 0) { + return renderengine::RenderEngine::RenderEngineType::THREADED; + } else if (strcmp(prop, "skiagl") == 0) { + return renderengine::RenderEngine::RenderEngineType::SKIA_GL; + } else if (strcmp(prop, "skiaglthreaded") == 0) { + return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED; + } else { + ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop); + return {}; + } +} + // Do not call property_set on main thread which will be blocked by init // Use StartPropertySetThread instead. void SurfaceFlinger::init() { @@ -782,19 +809,21 @@ void SurfaceFlinger::init() { // Get a RenderEngine for the given display / config (can't fail) // TODO(b/77156734): We need to stop casting and use HAL types when possible. // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display. - mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create( - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat)) - .setImageCacheSize(maxFrameBufferAcquiredBuffers) - .setUseColorManagerment(useColorManagement) - .setEnableProtectedContext(enable_protected_contents(false)) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(mSupportsBlur) - .setContextPriority( - useContextPriority - ? renderengine::RenderEngine::ContextPriority::REALTIME - : renderengine::RenderEngine::ContextPriority::MEDIUM) - .build())); + auto builder = renderengine::RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat)) + .setImageCacheSize(maxFrameBufferAcquiredBuffers) + .setUseColorManagerment(useColorManagement) + .setEnableProtectedContext(enable_protected_contents(false)) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(mSupportsBlur) + .setContextPriority( + useContextPriority + ? renderengine::RenderEngine::ContextPriority::REALTIME + : renderengine::RenderEngine::ContextPriority::MEDIUM); + if (auto type = chooseRenderEngineTypeViaSysProp()) { + builder.setRenderEngineType(type.value()); + } + mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build())); mMaxRenderTargetSize = std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims()); @@ -1102,7 +1131,7 @@ void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) { } } -status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) { +status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) { ATRACE_CALL(); if (!displayToken) { @@ -1143,7 +1172,7 @@ status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int mode return future.get(); } -void SurfaceFlinger::setActiveModeInternal() { +void SurfaceFlinger::updateInternalStateWithChangedMode() { ATRACE_CALL(); const auto display = getDefaultDisplayDeviceLocked(); @@ -1179,7 +1208,7 @@ void SurfaceFlinger::setActiveModeInternal() { mRefreshRateStats->setRefreshRate(refreshRate); updatePhaseConfiguration(refreshRate); - if (upcomingModeInfo.event != Scheduler::ModeEvent::None) { + if (upcomingModeInfo.event != DisplayModeEvent::None) { mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode); } } @@ -1198,9 +1227,10 @@ void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& displa updatePhaseConfiguration(refreshRate); } -void SurfaceFlinger::performSetActiveMode() { +void SurfaceFlinger::setActiveModeInHwcIfNeeded() { ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); + + std::optional<PhysicalDisplayId> displayToUpdateImmediately; for (const auto& iter : mDisplays) { const auto& display = iter.second; @@ -1235,8 +1265,7 @@ void SurfaceFlinger::performSetActiveMode() { to_string(display->getId()).c_str()); if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) { - // display is not valid or we are already in the requested mode - // on both cases there is nothing left to do + // we are already in the requested mode, there is nothing left to do desiredActiveModeChangeDone(display); continue; } @@ -1247,7 +1276,7 @@ void SurfaceFlinger::performSetActiveMode() { const auto displayModeAllowed = display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId()); if (!displayModeAllowed) { - desiredActiveModeChangeDone(display); + clearDesiredActiveModeState(display); continue; } @@ -1267,8 +1296,26 @@ void SurfaceFlinger::performSetActiveMode() { } mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline); - // Scheduler will submit an empty frame to HWC if needed. - mSetActiveModePending = true; + if (outTimeline.refreshRequired) { + // Scheduler will submit an empty frame to HWC. + mSetActiveModePending = true; + } else { + // Updating the internal state should be done outside the loop, + // because it can recreate a DisplayDevice and modify mDisplays + // which will invalidate the iterator. + displayToUpdateImmediately = display->getPhysicalId(); + } + } + + if (displayToUpdateImmediately) { + updateInternalStateWithChangedMode(); + + const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately); + const auto desiredActiveMode = display->getDesiredActiveMode(); + if (desiredActiveMode && + display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) { + desiredActiveModeChangeDone(display); + } } } @@ -1935,7 +1982,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected // We received the present fence from the HWC, so we assume it successfully updated // the mode, hence we update SF. mSetActiveModePending = false; - ON_MAIN_THREAD(setActiveModeInternal()); + ON_MAIN_THREAD(updateInternalStateWithChangedMode()); } if (framePending) { @@ -1946,7 +1993,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected } if (mTracingEnabledChanged) { - mTracingEnabled = mLayerTracing.isEnabled(); + mLayerTracingEnabled = mLayerTracing.isEnabled(); mTracingEnabledChanged = false; } @@ -1964,7 +2011,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected bool needsTraversal = false; if (clearTransactionFlags(eTransactionFlushNeeded)) { - needsTraversal = flushTransactionQueues(); + needsTraversal = flushTransactionQueues(vsyncId); } const bool shouldCommit = @@ -2002,7 +2049,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected mScheduler->chooseRefreshRateForContent(); } - ON_MAIN_THREAD(performSetActiveMode()); + ON_MAIN_THREAD(setActiveModeInHwcIfNeeded()); updateCursorAsync(); updateInputFlinger(); @@ -2095,7 +2142,7 @@ void SurfaceFlinger::composite(nsecs_t frameTime) { modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition); mLayersWithQueuedFrames.clear(); - if (mTracingEnabled) { + if (mLayerTracingEnabled) { // This will block and should only be used for debugging. if (mVisibleRegionsDirty) { mLayerTracing.notify("visibleRegionsDirty"); @@ -3008,28 +3055,45 @@ void SurfaceFlinger::updateInputFlinger() { return; } + std::vector<WindowInfo> windowInfos; + std::vector<DisplayInfo> displayInfos; + bool updateWindowInfo = false; if (mVisibleRegionsDirty || mInputInfoChanged) { mInputInfoChanged = false; - notifyWindowInfos(); - } else if (mInputWindowCommands.syncInputWindows) { - // If the caller requested to sync input windows, but there are no - // changes to input windows, notify immediately. - windowInfosReported(); + updateWindowInfo = true; + buildWindowInfos(windowInfos, displayInfos); } - - for (const auto& focusRequest : mInputWindowCommands.focusRequests) { - mInputFlinger->setFocusedWindow(focusRequest); + if (!updateWindowInfo && mInputWindowCommands.empty()) { + return; } + BackgroundExecutor::getInstance().execute([updateWindowInfo, + windowInfos = std::move(windowInfos), + displayInfos = std::move(displayInfos), + inputWindowCommands = + std::move(mInputWindowCommands), + inputFlinger = mInputFlinger, this]() { + ATRACE_NAME("BackgroundExecutor::updateInputFlinger"); + if (updateWindowInfo) { + mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos, + inputWindowCommands.syncInputWindows); + } else if (inputWindowCommands.syncInputWindows) { + // If the caller requested to sync input windows, but there are no + // changes to input windows, notify immediately. + windowInfosReported(); + } + for (const auto& focusRequest : inputWindowCommands.focusRequests) { + inputFlinger->setFocusedWindow(focusRequest); + } + }); + mInputWindowCommands.clear(); } -void SurfaceFlinger::notifyWindowInfos() { - std::vector<WindowInfo> windowInfos; - std::vector<DisplayInfo> displayInfos; +void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, + std::vector<DisplayInfo>& outDisplayInfos) { std::unordered_map<uint32_t /*layerStackId*/, std::pair<bool /* isSecure */, const ui::Transform>> inputDisplayDetails; - for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) { if (!display->receivesInput()) { continue; @@ -3043,7 +3107,7 @@ void SurfaceFlinger::notifyWindowInfos() { layerStackId); continue; } - displayInfos.emplace_back(info); + outDisplayInfos.emplace_back(info); } mDrawingState.traverseInReverseZOrder([&](Layer* layer) { @@ -3063,10 +3127,8 @@ void SurfaceFlinger::notifyWindowInfos() { layer->getDebugName(), layerStackId); } - windowInfos.push_back(layer->fillInputInfo(displayTransform, isSecure)); + outWindowInfos.push_back(layer->fillInputInfo(displayTransform, isSecure)); }); - mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos, - mInputWindowCommands.syncInputWindows); } void SurfaceFlinger::updateCursorAsync() { @@ -3080,7 +3142,7 @@ void SurfaceFlinger::updateCursorAsync() { mCompositionEngine->updateCursorAsync(refreshArgs); } -void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, Scheduler::ModeEvent event) { +void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, DisplayModeEvent event) { // If this is called from the main thread mStateLock must be locked before // Currently the only way to call this function from the main thread is from // Scheduler::chooseRefreshRateForContent @@ -3128,17 +3190,32 @@ void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) { mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate); mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs()); - const Scheduler::Options options = { - .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}; + using Feature = scheduler::Feature; + scheduler::FeatureFlags features; + + if (sysprop::use_content_detection_for_refresh_rate(false)) { + features |= Feature::kContentDetection; + } + if (base::GetBoolProperty("debug.sf.show_predicted_vsync"s, false)) { + features |= Feature::kTracePredictedVsync; + } + if (!base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false) && + !getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) { + features |= Feature::kPresentFences; + } - mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), - static_cast<ISchedulerCallback&>(*this), options); + mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this), + static_cast<ISchedulerCallback&>(*this), + features); { auto configs = display->holdRefreshRateConfigs(); - mScheduler->createVsyncSchedule(configs->supportsKernelIdleTimer()); + if (configs->supportsKernelIdleTimer()) { + features |= Feature::kKernelIdleTimer; + } + + mScheduler->createVsyncSchedule(features); mScheduler->setRefreshRateConfigs(std::move(configs)); } - setVsyncEnabled(false); mScheduler->startTimers(); @@ -3171,11 +3248,6 @@ void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) { // classes from EventThread, and there should be no run-time binder cost // anyway since there are no connected apps at this point. mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode()); - static auto ignorePresentFences = - base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false); - mScheduler->setIgnorePresentFences( - ignorePresentFences || - getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE)); } void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) { @@ -3374,10 +3446,11 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind client->attachLayer(handle, lbc); } + int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++; return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr, InputWindowCommands{}, -1 /* desiredPresentTime */, true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {}, - 0 /* Undefined transactionId */); + transactionId); } uint32_t SurfaceFlinger::getTransactionFlags() const { @@ -3400,7 +3473,7 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule return old; } -bool SurfaceFlinger::flushTransactionQueues() { +bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { // to prevent onHandleDestroyed from being called while the lock is held, // we must keep a copy of the transactions (specifically the composer // states) around outside the scope of the lock @@ -3486,12 +3559,13 @@ bool SurfaceFlinger::flushTransactionQueues() { ATRACE_INT("TransactionQueue", mTransactionQueue.size()); } - return applyTransactions(transactions); + return applyTransactions(transactions, vsyncId); } } } -bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) { +bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions, + int64_t vsyncId) { bool needsTraversal = false; // Now apply all transactions. for (const auto& transaction : transactions) { @@ -3509,6 +3583,10 @@ bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactio std::move(transaction.transactionCommittedSignal)); } } + + if (mTransactionTracingEnabled) { + mTransactionTracing.addCommittedTransactions(transactions, vsyncId); + } return needsTraversal; } @@ -3762,6 +3840,10 @@ status_t SurfaceFlinger::setTransactionState( state.traverseStatesWithBuffers([&](const layer_state_t& state) { mBufferCountTracker.increment(state.surface->localBinder()); }); + + if (mTransactionTracingEnabled) { + mTransactionTracing.addQueuedTransaction(state); + } queueTransaction(state); // Check the pending state to make sure the transaction is synchronous. @@ -3799,10 +3881,11 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp, postTime, permissions); if ((flags & eAnimation) && state.state.surface) { - if (const auto layer = fromHandle(state.state.surface).promote(); layer) { + if (const auto layer = fromHandle(state.state.surface).promote()) { + using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; mScheduler->recordLayerHistory(layer.get(), isAutoTimestamp ? 0 : desiredPresentTime, - LayerHistory::LayerUpdateType::AnimationTX); + LayerUpdateType::AnimationTX); } } } @@ -4139,13 +4222,14 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime std::optional<nsecs_t> dequeueBufferTimestamp; if (what & layer_state_t::eMetadataChanged) { dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME); - auto gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1); - if (gameMode != -1) { + + if (const int32_t gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1); gameMode != -1) { // The transaction will be received on the Task layer and needs to be applied to all // child layers. Child layers that are added at a later point will obtain the game mode // info through addChild(). - layer->setGameModeForTree(gameMode); + layer->setGameModeForTree(static_cast<GameMode>(gameMode)); } + if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded; } if (what & layer_state_t::eColorSpaceAgnosticChanged) { @@ -4339,6 +4423,16 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHa return result; } + int parentId = -1; + // We can safely promote the layer in binder thread because we have a strong reference + // to the layer's handle inside this scope or we were passed in a sp reference to the layer. + sp<Layer> parentSp = parent.promote(); + if (parentSp != nullptr) { + parentId = parentSp->getSequence(); + } + mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name, + args.flags, parentId); + setTransactionFlags(eTransactionNeeded); *outLayerId = layer->sequence; return result; @@ -4448,10 +4542,12 @@ void SurfaceFlinger::onInitializeDisplays() { displays.add(d); nsecs_t now = systemTime(); + + 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, - {}, getpid(), getuid(), 0 /* Undefined transactionId */); + {}, mPid, getuid(), transactionId); setPowerModeInternal(display, hal::PowerMode::ON); const nsecs_t vsyncPeriod = @@ -4650,8 +4746,9 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { } status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) { - if (asProto && mLayerTracing.isEnabled()) { + if (asProto) { mLayerTracing.writeToFile(); + mTransactionTracing.writeToFile(); } return doDump(fd, DumpArgs(), asProto); @@ -4848,7 +4945,6 @@ void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { } LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { - // If context is SurfaceTracing thread, mTracingLock blocks display transactions on main thread. const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); LayersProto layersProto; @@ -4872,6 +4968,7 @@ void SurfaceFlinger::dumpDisplayProto(LayersTraceProto& layersTraceProto) const }); LayerProtoHelper::writeTransformToProto(display->getTransform(), displayProto->mutable_transform()); + displayProto->set_is_virtual(display->isVirtual()); } } @@ -5049,6 +5146,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co */ mLayerTracing.dump(result); result.append("\n"); + mTransactionTracing.dump(result); + result.append("\n"); /* * HWC layer minidump @@ -5281,9 +5380,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { code == IBinder::SYSPROPS_TRANSACTION) { return OK; } - // Numbers from 1000 to 1040 are currently used for backdoors. The code + // Numbers from 1000 to 1041 are currently used for backdoors. The code // in onTransact verifies that the user is root, and has access to use SF. - if (code >= 1000 && code <= 1040) { + if (code >= 1000 && code <= 1041) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -5611,7 +5710,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r }(); mDebugDisplayModeSetByBackdoor = false; - const status_t result = setActiveMode(display, modeId); + const status_t result = setActiveModeFromBackdoor(display, modeId); mDebugDisplayModeSetByBackdoor = result == NO_ERROR; return result; } @@ -5722,6 +5821,20 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r scheduleRepaint(); return NO_ERROR; } + case 1041: { // Transaction tracing + if (data.readInt32()) { + // Transaction tracing is always running but allow the user to temporarily + // increase the buffer when actively debugging. + mTransactionTracing.setBufferSize( + TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE); + } else { + mTransactionTracing.setBufferSize( + TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE); + mTransactionTracing.writeToFile(); + } + reply->writeInt32(NO_ERROR); + return NO_ERROR; + } } } return err; @@ -6459,7 +6572,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) { ALOGV("switching to Scheduler preferred display mode %d", preferredDisplayMode->getId().value()); - setDesiredActiveMode({preferredDisplayMode, Scheduler::ModeEvent::Changed}); + setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed}); } else { LOG_ALWAYS_FATAL("Desired display mode not allowed: %d", preferredDisplayMode->getId().value()); @@ -6556,6 +6669,7 @@ void SurfaceFlinger::onLayerDestroyed(Layer* layer) { if (!layer->isRemovedFromCurrentState()) { mScheduler->deregisterLayer(layer); } + mTransactionTracing.onLayerRemoved(layer->getSequence()); } void SurfaceFlinger::onLayerUpdate() { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 6093be91f9..17dfef9d32 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -23,7 +23,6 @@ */ #include <android-base/thread_annotations.h> -#include <compositionengine/OutputColorSetting.h> #include <cutils/atomic.h> #include <cutils/compiler.h> #include <gui/BufferQueue.h> @@ -47,13 +46,15 @@ #include <utils/Trace.h> #include <utils/threads.h> +#include <compositionengine/OutputColorSetting.h> +#include <scheduler/Fps.h> + #include "ClientCache.h" #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/PowerAdvisor.h" #include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" -#include "Fps.h" #include "FrameTracker.h" #include "LayerVector.h" #include "Scheduler/RefreshRateConfigs.h" @@ -63,6 +64,7 @@ #include "SurfaceFlingerFactory.h" #include "TracedOrdinal.h" #include "Tracing/LayerTracing.h" +#include "Tracing/TransactionTracing.h" #include "TransactionCallbackInvoker.h" #include "TransactionState.h" @@ -104,6 +106,7 @@ class TimeStats; class FrameTracer; class WindowInfosListenerInvoker; +using gui::IRegionSamplingListener; using gui::ScreenCaptureResults; namespace frametimeline { @@ -164,7 +167,7 @@ class SurfaceFlinger : public BnSurfaceComposer, private IBinder::DeathRecipient, private HWC2::ComposerCallback, private ICompositor, - private ISchedulerCallback { + private scheduler::ISchedulerCallback { public: struct SkipInitializationTag {}; @@ -355,7 +358,6 @@ private: friend class TransactionApplicationTest; friend class TunnelModeEnabledReporterTest; - using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; using VsyncModulator = scheduler::VsyncModulator; using TransactionSchedule = scheduler::TransactionSchedule; using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>; @@ -642,7 +644,7 @@ private: // Toggles hardware VSYNC by calling into HWC. void setVsyncEnabled(bool) override; // Initiates a refresh rate change to be applied on commit. - void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override; + void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override; // Called when kernel idle timer has expired. Used to update the refresh rate overlay. void kernelTimerChanged(bool expired) override; // Called when the frame rate override list changed to trigger an event. @@ -659,13 +661,12 @@ private: void onInitializeDisplays() REQUIRES(mStateLock); // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode. void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock); - status_t setActiveMode(const sp<IBinder>& displayToken, int id); - // Once HWC has returned the present fence, this sets the active mode and a new refresh - // rate in SF. - void setActiveModeInternal() REQUIRES(mStateLock); + status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id); + // Sets the active mode and a new refresh rate in SF. + void updateInternalStateWithChangedMode() REQUIRES(mStateLock); // Calls to setActiveMode on the main thread if there is a pending mode change // that needs to be applied. - void performSetActiveMode() REQUIRES(mStateLock); + void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock); void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock); // Called when active mode is no longer is progress void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock); @@ -689,7 +690,8 @@ private: void updateLayerGeometry(); void updateInputFlinger(); - void notifyWindowInfos(); + void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos, + std::vector<gui::DisplayInfo>& outDisplayInfos); void commitInputWindowCommands() REQUIRES(mStateLock); void updateCursorAsync(); @@ -711,7 +713,7 @@ private: int originPid, int originUid, uint64_t transactionId) REQUIRES(mStateLock); // flush pending transaction that was presented after desiredPresentTime. - bool flushTransactionQueues(); + bool flushTransactionQueues(int64_t vsyncId); // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); @@ -747,7 +749,8 @@ private: bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock); bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction) REQUIRES(mStateLock); - bool applyTransactions(std::vector<TransactionState>& transactions) REQUIRES(mStateLock); + bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId) + REQUIRES(mStateLock); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) REQUIRES(mStateLock); @@ -1082,7 +1085,7 @@ private: sp<StartPropertySetThread> mStartPropertySetThread; surfaceflinger::Factory& mFactory; - + pid_t mPid; std::future<void> mRenderEnginePrimeCacheFuture; // access must be protected by mStateLock @@ -1091,6 +1094,7 @@ private: std::atomic<int32_t> mTransactionFlags = 0; std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals; bool mAnimTransactionPending = false; + std::atomic<uint32_t> mUniqueTransactionId = 1; SortedVector<sp<Layer>> mLayersPendingRemoval; // global color transform states @@ -1181,8 +1185,10 @@ private: sp<SurfaceInterceptor> mInterceptor; LayerTracing mLayerTracing{*this}; - std::mutex mTracingLock; - bool mTracingEnabled = false; + bool mLayerTracingEnabled = false; + + TransactionTracing mTransactionTracing; + bool mTransactionTracingEnabled = false; std::atomic<bool> mTracingEnabledChanged = false; const std::shared_ptr<TimeStats> mTimeStats; @@ -1260,7 +1266,7 @@ private: /* * Scheduler */ - std::unique_ptr<Scheduler> mScheduler; + std::unique_ptr<scheduler::Scheduler> mScheduler; scheduler::ConnectionHandle mAppConnectionHandle; scheduler::ConnectionHandle mSfConnectionHandle; @@ -1289,8 +1295,8 @@ private: const float mInternalDisplayDensity; const float mEmulatedDisplayDensity; - sp<os::IInputFlinger> mInputFlinger; // Should only be accessed by the main thread. + sp<os::IInputFlinger> mInputFlinger; InputWindowCommands mInputWindowCommands; Hwc2::impl::PowerAdvisor mPowerAdvisor; @@ -1346,6 +1352,11 @@ private: const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker; std::unique_ptr<FlagManager> mFlagManager; + + // returns the framerate of the layer with the given sequence ID + float getLayerFramerate(nsecs_t now, int32_t id) const { + return mScheduler->getLayerFramerate(now, id); + } }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h index e670f37189..6153e8e354 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -16,16 +16,16 @@ #pragma once -#include "Fps.h" - -#include <cutils/compiler.h> -#include <utils/StrongPointer.h> - #include <cinttypes> #include <functional> #include <memory> #include <string> +#include <cutils/compiler.h> +#include <utils/StrongPointer.h> + +#include <scheduler/Fps.h> + namespace android { typedef int32_t PixelFormat; @@ -42,16 +42,12 @@ class HWComposer; class IGraphicBufferConsumer; class IGraphicBufferProducer; class Layer; -class MessageQueue; -class Scheduler; class StartPropertySetThread; class SurfaceFlinger; class SurfaceInterceptor; class TimeStats; struct DisplayDeviceCreationArgs; -struct ICompositor; -struct ISchedulerCallback; struct LayerCreationArgs; namespace compositionengine { diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp index bcc3e4e52a..4686eed54c 100644 --- a/services/surfaceflinger/TimeStats/Android.bp +++ b/services/surfaceflinger/TimeStats/Android.bp @@ -12,6 +12,9 @@ cc_library { srcs: [ "TimeStats.cpp", ], + header_libs: [ + "libscheduler_headers", + ], shared_libs: [ "android.hardware.graphics.composer@2.4", "libbase", @@ -24,6 +27,9 @@ cc_library { "libutils", ], export_include_dirs: ["."], + export_header_lib_headers: [ + "libscheduler_headers", + ], export_shared_lib_headers: [ "libtimestats_proto", ], diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index bf2038b277..b1a2bdaa91 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <unordered_map> #undef LOG_TAG #define LOG_TAG "TimeStats" #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -28,6 +27,7 @@ #include <algorithm> #include <chrono> +#include <unordered_map> #include "TimeStats.h" #include "timestatsproto/TimeStatsHelper.h" @@ -58,15 +58,15 @@ FrameTimingHistogram histogramToProto(const std::unordered_map<int32_t, int32_t> return histogramProto; } -SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(int32_t gameMode) { +SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(GameMode gameMode) { switch (gameMode) { - case TimeStatsHelper::GameModeUnsupported: + case GameMode::Unsupported: return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSUPPORTED; - case TimeStatsHelper::GameModeStandard: + case GameMode::Standard: return SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD; - case TimeStatsHelper::GameModePerformance: + case GameMode::Performance: return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE; - case TimeStatsHelper::GameModeBattery: + case GameMode::Battery: return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY; default: return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED; @@ -454,7 +454,7 @@ static int32_t clampToNearestBucket(Fps fps, size_t bucketWidth) { void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate, std::optional<Fps> renderRate, SetFrameRateVote frameRateVote, - int32_t gameMode) { + GameMode gameMode) { ATRACE_CALL(); ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId); @@ -554,7 +554,7 @@ static bool layerNameIsValid(const std::string& layerName) { } bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName, - int32_t gameMode) { + GameMode gameMode) { uint32_t layerRecords = 0; for (const auto& record : mTimeStats.stats) { if (record.second.stats.count({uid, layerName, gameMode}) > 0) { @@ -568,7 +568,7 @@ bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName } void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, - uid_t uid, nsecs_t postTime, int32_t gameMode) { + uid_t uid, nsecs_t postTime, GameMode gameMode) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -718,7 +718,7 @@ void TimeStats::setAcquireFence(int32_t layerId, uint64_t frameNumber, void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, Fps displayRefreshRate, std::optional<Fps> renderRate, - SetFrameRateVote frameRateVote, int32_t gameMode) { + SetFrameRateVote frameRateVote, GameMode gameMode) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -744,7 +744,7 @@ void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t pr void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate, std::optional<Fps> renderRate, - SetFrameRateVote frameRateVote, int32_t gameMode) { + SetFrameRateVote frameRateVote, GameMode gameMode) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -823,7 +823,7 @@ void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) { // the first jank record is not dropped. static const std::string kDefaultLayerName = "none"; - static constexpr int32_t kDefaultGameMode = TimeStatsHelper::GameModeUnsupported; + constexpr GameMode kDefaultGameMode = GameMode::Unsupported; const int32_t refreshRateBucket = clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH); diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index bdeaeb8468..77c7973532 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -17,21 +17,22 @@ #pragma once #include <cstdint> +#include <deque> +#include <mutex> +#include <optional> +#include <unordered_map> +#include <variant> -#include <../Fps.h> #include <android/hardware/graphics/composer/2.4/IComposerClient.h> #include <gui/JankInfo.h> +#include <gui/LayerMetadata.h> #include <timestatsproto/TimeStatsHelper.h> #include <timestatsproto/TimeStatsProtoHeader.h> #include <ui/FenceTime.h> #include <utils/String16.h> #include <utils/Vector.h> -#include <deque> -#include <mutex> -#include <optional> -#include <unordered_map> -#include <variant> +#include <scheduler/Fps.h> using namespace android::surfaceflinger; @@ -79,7 +80,7 @@ public: const std::shared_ptr<FenceTime>& readyFence) = 0; virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, - uid_t uid, nsecs_t postTime, int32_t gameMode) = 0; + uid_t uid, nsecs_t postTime, GameMode) = 0; virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0; // Reasons why latching a particular buffer may be skipped enum class LatchSkipReason { @@ -101,11 +102,11 @@ public: // rendering path, as they flush prior fences if those fences have fired. virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, Fps displayRefreshRate, std::optional<Fps> renderRate, - SetFrameRateVote frameRateVote, int32_t gameMode) = 0; + SetFrameRateVote frameRateVote, GameMode) = 0; virtual void setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate, std::optional<Fps> renderRate, - SetFrameRateVote frameRateVote, int32_t gameMode) = 0; + SetFrameRateVote frameRateVote, GameMode) = 0; // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName} // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the @@ -123,7 +124,7 @@ public: std::optional<Fps> renderRate; uid_t uid = 0; std::string layerName; - int32_t gameMode = 0; + GameMode gameMode = GameMode::Unsupported; int32_t reasons = 0; nsecs_t displayDeadlineDelta = 0; nsecs_t displayPresentJitter = 0; @@ -194,7 +195,7 @@ class TimeStats : public android::TimeStats { struct LayerRecord { uid_t uid; std::string layerName; - int32_t gameMode = 0; + GameMode gameMode = GameMode::Unsupported; // This is the index in timeRecords, at which the timestamps for that // specific frame are still not fully received. This is not waiting for // fences to signal, but rather waiting to receive those fences/timestamps. @@ -247,7 +248,7 @@ public: const std::shared_ptr<FenceTime>& readyFence) override; void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid, - nsecs_t postTime, int32_t gameMode) override; + nsecs_t postTime, GameMode) override; void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override; void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override; void incrementBadDesiredPresent(int32_t layerId) override; @@ -256,12 +257,11 @@ public: void setAcquireFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& acquireFence) override; void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, - Fps displayRefreshRate, std::optional<Fps> renderRate, - SetFrameRateVote frameRateVote, int32_t gameMode) override; + Fps displayRefreshRate, std::optional<Fps> renderRate, SetFrameRateVote, + GameMode) override; void setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate, - std::optional<Fps> renderRate, SetFrameRateVote frameRateVote, - int32_t gameMode) override; + std::optional<Fps> renderRate, SetFrameRateVote, GameMode) override; void incrementJankyFrames(const JankyFramesInfo& info) override; // Clean up the layer record @@ -282,11 +282,11 @@ private: bool populateLayerAtom(std::string* pulledData); bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord); void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate, - std::optional<Fps> renderRate, - SetFrameRateVote frameRateVote, int32_t gameMode); + std::optional<Fps> renderRate, SetFrameRateVote, + GameMode); void flushPowerTimeLocked(); void flushAvailableGlobalRecordsToStatsLocked(); - bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, int32_t gameMode); + bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, GameMode); void enable(); void disable(); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index ffb2f0921d..69afa2a7a1 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -16,9 +16,10 @@ #include "timestatsproto/TimeStatsHelper.h" #include <android-base/stringprintf.h> -#include <inttypes.h> +#include <ftl/enum.h> #include <array> +#include <cinttypes> #define HISTOGRAM_SIZE 85 @@ -91,51 +92,15 @@ std::string TimeStatsHelper::JankPayload::toString() const { return result; } -std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) { - switch (compatibility) { - case FrameRateCompatibility::Undefined: - return "Undefined"; - case FrameRateCompatibility::Default: - return "Default"; - case FrameRateCompatibility::ExactOrMultiple: - return "ExactOrMultiple"; - } -} - -std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) { - switch (seamlessness) { - case Seamlessness::Undefined: - return "Undefined"; - case Seamlessness::ShouldBeSeamless: - return "ShouldBeSeamless"; - case Seamlessness::NotRequired: - return "NotRequired"; - } -} - std::string TimeStatsHelper::SetFrameRateVote::toString() const { std::string result; StringAppendF(&result, "frameRate = %.2f\n", frameRate); StringAppendF(&result, "frameRateCompatibility = %s\n", - toString(frameRateCompatibility).c_str()); - StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str()); + ftl::enum_string(frameRateCompatibility).c_str()); + StringAppendF(&result, "seamlessness = %s\n", ftl::enum_string(seamlessness).c_str()); return result; } -std::string TimeStatsHelper::TimeStatsLayer::toString(int32_t gameMode) const { - switch (gameMode) { - case TimeStatsHelper::GameModeUnsupported: - return "GameModeUnsupported"; - case TimeStatsHelper::GameModeStandard: - return "GameModeStandard"; - case TimeStatsHelper::GameModePerformance: - return "GameModePerformance"; - case TimeStatsHelper::GameModeBattery: - return "GameModeBattery"; - default: - return "GameModeUnspecified"; - } -} std::string TimeStatsHelper::TimeStatsLayer::toString() const { std::string result = "\n"; StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket); @@ -143,7 +108,7 @@ std::string TimeStatsHelper::TimeStatsLayer::toString() const { StringAppendF(&result, "uid = %d\n", uid); StringAppendF(&result, "layerName = %s\n", layerName.c_str()); StringAppendF(&result, "packageName = %s\n", packageName.c_str()); - StringAppendF(&result, "gameMode = %s\n", toString(gameMode).c_str()); + StringAppendF(&result, "gameMode = %s\n", ftl::enum_string(gameMode).c_str()); StringAppendF(&result, "totalFrames = %d\n", totalFrames); StringAppendF(&result, "droppedFrames = %d\n", droppedFrames); StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 2afff8d313..438561cc05 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -15,6 +15,7 @@ */ #pragma once +#include <gui/LayerMetadata.h> #include <timestatsproto/TimeStatsProtoHeader.h> #include <utils/Timers.h> @@ -63,6 +64,8 @@ public: Undefined = 0, Default = 1, ExactOrMultiple = 2, + + ftl_last = ExactOrMultiple } frameRateCompatibility = FrameRateCompatibility::Undefined; // Needs to be in sync with atoms.proto @@ -70,25 +73,13 @@ public: Undefined = 0, ShouldBeSeamless = 1, NotRequired = 2, + + ftl_last = NotRequired } seamlessness = Seamlessness::Undefined; - static std::string toString(FrameRateCompatibility); - static std::string toString(Seamlessness); std::string toString() const; }; - /** - * GameMode of the layer. GameModes are set by SysUI through WMShell. - * Actual game mode definitions are managed by GameManager.java - * The values defined here should always be in sync with the ones in GameManager. - */ - enum GameMode { - GameModeUnsupported = 0, - GameModeStandard = 1, - GameModePerformance = 2, - GameModeBattery = 3, - }; - class TimeStatsLayer { public: uid_t uid; @@ -96,7 +87,7 @@ public: std::string packageName; int32_t displayRefreshRateBucket = 0; int32_t renderRateBucket = 0; - int32_t gameMode = 0; + GameMode gameMode = GameMode::Unsupported; int32_t totalFrames = 0; int32_t droppedFrames = 0; int32_t lateAcquireFrames = 0; @@ -106,7 +97,6 @@ public: std::unordered_map<std::string, Histogram> deltas; std::string toString() const; - std::string toString(int32_t gameMode) const; SFTimeStatsLayerProto toProto() const; }; @@ -137,13 +127,14 @@ public: struct LayerStatsKey { uid_t uid = 0; std::string layerName; - int32_t gameMode = 0; + GameMode gameMode = GameMode::Unsupported; struct Hasher { size_t operator()(const LayerStatsKey& key) const { size_t uidHash = std::hash<uid_t>{}(key.uid); size_t layerNameHash = std::hash<std::string>{}(key.layerName); - size_t gameModeHash = std::hash<int32_t>{}(key.gameMode); + using T = std::underlying_type_t<GameMode>; + size_t gameModeHash = std::hash<T>{}(static_cast<T>(key.gameMode)); return HashCombine(uidHash, HashCombine(layerNameHash, gameModeHash)); } }; diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp index 84890eefd1..d136e0b6c8 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.cpp +++ b/services/surfaceflinger/Tracing/LayerTracing.cpp @@ -53,6 +53,7 @@ bool LayerTracing::disable() { mEnabled = false; LayersTraceFileProto fileProto = createTraceFileProto(); mBuffer->writeToFile(fileProto, FILE_NAME); + mBuffer->reset(); return true; } diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h index d0fb3f28fe..281cd1951f 100644 --- a/services/surfaceflinger/Tracing/RingBuffer.h +++ b/services/surfaceflinger/Tracing/RingBuffer.h @@ -21,8 +21,9 @@ #include <log/log.h> #include <utils/Errors.h> -#include <utils/SystemClock.h> +#include <utils/Timers.h> #include <utils/Trace.h> +#include <chrono> #include <queue> namespace android { @@ -38,27 +39,27 @@ public: void setSize(size_t newSize) { mSizeInBytes = newSize; } EntryProto& front() { return mStorage.front(); } const EntryProto& front() const { return mStorage.front(); } + const EntryProto& back() const { return mStorage.back(); } - void reset(size_t newSize) { + void reset() { // use the swap trick to make sure memory is released - std::queue<EntryProto>().swap(mStorage); - mSizeInBytes = newSize; + std::deque<EntryProto>().swap(mStorage); mUsedInBytes = 0U; } - void flush(FileProto& fileProto) { - fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size())); - while (!mStorage.empty()) { - auto entry = fileProto.add_entry(); - entry->Swap(&mStorage.front()); - mStorage.pop(); + + void writeToProto(FileProto& fileProto) { + fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()) + + fileProto.entry().size()); + for (const EntryProto& entry : mStorage) { + EntryProto* entryProto = fileProto.add_entry(); + *entryProto = entry; } } status_t writeToFile(FileProto& fileProto, std::string filename) { ATRACE_CALL(); + writeToProto(fileProto); std::string output; - flush(fileProto); - reset(mSizeInBytes); if (!fileProto.SerializeToString(&output)) { ALOGE("Could not serialize proto."); return UNKNOWN_ERROR; @@ -82,10 +83,10 @@ public: } mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize()); replacedEntries.emplace_back(mStorage.front()); - mStorage.pop(); + mStorage.pop_front(); } mUsedInBytes += protoSize; - mStorage.emplace(); + mStorage.emplace_back(); mStorage.back().Swap(&proto); return replacedEntries; } @@ -99,14 +100,14 @@ public: const int64_t durationCount = duration.count(); base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB) duration: %" PRIi64 "ms\n", - frameCount(), float(used()) / 1024.f * 1024.f, - float(size()) / 1024.f * 1024.f, durationCount); + frameCount(), float(used()) / (1024.f * 1024.f), + float(size()) / (1024.f * 1024.f), durationCount); } private: size_t mUsedInBytes = 0U; size_t mSizeInBytes = 0U; - std::queue<EntryProto> mStorage; + std::deque<EntryProto> mStorage; }; } // namespace android diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index fb1d43bf6c..783b36e66c 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -22,9 +22,9 @@ namespace android::surfaceflinger { -proto::TransactionState TransactionProtoParser::toProto( - const TransactionState& t, std::function<int32_t(const sp<IBinder>&)> getLayerId, - std::function<int32_t(const sp<IBinder>&)> getDisplayId) { +proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t, + LayerHandleToIdFn getLayerId, + DisplayHandleToIdFn getDisplayId) { proto::TransactionState proto; proto.set_pid(t.originPid); proto.set_uid(t.originUid); @@ -42,10 +42,38 @@ proto::TransactionState TransactionProtoParser::toProto( return proto; } -proto::LayerState TransactionProtoParser::toProto( - const layer_state_t& layer, std::function<int32_t(const sp<IBinder>&)> getLayerId) { +proto::TransactionState TransactionProtoParser::toProto( + const std::map<int32_t /* layerId */, TracingLayerState> states) { + proto::TransactionState proto; + for (auto& [layerId, state] : states) { + proto::LayerState layerProto = toProto(state, nullptr); + 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); + } + 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, + LayerHandleToIdFn getLayerId) { proto::LayerState proto; - proto.set_layer_id(layer.layerId); + if (getLayerId != nullptr) { + proto.set_layer_id(getLayerId(layer.surface)); + } else { + proto.set_layer_id(layer.layerId); + } + proto.set_what(layer.what); if (layer.what & layer_state_t::ePositionChanged) { @@ -130,13 +158,13 @@ proto::LayerState TransactionProtoParser::toProto( } } - if (layer.what & layer_state_t::eReparent) { + if ((layer.what & layer_state_t::eReparent) && getLayerId != nullptr) { int32_t layerId = layer.parentSurfaceControlForChild ? getLayerId(layer.parentSurfaceControlForChild->getHandle()) : -1; proto.set_parent_id(layerId); } - if (layer.what & layer_state_t::eRelativeLayerChanged) { + if ((layer.what & layer_state_t::eRelativeLayerChanged) && getLayerId != nullptr) { int32_t layerId = layer.relativeLayerSurfaceControl ? getLayerId(layer.relativeLayerSurfaceControl->getHandle()) : -1; @@ -164,8 +192,12 @@ proto::LayerState TransactionProtoParser::toProto( transformProto->set_ty(inputInfo->transform.ty()); windowInfoProto->set_replace_touchable_region_with_crop( inputInfo->replaceTouchableRegionWithCrop); - windowInfoProto->set_crop_layer_id( - getLayerId(inputInfo->touchableRegionCropHandle.promote())); + if (getLayerId != nullptr) { + windowInfoProto->set_crop_layer_id( + getLayerId(inputInfo->touchableRegionCropHandle.promote())); + } else { + windowInfoProto->set_crop_layer_id(-1); + } } } if (layer.what & layer_state_t::eBackgroundColorChanged) { @@ -212,11 +244,13 @@ proto::LayerState TransactionProtoParser::toProto( return proto; } -proto::DisplayState TransactionProtoParser::toProto( - const DisplayState& display, std::function<int32_t(const sp<IBinder>&)> getDisplayId) { +proto::DisplayState TransactionProtoParser::toProto(const DisplayState& display, + DisplayHandleToIdFn getDisplayId) { proto::DisplayState proto; proto.set_what(display.what); - proto.set_id(getDisplayId(display.token)); + if (getDisplayId != nullptr) { + proto.set_id(getDisplayId(display.token)); + } if (display.what & DisplayState::eLayerStackChanged) { proto.set_layer_stack(display.layerStack.id); @@ -238,9 +272,18 @@ proto::DisplayState TransactionProtoParser::toProto( return proto; } -TransactionState TransactionProtoParser::fromProto( - const proto::TransactionState& proto, std::function<sp<IBinder>(int32_t)> getLayerHandle, - std::function<sp<IBinder>(int32_t)> getDisplayHandle) { +proto::LayerCreationArgs TransactionProtoParser::toProto(const TracingLayerCreationArgs& args) { + proto::LayerCreationArgs proto; + proto.set_layer_id(args.layerId); + proto.set_name(args.name); + proto.set_flags(args.flags); + proto.set_parent_id(args.parentId); + return proto; +} + +TransactionState TransactionProtoParser::fromProto(const proto::TransactionState& proto, + LayerIdToHandleFn getLayerHandle, + DisplayIdToHandleFn getDisplayHandle) { TransactionState t; t.originPid = proto.pid(); t.originUid = proto.uid(); @@ -251,7 +294,7 @@ TransactionState TransactionProtoParser::fromProto( t.states.reserve(static_cast<size_t>(layerCount)); for (int i = 0; i < layerCount; i++) { ComposerState s; - s.state = std::move(fromProto(proto.layer_changes(i), getLayerHandle)); + fromProto(proto.layer_changes(i), getLayerHandle, s.state); t.states.add(s); } @@ -263,88 +306,116 @@ TransactionState TransactionProtoParser::fromProto( return t; } -layer_state_t TransactionProtoParser::fromProto( - const proto::LayerState& proto, std::function<sp<IBinder>(int32_t)> getLayerHandle) { - layer_state_t layer; +void TransactionProtoParser::fromProto(const proto::LayerCreationArgs& proto, + TracingLayerCreationArgs& outArgs) { + outArgs.layerId = proto.layer_id(); + outArgs.name = proto.name(); + outArgs.flags = proto.flags(); + outArgs.parentId = proto.parent_id(); +} + +void TransactionProtoParser::fromProto(const proto::LayerState& proto, + LayerIdToHandleFn getLayerHandle, + TracingLayerState& outState) { + fromProto(proto, getLayerHandle, static_cast<layer_state_t&>(outState)); + if (proto.what() & layer_state_t::eReparent) { + outState.parentId = proto.parent_id(); + } + if (proto.what() & layer_state_t::eRelativeLayerChanged) { + outState.relativeParentId = proto.relative_parent_id(); + } + if (proto.what() & layer_state_t::eInputInfoChanged) { + outState.inputCropId = proto.window_info_handle().crop_layer_id(); + } + if (proto.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(); + } + if (proto.what() & layer_state_t::eSidebandStreamChanged) { + outState.hasSidebandStream = proto.has_sideband_stream(); + } +} + +void TransactionProtoParser::fromProto(const proto::LayerState& proto, + LayerIdToHandleFn getLayerHandle, layer_state_t& layer) { layer.layerId = proto.layer_id(); - layer.what = proto.what(); + layer.what |= proto.what(); - if (layer.what & layer_state_t::ePositionChanged) { + if (getLayerHandle != nullptr) { + layer.surface = getLayerHandle(layer.layerId); + } + + if (proto.what() & layer_state_t::ePositionChanged) { layer.x = proto.x(); layer.y = proto.y(); } - if (layer.what & layer_state_t::eLayerChanged) { + if (proto.what() & layer_state_t::eLayerChanged) { layer.z = proto.z(); } - if (layer.what & layer_state_t::eSizeChanged) { + if (proto.what() & layer_state_t::eSizeChanged) { layer.w = proto.w(); layer.h = proto.h(); } - if (layer.what & layer_state_t::eLayerStackChanged) { + if (proto.what() & layer_state_t::eLayerStackChanged) { layer.layerStack.id = proto.layer_stack(); } - if (layer.what & layer_state_t::eFlagsChanged) { + if (proto.what() & layer_state_t::eFlagsChanged) { layer.flags = proto.flags(); layer.mask = proto.mask(); } - if (layer.what & layer_state_t::eMatrixChanged) { + if (proto.what() & layer_state_t::eMatrixChanged) { const proto::LayerState_Matrix22& matrixProto = proto.matrix(); layer.matrix.dsdx = matrixProto.dsdx(); layer.matrix.dsdy = matrixProto.dsdy(); layer.matrix.dtdx = matrixProto.dtdx(); layer.matrix.dtdy = matrixProto.dtdy(); } - if (layer.what & layer_state_t::eCornerRadiusChanged) { + if (proto.what() & layer_state_t::eCornerRadiusChanged) { layer.cornerRadius = proto.corner_radius(); } - if (layer.what & layer_state_t::eBackgroundBlurRadiusChanged) { + if (proto.what() & layer_state_t::eBackgroundBlurRadiusChanged) { layer.backgroundBlurRadius = proto.background_blur_radius(); } - if (layer.what & layer_state_t::eAlphaChanged) { + if (proto.what() & layer_state_t::eAlphaChanged) { layer.alpha = proto.alpha(); } - if (layer.what & layer_state_t::eColorChanged) { + if (proto.what() & layer_state_t::eColorChanged) { const proto::LayerState_Color3& colorProto = proto.color(); layer.color.r = colorProto.r(); layer.color.g = colorProto.g(); layer.color.b = colorProto.b(); } - if (layer.what & layer_state_t::eTransparentRegionChanged) { + if (proto.what() & layer_state_t::eTransparentRegionChanged) { LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion); } - if (layer.what & layer_state_t::eTransformChanged) { + if (proto.what() & layer_state_t::eTransformChanged) { layer.transform = proto.transform(); } - if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) { + if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) { layer.transformToDisplayInverse = proto.transform_to_display_inverse(); } - if (layer.what & layer_state_t::eCropChanged) { + if (proto.what() & layer_state_t::eCropChanged) { LayerProtoHelper::readFromProto(proto.crop(), layer.crop); } - if (layer.what & layer_state_t::eBufferChanged) { + if (proto.what() & layer_state_t::eBufferChanged) { const proto::LayerState_BufferData& bufferProto = proto.buffer_data(); - layer.bufferData.buffer = new GraphicBuffer(bufferProto.width(), bufferProto.height(), - HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); layer.bufferData.frameNumber = bufferProto.frame_number(); layer.bufferData.flags = Flags<BufferData::BufferDataChange>(bufferProto.flags()); layer.bufferData.cachedBuffer.id = bufferProto.cached_buffer_id(); } - if (layer.what & layer_state_t::eSidebandStreamChanged) { - native_handle_t* handle = native_handle_create(0, 0); - layer.sidebandStream = - proto.has_sideband_stream() ? NativeHandle::create(handle, true) : nullptr; - } - if (layer.what & layer_state_t::eApiChanged) { + if (proto.what() & layer_state_t::eApiChanged) { layer.api = proto.api(); } - if (layer.what & layer_state_t::eColorTransformChanged) { + if (proto.what() & layer_state_t::eColorTransformChanged) { LayerProtoHelper::readFromProto(proto.color_transform(), layer.colorTransform); } - if (layer.what & layer_state_t::eBlurRegionsChanged) { + if (proto.what() & layer_state_t::eBlurRegionsChanged) { layer.blurRegions.reserve(static_cast<size_t>(proto.blur_regions_size())); for (int i = 0; i < proto.blur_regions_size(); i++) { android::BlurRegion region; @@ -353,20 +424,20 @@ layer_state_t TransactionProtoParser::fromProto( } } - if (layer.what & layer_state_t::eReparent) { + if ((proto.what() & layer_state_t::eReparent) && (getLayerHandle != nullptr)) { int32_t layerId = proto.parent_id(); layer.parentSurfaceControlForChild = new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId), nullptr, layerId); } - if (layer.what & layer_state_t::eRelativeLayerChanged) { + if ((proto.what() & layer_state_t::eRelativeLayerChanged) && (getLayerHandle != nullptr)) { int32_t layerId = proto.relative_parent_id(); layer.relativeLayerSurfaceControl = new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId), nullptr, layerId); } - if ((layer.what & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) { + if ((proto.what() & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) { gui::WindowInfo inputInfo; const proto::LayerState_WindowInfo& windowInfoProto = proto.window_info_handle(); @@ -385,10 +456,12 @@ layer_state_t TransactionProtoParser::fromProto( inputInfo.replaceTouchableRegionWithCrop = windowInfoProto.replace_touchable_region_with_crop(); int32_t layerId = windowInfoProto.crop_layer_id(); - inputInfo.touchableRegionCropHandle = getLayerHandle(layerId); + if (getLayerHandle != nullptr) { + inputInfo.touchableRegionCropHandle = getLayerHandle(layerId); + } layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo); } - if (layer.what & layer_state_t::eBackgroundColorChanged) { + if (proto.what() & layer_state_t::eBackgroundColorChanged) { layer.bgColorAlpha = proto.bg_color_alpha(); layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace()); const proto::LayerState_Color3& colorProto = proto.color(); @@ -396,44 +469,43 @@ layer_state_t TransactionProtoParser::fromProto( layer.color.g = colorProto.g(); layer.color.b = colorProto.b(); } - if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) { + if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) { layer.colorSpaceAgnostic = proto.color_space_agnostic(); } - if (layer.what & layer_state_t::eShadowRadiusChanged) { + if (proto.what() & layer_state_t::eShadowRadiusChanged) { layer.shadowRadius = proto.shadow_radius(); } - if (layer.what & layer_state_t::eFrameRateSelectionPriority) { + if (proto.what() & layer_state_t::eFrameRateSelectionPriority) { layer.frameRateSelectionPriority = proto.frame_rate_selection_priority(); } - if (layer.what & layer_state_t::eFrameRateChanged) { + if (proto.what() & layer_state_t::eFrameRateChanged) { layer.frameRate = proto.frame_rate(); layer.frameRateCompatibility = static_cast<int8_t>(proto.frame_rate_compatibility()); layer.changeFrameRateStrategy = static_cast<int8_t>(proto.change_frame_rate_strategy()); } - if (layer.what & layer_state_t::eFixedTransformHintChanged) { + if (proto.what() & layer_state_t::eFixedTransformHintChanged) { layer.fixedTransformHint = static_cast<ui::Transform::RotationFlags>(proto.fixed_transform_hint()); } - if (layer.what & layer_state_t::eAutoRefreshChanged) { + if (proto.what() & layer_state_t::eAutoRefreshChanged) { layer.autoRefresh = proto.auto_refresh(); } - if (layer.what & layer_state_t::eTrustedOverlayChanged) { + if (proto.what() & layer_state_t::eTrustedOverlayChanged) { layer.isTrustedOverlay = proto.is_trusted_overlay(); } - if (layer.what & layer_state_t::eBufferCropChanged) { + if (proto.what() & layer_state_t::eBufferCropChanged) { LayerProtoHelper::readFromProto(proto.buffer_crop(), layer.bufferCrop); } - if (layer.what & layer_state_t::eDestinationFrameChanged) { + if (proto.what() & layer_state_t::eDestinationFrameChanged) { LayerProtoHelper::readFromProto(proto.destination_frame(), layer.destinationFrame); } - if (layer.what & layer_state_t::eDropInputModeChanged) { + if (proto.what() & layer_state_t::eDropInputModeChanged) { layer.dropInputMode = static_cast<gui::DropInputMode>(proto.drop_input_mode()); } - return layer; } -DisplayState TransactionProtoParser::fromProto( - const proto::DisplayState& proto, std::function<sp<IBinder>(int32_t)> getDisplayHandle) { +DisplayState TransactionProtoParser::fromProto(const proto::DisplayState& proto, + DisplayIdToHandleFn getDisplayHandle) { DisplayState display; display.what = proto.what(); display.token = getDisplayHandle(proto.id()); diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h index a2b8889eb7..16e9b5e729 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.h +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h @@ -21,24 +21,53 @@ #include "TransactionState.h" namespace android::surfaceflinger { + +struct TracingLayerCreationArgs { + int32_t layerId; + std::string name; + uint32_t flags; + int32_t parentId; +}; + +struct TracingLayerState : layer_state_t { + uint64_t bufferId; + uint32_t bufferHeight; + uint32_t bufferWidth; + bool hasSidebandStream; + int32_t parentId; + int32_t relativeParentId; + int32_t inputCropId; + std::string name; + uint32_t layerCreationFlags; +}; + class TransactionProtoParser { public: + typedef std::function<sp<IBinder>(int32_t)> LayerIdToHandleFn; + typedef std::function<sp<IBinder>(int32_t)> DisplayIdToHandleFn; + typedef std::function<int32_t(const sp<IBinder>&)> LayerHandleToIdFn; + typedef std::function<int32_t(const sp<IBinder>&)> DisplayHandleToIdFn; + + static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn, + DisplayHandleToIdFn getDisplayIdFn); static proto::TransactionState toProto( - const TransactionState&, std::function<int32_t(const sp<IBinder>&)> getLayerIdFn, - std::function<int32_t(const sp<IBinder>&)> getDisplayIdFn); + const std::map<int32_t /* layerId */, TracingLayerState>); + + static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args); + static TransactionState fromProto(const proto::TransactionState&, - std::function<sp<IBinder>(int32_t)> getLayerHandleFn, - std::function<sp<IBinder>(int32_t)> getDisplayHandleFn); + LayerIdToHandleFn getLayerHandleFn, + DisplayIdToHandleFn getDisplayHandleFn); + static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn, + TracingLayerState& outState); + static void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs); private: - static proto::LayerState toProto(const layer_state_t&, - std::function<int32_t(const sp<IBinder>&)> getLayerId); - static proto::DisplayState toProto(const DisplayState&, - std::function<int32_t(const sp<IBinder>&)> getDisplayId); - static layer_state_t fromProto(const proto::LayerState&, - std::function<sp<IBinder>(int32_t)> getLayerHandle); - static DisplayState fromProto(const proto::DisplayState&, - std::function<sp<IBinder>(int32_t)> getDisplayHandle); + static proto::LayerState toProto(const layer_state_t&, LayerHandleToIdFn getLayerId); + static proto::DisplayState toProto(const DisplayState&, DisplayHandleToIdFn getDisplayId); + static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandle, + layer_state_t& out); + static DisplayState fromProto(const proto::DisplayState&, DisplayIdToHandleFn getDisplayHandle); }; } // namespace android::surfaceflinger
\ No newline at end of file diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp new file mode 100644 index 0000000000..cf488c26f9 --- /dev/null +++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp @@ -0,0 +1,329 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "TransactionTracing" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <android-base/stringprintf.h> +#include <log/log.h> +#include <utils/SystemClock.h> +#include <utils/Trace.h> + +#include "RingBuffer.h" +#include "TransactionTracing.h" + +namespace android { + +TransactionTracing::TransactionTracing() { + mBuffer = std::make_unique< + RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>>(); +} + +TransactionTracing::~TransactionTracing() = default; + +bool TransactionTracing::enable() { + std::scoped_lock lock(mTraceLock); + if (mEnabled) { + return false; + } + mBuffer->setSize(mBufferSizeInBytes); + mStartingTimestamp = systemTime(); + mEnabled = true; + { + std::scoped_lock lock(mMainThreadLock); + mDone = false; + mThread = std::thread(&TransactionTracing::loop, this); + } + return true; +} + +bool TransactionTracing::disable() { + std::thread thread; + { + std::scoped_lock lock(mMainThreadLock); + mDone = true; + mTransactionsAvailableCv.notify_all(); + thread = std::move(mThread); + } + if (thread.joinable()) { + thread.join(); + } + + std::scoped_lock lock(mTraceLock); + if (!mEnabled) { + return false; + } + mEnabled = false; + + writeToFileLocked(); + mBuffer->reset(); + mQueuedTransactions.clear(); + mStartingStates.clear(); + mLayerHandles.clear(); + return true; +} + +bool TransactionTracing::isEnabled() const { + std::scoped_lock lock(mTraceLock); + return mEnabled; +} + +status_t TransactionTracing::writeToFile() { + std::scoped_lock lock(mTraceLock); + if (!mEnabled) { + return STATUS_OK; + } + return writeToFileLocked(); +} + +status_t TransactionTracing::writeToFileLocked() { + proto::TransactionTraceFile fileProto = createTraceFileProto(); + addStartingStateToProtoLocked(fileProto); + return mBuffer->writeToFile(fileProto, FILE_NAME); +} + +void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) { + std::scoped_lock lock(mTraceLock); + mBufferSizeInBytes = bufferSizeInBytes; + mBuffer->setSize(mBufferSizeInBytes); +} + +proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const { + proto::TransactionTraceFile proto; + proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 | + proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L); + return proto; +} + +void TransactionTracing::dump(std::string& result) const { + std::scoped_lock lock(mTraceLock); + base::StringAppendF(&result, "Transaction tracing state: %s\n", + mEnabled ? "enabled" : "disabled"); + base::StringAppendF(&result, + " queued transactions=%zu created layers=%zu handles=%zu states=%zu\n", + mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(), + mStartingStates.size()); + mBuffer->dump(result); +} + +void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) { + std::scoped_lock lock(mTraceLock); + ATRACE_CALL(); + if (!mEnabled) { + return; + } + mQueuedTransactions[transaction.id] = + TransactionProtoParser::toProto(transaction, + std::bind(&TransactionTracing::getLayerIdLocked, this, + std::placeholders::_1), + nullptr); +} + +void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions, + int64_t vsyncId) { + CommittedTransactions committedTransactions; + committedTransactions.vsyncId = vsyncId; + committedTransactions.timestamp = systemTime(); + committedTransactions.transactionIds.reserve(transactions.size()); + for (const auto& transaction : transactions) { + committedTransactions.transactionIds.emplace_back(transaction.id); + } + + mPendingTransactions.emplace_back(committedTransactions); + tryPushToTracingThread(); +} + +void TransactionTracing::loop() { + while (true) { + std::vector<CommittedTransactions> committedTransactions; + std::vector<int32_t> removedLayers; + { + std::unique_lock<std::mutex> lock(mMainThreadLock); + base::ScopedLockAssertion assumeLocked(mMainThreadLock); + mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) { + return mDone || !mCommittedTransactions.empty(); + }); + if (mDone) { + mCommittedTransactions.clear(); + mRemovedLayers.clear(); + break; + } + + removedLayers = std::move(mRemovedLayers); + mRemovedLayers.clear(); + committedTransactions = std::move(mCommittedTransactions); + mCommittedTransactions.clear(); + } // unlock mMainThreadLock + + addEntry(committedTransactions, removedLayers); + } +} + +void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions, + const std::vector<int32_t>& removedLayers) { + ATRACE_CALL(); + std::scoped_lock lock(mTraceLock); + std::vector<proto::TransactionTraceEntry> removedEntries; + for (const CommittedTransactions& entry : committedTransactions) { + proto::TransactionTraceEntry entryProto; + entryProto.set_elapsed_realtime_nanos(entry.timestamp); + entryProto.set_vsync_id(entry.vsyncId); + entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size())); + for (auto& newLayer : mCreatedLayers) { + entryProto.mutable_added_layers()->Add(std::move(newLayer)); + } + entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size())); + for (auto& removedLayer : removedLayers) { + entryProto.mutable_removed_layers()->Add(removedLayer); + } + mCreatedLayers.clear(); + entryProto.mutable_transactions()->Reserve( + static_cast<int32_t>(entry.transactionIds.size())); + for (const uint64_t& id : entry.transactionIds) { + auto it = mQueuedTransactions.find(id); + if (it != mQueuedTransactions.end()) { + entryProto.mutable_transactions()->Add(std::move(it->second)); + mQueuedTransactions.erase(it); + } else { + ALOGE("Could not find transaction id %" PRIu64, id); + } + } + std::vector<proto::TransactionTraceEntry> entries = mBuffer->emplace(std::move(entryProto)); + removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()), + std::make_move_iterator(entries.end())); + } + + for (const proto::TransactionTraceEntry& removedEntry : removedEntries) { + updateStartingStateLocked(removedEntry); + } + mTransactionsAddedToBufferCv.notify_one(); +} + +void TransactionTracing::flush(int64_t vsyncId) { + while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) { + tryPushToTracingThread(); + } + std::unique_lock<std::mutex> lock(mTraceLock); + base::ScopedLockAssertion assumeLocked(mTraceLock); + mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) { + return mBuffer->used() > 0 && mBuffer->back().vsync_id() >= vsyncId; + }); +} + +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}; + mLayerHandles[layerHandle] = layerId; + mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args)); +} + +void TransactionTracing::onLayerRemoved(int32_t layerId) { + mPendingRemovedLayers.emplace_back(layerId); + tryPushToTracingThread(); +} + +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(); + mTransactionsAvailableCv.notify_one(); + mMainThreadLock.unlock(); + } else { + ALOGV("Couldn't get lock"); + } +} + +int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) { + if (layerHandle == nullptr) { + return -1; + } + auto it = mLayerHandles.find(layerHandle->localBinder()); + return it == mLayerHandles.end() ? -1 : it->second; +} + +void TransactionTracing::updateStartingStateLocked( + const proto::TransactionTraceEntry& removedEntry) { + // Keep track of layer starting state so we can reconstruct the layer state as we purge + // transactions from the buffer. + for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) { + TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()]; + startingState.layerId = addedLayer.layer_id(); + startingState.name = addedLayer.name(); + startingState.layerCreationFlags = addedLayer.flags(); + startingState.parentId = addedLayer.parent_id(); + } + + // 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(layerState.layer_id()); + if (it == mStartingStates.end()) { + ALOGE("Could not find layer id %d", layerState.layer_id()); + continue; + } + TransactionProtoParser::fromProto(layerState, nullptr, it->second); + } + } + + // 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()) { + auto it = std::find_if(mLayerHandles.begin(), mLayerHandles.end(), + [removedLayerId](auto& layer) { + return layer.second == removedLayerId; + }); + if (it != mLayerHandles.end()) { + mLayerHandles.erase(it); + } + mStartingStates.erase(removedLayerId); + } +} + +void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) { + proto::TransactionTraceEntry* entryProto = proto.add_entry(); + entryProto->set_elapsed_realtime_nanos(mStartingTimestamp); + entryProto->set_vsync_id(0); + entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size())); + for (auto& [layerId, state] : mStartingStates) { + TracingLayerCreationArgs args{layerId, state.name, state.layerCreationFlags, + state.parentId}; + entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(args)); + } + + proto::TransactionState transactionProto = TransactionProtoParser::toProto(mStartingStates); + transactionProto.set_vsync_id(0); + transactionProto.set_post_time(mStartingTimestamp); + entryProto->mutable_transactions()->Add(std::move(transactionProto)); +} + +proto::TransactionTraceFile TransactionTracing::writeToProto() { + std::scoped_lock<std::mutex> lock(mTraceLock); + proto::TransactionTraceFile proto = createTraceFileProto(); + addStartingStateToProtoLocked(proto); + mBuffer->writeToProto(proto); + return proto; +} + +} // namespace android diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h new file mode 100644 index 0000000000..546ac7afb0 --- /dev/null +++ b/services/surfaceflinger/Tracing/TransactionTracing.h @@ -0,0 +1,125 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/thread_annotations.h> +#include <layerproto/TransactionProto.h> +#include <utils/Errors.h> +#include <utils/Timers.h> + +#include <memory> +#include <mutex> +#include <thread> + +#include "TransactionProtoParser.h" + +using namespace android::surfaceflinger; + +namespace android { + +template <typename FileProto, typename EntryProto> +class RingBuffer; + +class SurfaceFlinger; +class TransactionTracingTest; +/* + * Records all committed transactions into a ring bufffer. + * + * Transactions come in via the binder thread. They are serialized to proto + * and stored in a map using the transaction id as key. Main thread will + * pass the list of transaction ids that are committed every vsync and notify + * the tracing thread. The tracing thread will then wake up and add the + * committed transactions to the ring buffer. + * + * When generating SF dump state, we will flush the buffer to a file which + * will then be included in the bugreport. + * + */ +class TransactionTracing { +public: + TransactionTracing(); + ~TransactionTracing(); + + bool enable(); + bool disable(); + bool isEnabled() const; + + void addQueuedTransaction(const TransactionState&); + void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId); + status_t writeToFile(); + void setBufferSize(size_t bufferSizeInBytes); + void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags, + int parentId); + void onLayerRemoved(int layerId); + void dump(std::string&) const; + static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024; + static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024; + +private: + friend class TransactionTracingTest; + + static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope"; + + mutable std::mutex mTraceLock; + bool mEnabled GUARDED_BY(mTraceLock) = false; + std::unique_ptr<RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>> mBuffer + GUARDED_BY(mTraceLock); + size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE; + std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions + GUARDED_BY(mTraceLock); + nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock); + std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock); + std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles + GUARDED_BY(mTraceLock); + std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock); + + // We do not want main thread to block so main thread will try to acquire mMainThreadLock, + // otherwise will push data to temporary container. + std::mutex mMainThreadLock; + std::thread mThread GUARDED_BY(mMainThreadLock); + bool mDone GUARDED_BY(mMainThreadLock) = false; + std::condition_variable mTransactionsAvailableCv; + std::condition_variable mTransactionsAddedToBufferCv; + struct CommittedTransactions { + std::vector<uint64_t> transactionIds; + int64_t vsyncId; + int64_t timestamp; + }; + std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock); + std::vector<CommittedTransactions> mPendingTransactions; // 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 + + proto::TransactionTraceFile createTraceFileProto() const; + void loop(); + void addEntry(const std::vector<CommittedTransactions>& committedTransactions, + const std::vector<int32_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); + status_t writeToFileLocked() REQUIRES(mTraceLock); + + // TEST + // Wait until all the committed transactions for the specified vsync id are added to the buffer. + void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock); + // Return buffer contents as trace file proto + proto::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock); +}; + +} // namespace android diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index f3d46ea061..b705d9cf7b 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -24,6 +24,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "TransactionCallbackInvoker.h" +#include "BackgroundExecutor.h" #include <cinttypes> @@ -49,31 +50,6 @@ static bool containsOnCommitCallbacks(const std::vector<CallbackId>& callbacks) return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT; } -TransactionCallbackInvoker::TransactionCallbackInvoker() { - mThread = std::thread([&]() { - std::unique_lock lock(mCallbackThreadMutex); - - while (mKeepRunning) { - while (mCallbackThreadWork.size() > 0) { - mCallbackThreadWork.front()(); - mCallbackThreadWork.pop(); - } - mCallbackConditionVariable.wait(lock); - } - }); -} - -TransactionCallbackInvoker::~TransactionCallbackInvoker() { - { - std::unique_lock lock(mCallbackThreadMutex); - mKeepRunning = false; - mCallbackConditionVariable.notify_all(); - } - if (mThread.joinable()) { - mThread.join(); - } -} - void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) { auto& [listener, callbackIds] = listenerCallbacks; auto& transactionStatsDeque = mCompletedTransactions[listener]; @@ -242,15 +218,10 @@ void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { // keep it as an IBinder due to consistency reasons: if we // interface_cast at the IPC boundary when reading a Parcel, // we get pointers that compare unequal in the SF process. - { - std::unique_lock lock(mCallbackThreadMutex); - mCallbackThreadWork.push( - [stats = std::move(listenerStats)]() { - interface_cast<ITransactionCompletedListener>(stats.listener) - ->onTransactionCompleted(stats); - }); - mCallbackConditionVariable.notify_all(); - } + BackgroundExecutor::getInstance().execute([stats = std::move(listenerStats)]() { + interface_cast<ITransactionCompletedListener>(stats.listener) + ->onTransactionCompleted(stats); + }); } } completedTransactionsItr++; diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index e203d41bd9..5ef54757d7 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -61,9 +61,6 @@ public: class TransactionCallbackInvoker { public: - TransactionCallbackInvoker(); - ~TransactionCallbackInvoker(); - status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData); status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, @@ -94,12 +91,6 @@ private: mCompletedTransactions; sp<Fence> mPresentFence; - - std::mutex mCallbackThreadMutex; - std::condition_variable mCallbackConditionVariable; - std::thread mThread; - bool mKeepRunning = true; - std::queue<std::function<void()>> mCallbackThreadWork; }; } // namespace android diff --git a/services/surfaceflinger/layerproto/display.proto b/services/surfaceflinger/layerproto/display.proto index ee8830e7f2..c8cd9266a7 100644 --- a/services/surfaceflinger/layerproto/display.proto +++ b/services/surfaceflinger/layerproto/display.proto @@ -33,4 +33,6 @@ message DisplayProto { RectProto layer_stack_space_rect = 5; TransformProto transform = 6; + + bool is_virtual = 7; } diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto index e7fb1806be..10222cc283 100644 --- a/services/surfaceflinger/layerproto/transactions.proto +++ b/services/surfaceflinger/layerproto/transactions.proto @@ -39,21 +39,30 @@ message TransactionTraceFile { } message TransactionTraceEntry { - int64 elapsed_time = 1; + int64 elapsed_realtime_nanos = 1; int64 vsync_id = 2; repeated TransactionState transactions = 3; + repeated LayerCreationArgs added_layers = 4; + repeated int32 removed_layers = 5; + repeated DisplayState added_displays = 6; + repeated int32 removed_displays = 7; +} + +message LayerCreationArgs { + int32 layer_id = 1; + string name = 2; + uint32 flags = 3; + int32 parent_id = 4; } message TransactionState { - string tag = 2; - int32 pid = 3; - int32 uid = 4; - int64 vsync_id = 5; - int32 input_event_id = 6; - int64 post_time = 7; - repeated LayerState layer_changes = 9; - repeated DisplayState new_displays = 10; - repeated DisplayState display_changes = 11; + int32 pid = 1; + int32 uid = 2; + int64 vsync_id = 3; + int32 input_event_id = 4; + int64 post_time = 5; + repeated LayerState layer_changes = 6; + repeated DisplayState display_changes = 7; } // Keep insync with layer_state_t @@ -130,8 +139,8 @@ message LayerState { eLayerSecure = 0x80; eEnableBackpressure = 0x100; }; - uint32 flags = 10; - uint32 mask = 11; + uint32 flags = 9; + uint32 mask = 10; message Matrix22 { float dsdx = 1; @@ -139,29 +148,29 @@ message LayerState { float dtdy = 3; float dsdy = 4; }; - Matrix22 matrix = 12; - float corner_radius = 13; - uint32 background_blur_radius = 14; - int32 parent_id = 15; - int32 relative_parent_id = 16; + Matrix22 matrix = 11; + float corner_radius = 12; + uint32 background_blur_radius = 13; + int32 parent_id = 14; + int32 relative_parent_id = 15; - float alpha = 50; + float alpha = 16; message Color3 { float r = 1; float g = 2; float b = 3; } - Color3 color = 18; - RegionProto transparent_region = 19; - uint32 transform = 20; - bool transform_to_display_inverse = 21; - RectProto crop = 49; + Color3 color = 17; + RegionProto transparent_region = 18; + uint32 transform = 19; + bool transform_to_display_inverse = 20; + RectProto crop = 21; message BufferData { uint64 buffer_id = 1; uint32 width = 2; uint32 height = 3; - uint64 frame_number = 5; + uint64 frame_number = 4; enum BufferDataChange { BufferDataChangeNone = 0; @@ -169,14 +178,14 @@ message LayerState { frameNumberChanged = 0x02; cachedBufferChanged = 0x04; } - uint32 flags = 6; - uint64 cached_buffer_id = 7; + uint32 flags = 5; + uint64 cached_buffer_id = 6; } - BufferData buffer_data = 23; - int32 api = 24; - bool has_sideband_stream = 25; - ColorTransformProto color_transform = 26; - repeated BlurRegion blur_regions = 27; + BufferData buffer_data = 22; + int32 api = 23; + bool has_sideband_stream = 24; + ColorTransformProto color_transform = 25; + repeated BlurRegion blur_regions = 26; message Transform { float dsdx = 1; @@ -189,38 +198,38 @@ message LayerState { message WindowInfo { uint32 layout_params_flags = 1; int32 layout_params_type = 2; - RegionProto touchable_region = 4; - int32 surface_inset = 5; - bool focusable = 8; - bool has_wallpaper = 9; - float global_scale_factor = 10; - int32 crop_layer_id = 13; - bool replace_touchable_region_with_crop = 14; - RectProto touchable_region_crop = 15; - Transform transform = 16; + RegionProto touchable_region = 3; + int32 surface_inset = 4; + bool focusable = 5; + bool has_wallpaper = 6; + float global_scale_factor = 7; + int32 crop_layer_id = 8; + bool replace_touchable_region_with_crop = 9; + RectProto touchable_region_crop = 10; + Transform transform = 11; } - WindowInfo window_info_handle = 28; - float bg_color_alpha = 31; - int32 bg_color_dataspace = 32; - bool color_space_agnostic = 33; - float shadow_radius = 34; - int32 frame_rate_selection_priority = 35; - float frame_rate = 36; - int32 frame_rate_compatibility = 37; - int32 change_frame_rate_strategy = 38; - uint32 fixed_transform_hint = 39; - uint64 frame_number = 40; - bool auto_refresh = 41; - bool is_trusted_overlay = 42; - RectProto buffer_crop = 44; - RectProto destination_frame = 45; + WindowInfo window_info_handle = 27; + float bg_color_alpha = 28; + int32 bg_color_dataspace = 29; + bool color_space_agnostic = 30; + float shadow_radius = 31; + int32 frame_rate_selection_priority = 32; + float frame_rate = 33; + int32 frame_rate_compatibility = 34; + int32 change_frame_rate_strategy = 35; + uint32 fixed_transform_hint = 36; + uint64 frame_number = 37; + bool auto_refresh = 38; + bool is_trusted_overlay = 39; + RectProto buffer_crop = 40; + RectProto destination_frame = 41; enum DropInputMode { NONE = 0; ALL = 1; OBSCURED = 2; }; - DropInputMode drop_input_mode = 48; + DropInputMode drop_input_mode = 42; } message DisplayState { diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp index 0069111e09..bb522451a6 100644 --- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp +++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp @@ -112,11 +112,12 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { sp<SurfaceControl> surfaceControl = mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); - + const Rect crop(0, 0, 100, 100); Transaction() .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK) .show(surfaceControl) .setLayer(surfaceControl, INT32_MAX - 1) + .setCrop(surfaceControl, crop) .setInputWindowInfo(surfaceControl, windowInfo) .apply(); diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 3dc6d8b5c1..55684187f8 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -71,6 +71,7 @@ cc_test { "MessageQueueTest.cpp", "SurfaceFlinger_CreateDisplayTest.cpp", "SurfaceFlinger_DestroyDisplayTest.cpp", + "SurfaceFlinger_DisplayModeSwitching.cpp", "SurfaceFlinger_DisplayTransactionCommitTest.cpp", "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp", "SurfaceFlinger_HotplugTest.cpp", @@ -93,6 +94,7 @@ cc_test { "TransactionFrameTracerTest.cpp", "TransactionProtoParserTest.cpp", "TransactionSurfaceFrameTest.cpp", + "TransactionTracingTest.cpp", "TunnelModeEnabledReporterTest.cpp", "StrongTypingTest.cpp", "VSyncDispatchTimerQueueTest.cpp", @@ -117,6 +119,7 @@ cc_test { static_libs: [ "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", + "android.hardware.graphics.common-V3-ndk", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", @@ -135,8 +138,9 @@ cc_test { "libgui_mocks", "liblayers_proto", "libperfetto_client_experimental", - "librenderengine_mocks", "librenderengine", + "librenderengine_mocks", + "libscheduler", "libserviceutils", "libtimestats", "libtimestats_atoms_proto", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 8d2c078305..f1e6e48049 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -138,7 +138,7 @@ public: .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); - constexpr ISchedulerCallback* kCallback = nullptr; + constexpr scheduler::ISchedulerCallback* kCallback = nullptr; constexpr bool kHasMultipleConfigs = true; mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), kCallback, @@ -370,7 +370,7 @@ struct BaseDisplayVariant { EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1); EXPECT_CALL(*test->mDisplaySurface, - prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC)) + prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc)) .Times(1); } @@ -384,7 +384,7 @@ struct BaseDisplayVariant { static void setupRECompositionCallExpectations(CompositionTest* test) { EXPECT_CALL(*test->mDisplaySurface, - prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GPU)) + prepareFrame(compositionengine::DisplaySurface::CompositionType::Gpu)) .Times(1); EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence()) .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence)); diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp index d4cfbbbe0c..5a0033ea7e 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp @@ -29,7 +29,7 @@ using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjec class InitiateModeChangeTest : public DisplayTransactionTest { public: - using Event = scheduler::RefreshRateConfigEvent; + using Event = scheduler::DisplayModeEvent; void SetUp() override { injectFakeBufferQueueFactory(); diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index de5e9dfb97..0a3437aab9 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -121,7 +121,7 @@ public: mock::VsyncController* mVsyncController = new mock::VsyncController; mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker; - mock::SchedulerCallback mSchedulerCallback; + scheduler::mock::SchedulerCallback mSchedulerCallback; mock::EventThread* mEventThread = new mock::EventThread; mock::EventThread* mSFEventThread = new mock::EventThread; diff --git a/services/surfaceflinger/tests/unittests/FpsOps.h b/services/surfaceflinger/tests/unittests/FpsOps.h index 23c2841efc..7c737dce28 100644 --- a/services/surfaceflinger/tests/unittests/FpsOps.h +++ b/services/surfaceflinger/tests/unittests/FpsOps.h @@ -16,7 +16,7 @@ #pragma once -#include "Fps.h" +#include <scheduler/Fps.h> namespace android { diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp index cd2fc7426e..bb1f4328b5 100644 --- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp @@ -17,6 +17,8 @@ #undef LOG_TAG #define LOG_TAG "FpsReporterTest" +#include <chrono> + #include <android/gui/BnFpsListener.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -36,6 +38,8 @@ namespace android { +using namespace std::chrono_literals; + using testing::_; using testing::DoAll; using testing::Mock; diff --git a/services/surfaceflinger/tests/unittests/FpsTest.cpp b/services/surfaceflinger/tests/unittests/FpsTest.cpp index b44dd89229..88b74d2a22 100644 --- a/services/surfaceflinger/tests/unittests/FpsTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsTest.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#include "Fps.h" -#include "FpsOps.h" - #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <scheduler/Fps.h> + +#include "FpsOps.h" + namespace android { TEST(FpsTest, construct) { diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 9fbaeced7b..397c6193bf 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -169,13 +169,14 @@ public: static const std::string sLayerNameOne = "layer1"; static const std::string sLayerNameTwo = "layer2"; -static constexpr const uid_t sUidOne = 0; -static constexpr pid_t sPidOne = 10; -static constexpr pid_t sPidTwo = 20; -static constexpr int32_t sInputEventId = 5; -static constexpr int32_t sLayerIdOne = 1; -static constexpr int32_t sLayerIdTwo = 2; -static constexpr int32_t sGameMode = 0; + +constexpr const uid_t sUidOne = 0; +constexpr pid_t sPidOne = 10; +constexpr pid_t sPidTwo = 20; +constexpr int32_t sInputEventId = 5; +constexpr int32_t sLayerIdOne = 1; +constexpr int32_t sLayerIdTwo = 2; +constexpr GameMode sGameMode = GameMode::Unsupported; TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) { int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0}); diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp index d6459429fb..981ca1d227 100644 --- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp +++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp @@ -91,8 +91,8 @@ public: } // Mocks the behavior of applying a transaction from WMShell - void setGameModeMetadata(sp<Layer> layer, int gameMode) { - mLayerMetadata.setInt32(METADATA_GAME_MODE, gameMode); + void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) { + mLayerMetadata.setInt32(METADATA_GAME_MODE, static_cast<int32_t>(gameMode)); layer->setMetadata(mLayerMetadata); layer->setGameModeForTree(gameMode); } @@ -109,51 +109,52 @@ TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) { sp<BufferStateLayer> childLayer2 = createBufferStateLayer(); rootLayer->addChild(childLayer1); rootLayer->addChild(childLayer2); - rootLayer->setGameModeForTree(/*gameMode*/ 2); + rootLayer->setGameModeForTree(GameMode::Performance); - EXPECT_EQ(rootLayer->getGameMode(), 2); - EXPECT_EQ(childLayer1->getGameMode(), 2); - EXPECT_EQ(childLayer2->getGameMode(), 2); + EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance); + EXPECT_EQ(childLayer1->getGameMode(), GameMode::Performance); + EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance); } TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) { sp<BufferStateLayer> rootLayer = createBufferStateLayer(); sp<BufferStateLayer> childLayer = createBufferStateLayer(); - rootLayer->setGameModeForTree(/*gameMode*/ 2); + rootLayer->setGameModeForTree(GameMode::Performance); rootLayer->addChild(childLayer); - EXPECT_EQ(rootLayer->getGameMode(), 2); - EXPECT_EQ(childLayer->getGameMode(), 2); + EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance); + EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance); } TEST_F(GameModeTest, RemoveChildResetsGameMode) { sp<BufferStateLayer> rootLayer = createBufferStateLayer(); sp<BufferStateLayer> childLayer = createBufferStateLayer(); - rootLayer->setGameModeForTree(/*gameMode*/ 2); + rootLayer->setGameModeForTree(GameMode::Performance); rootLayer->addChild(childLayer); - EXPECT_EQ(rootLayer->getGameMode(), 2); - EXPECT_EQ(childLayer->getGameMode(), 2); + EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance); + EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance); rootLayer->removeChild(childLayer); - EXPECT_EQ(childLayer->getGameMode(), 0); + EXPECT_EQ(childLayer->getGameMode(), GameMode::Unsupported); } TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) { sp<BufferStateLayer> rootLayer = createBufferStateLayer(); sp<BufferStateLayer> childLayer1 = createBufferStateLayer(); sp<BufferStateLayer> childLayer2 = createBufferStateLayer(); - rootLayer->setGameModeForTree(/*gameMode*/ 1); + rootLayer->setGameModeForTree(GameMode::Standard); rootLayer->addChild(childLayer1); - setGameModeMetadata(childLayer2, /*gameMode*/ 2); + setGameModeMetadata(childLayer2, GameMode::Performance); rootLayer->addChild(childLayer2); - EXPECT_EQ(rootLayer->getGameMode(), 1); - EXPECT_EQ(childLayer1->getGameMode(), 1); - EXPECT_EQ(childLayer2->getGameMode(), 2); + EXPECT_EQ(rootLayer->getGameMode(), GameMode::Standard); + EXPECT_EQ(childLayer1->getGameMode(), GameMode::Standard); + EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance); rootLayer->removeChild(childLayer2); - EXPECT_EQ(childLayer2->getGameMode(), 2); + EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance); } -} // namespace android
\ No newline at end of file + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 4993a2d8bc..cdb2240b40 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -38,9 +38,9 @@ using testing::_; using testing::Return; using testing::ReturnRef; -namespace android { +namespace android::scheduler { -namespace scheduler { +using MockLayer = android::mock::MockLayer; class LayerHistoryTest : public testing::Test { protected: @@ -63,42 +63,51 @@ protected: const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); } LayerHistory::Summary summarizeLayerHistory(nsecs_t now) { - return history().summarize(*mScheduler->refreshRateConfigs(), now); + // LayerHistory::summarize makes no guarantee of the order of the elements in the summary + // however, for testing only, a stable order is required, therefore we sort the list here. + // Any tests requiring ordered results must create layers with names. + auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now); + std::sort(summary.begin(), summary.end(), + [](const RefreshRateConfigs::LayerRequirement& a, + const RefreshRateConfigs::LayerRequirement& b) -> bool { + return a.name < b.name; + }); + return summary; } size_t layerCount() const { return mScheduler->layerHistorySize(); } - size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; } + size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { + return history().mActiveLayerInfos.size(); + } auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS { - const auto& infos = history().mLayerInfos; - return std::count_if(infos.begin(), - infos.begin() + static_cast<long>(history().mActiveLayersEnd), - [now](const auto& pair) { return pair.second->isFrequent(now); }); + const auto& infos = history().mActiveLayerInfos; + return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) { + return pair.second.second->isFrequent(now); + }); } auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS { - const auto& infos = history().mLayerInfos; - return std::count_if(infos.begin(), - infos.begin() + static_cast<long>(history().mActiveLayersEnd), - [now](const auto& pair) { return pair.second->isAnimating(now); }); + const auto& infos = history().mActiveLayerInfos; + return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) { + return pair.second.second->isAnimating(now); + }); } void setDefaultLayerVote(Layer* layer, LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS { - for (auto& [layerUnsafe, info] : history().mLayerInfos) { - if (layerUnsafe == layer) { - info->setDefaultLayerVote(vote); - return; - } + auto [found, layerPair] = history().findLayer(layer->getSequence()); + if (found != LayerHistory::layerStatus::NotFound) { + layerPair->second->setDefaultLayerVote(vote); } } - auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); } + auto createLayer() { return sp<MockLayer>::make(mFlinger.flinger()); } auto createLayer(std::string name) { - return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name))); + return sp<MockLayer>::make(mFlinger.flinger(), std::move(name)); } - void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, Fps frameRate, + void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate, Fps desiredRefreshRate, int numFrames) { LayerHistory::Summary summary; for (int i = 0; i < numFrames; i++) { @@ -144,6 +153,8 @@ TEST_F(LayerHistoryTest, oneLayer) { EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + // history().registerLayer(layer, LayerHistory::LayerVoteType::Max); + EXPECT_EQ(1, layerCount()); EXPECT_EQ(0, activeLayerCount()); @@ -368,9 +379,9 @@ TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) { } TEST_F(LayerHistoryTest, multipleLayers) { - auto layer1 = createLayer(); - auto layer2 = createLayer(); - auto layer3 = createLayer(); + auto layer1 = createLayer("A"); + auto layer2 = createLayer("B"); + auto layer3 = createLayer("C"); EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); @@ -654,6 +665,29 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { EXPECT_EQ(1, animatingLayerCount(time)); } +TEST_F(LayerHistoryTest, getFramerate) { + auto layer = createLayer(); + + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + + nsecs_t time = systemTime(); + + EXPECT_EQ(1, layerCount()); + EXPECT_EQ(0, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // layer is active but infrequent. + for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + } + + float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count(); + EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence())); +} + TEST_F(LayerHistoryTest, heuristicLayer60Hz) { const auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); @@ -768,8 +802,7 @@ INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestParameterized, ::testing::Values(1s, 2s, 3s, 4s, 5s)); } // namespace -} // namespace scheduler -} // namespace android +} // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wextra" diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index f25994e399..5c2d2e1f43 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -19,7 +19,8 @@ #include <gtest/gtest.h> -#include "Fps.h" +#include <scheduler/Fps.h> + #include "FpsOps.h" #include "Scheduler/LayerHistory.h" #include "Scheduler/LayerInfo.h" diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index fc84d48b46..98746bcd21 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -21,10 +21,9 @@ #undef LOG_TAG #define LOG_TAG "SchedulerUnittests" +#include <ftl/enum.h> #include <gmock/gmock.h> #include <log/log.h> -#include <thread> - #include <ui/Size.h> #include "DisplayHardware/HWC2.h" @@ -1975,7 +1974,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) { layers[0].vote = vote; EXPECT_EQ(fps.getIntValue(), refreshRateConfigs->getBestRefreshRate(layers, {}).getFps().getIntValue()) - << "Failed for " << RefreshRateConfigs::layerVoteTypeString(vote); + << "Failed for " << ftl::enum_string(vote); }; for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index e558f3b700..f48abb7519 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -28,12 +28,16 @@ #include "mock/MockLayer.h" #include "mock/MockSchedulerCallback.h" +namespace android::scheduler { + using testing::_; using testing::Return; -namespace android { namespace { +using MockEventThread = android::mock::EventThread; +using MockLayer = android::mock::MockLayer; + constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u); class SchedulerTest : public testing::Test { @@ -44,9 +48,9 @@ protected: : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {} ~MockEventThreadConnection() = default; - MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel)); - MOCK_METHOD1(setVsyncRate, status_t(uint32_t count)); - MOCK_METHOD0(requestNextVsync, void()); + MOCK_METHOD1(stealReceiveChannel, binder::Status(gui::BitTube* outChannel)); + MOCK_METHOD1(setVsyncRate, binder::Status(int count)); + MOCK_METHOD0(requestNextVsync, binder::Status()); }; SchedulerTest(); @@ -64,21 +68,21 @@ protected: .setGroup(0) .build(); - std::shared_ptr<scheduler::RefreshRateConfigs> mConfigs = - std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId()); + std::shared_ptr<RefreshRateConfigs> mConfigs = + std::make_shared<RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId()); mock::SchedulerCallback mSchedulerCallback; TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback}; - Scheduler::ConnectionHandle mConnectionHandle; - mock::EventThread* mEventThread; + ConnectionHandle mConnectionHandle; + MockEventThread* mEventThread; sp<MockEventThreadConnection> mEventThreadConnection; TestableSurfaceFlinger mFlinger; }; SchedulerTest::SchedulerTest() { - auto eventThread = std::make_unique<mock::EventThread>(); + auto eventThread = std::make_unique<MockEventThread>(); mEventThread = eventThread.get(); EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0)); @@ -98,7 +102,7 @@ SchedulerTest::SchedulerTest() { } // namespace TEST_F(SchedulerTest, invalidConnectionHandle) { - Scheduler::ConnectionHandle handle; + ConnectionHandle handle; const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle); @@ -155,7 +159,7 @@ TEST_F(SchedulerTest, validConnectionHandle) { TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) { // The layer is registered at creation time and deregistered at destruction time. - sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger()); + sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); // recordLayerHistory should be a noop ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); @@ -174,24 +178,22 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup TEST_F(SchedulerTest, updateDisplayModes) { ASSERT_EQ(0u, mScheduler->layerHistorySize()); - sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger()); + sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); ASSERT_EQ(1u, mScheduler->layerHistorySize()); mScheduler->setRefreshRateConfigs( - std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120}, - mode60->getId())); + std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId())); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1u, mScheduler->getNumActiveLayers()); } -TEST_F(SchedulerTest, testDispatchCachedReportedMode) { - // If the optional fields are cleared, the function should return before - // onModeChange is called. - mScheduler->clearOptionalFieldsInFeatures(); - EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode()); +TEST_F(SchedulerTest, dispatchCachedReportedMode) { + mScheduler->clearCachedReportedMode(); + EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); + EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode()); } TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) { @@ -203,7 +205,7 @@ TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) { // If the handle is incorrect, the function should return before // onModeChange is called. - Scheduler::ConnectionHandle invalidHandle = {.id = 123}; + ConnectionHandle invalidHandle = {.id = 123}; EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode)); EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); } @@ -224,10 +226,9 @@ MATCHER(Is120Hz, "") { TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { mScheduler->setRefreshRateConfigs( - std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120}, - mode60->getId())); + std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId())); - sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger()); + sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); @@ -241,4 +242,4 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { mScheduler->chooseRefreshRateForContent(); } -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index eed62a70c3..fe5f9e0717 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -48,6 +48,8 @@ using android::Hwc2::IComposerClient; using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using scheduler::LayerHistory; + using FrameRate = Layer::FrameRate; using FrameRateCompatibility = Layer::FrameRateCompatibility; diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp new file mode 100644 index 0000000000..9796a7072a --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -0,0 +1,295 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mock/MockEventThread.h" +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include "DisplayTransactionTestHelpers.h" + +#include <scheduler/Fps.h> + +namespace android { +namespace { + +using android::hardware::graphics::composer::V2_4::Error; +using android::hardware::graphics::composer::V2_4::VsyncPeriodChangeTimeline; + +class DisplayModeSwitchingTest : public DisplayTransactionTest { +public: + void SetUp() override { + injectFakeBufferQueueFactory(); + injectFakeNativeWindowSurfaceFactory(); + + PrimaryDisplayVariant::setupHwcHotplugCallExpectations(this); + PrimaryDisplayVariant::setupFramebufferConsumerBufferQueueCallExpectations(this); + PrimaryDisplayVariant::setupFramebufferProducerBufferQueueCallExpectations(this); + PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this); + PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this); + + mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED); + + mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) + .setSupportedModes({kDisplayMode60, kDisplayMode90, kDisplayMode120, + kDisplayMode90DifferentResolution}) + .setActiveMode(kDisplayModeId60) + .inject(); + + setupScheduler(); + + // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy + // will call setActiveConfig instead of setActiveConfigWithConstraints. + ON_CALL(*mComposer, isVsyncPeriodSwitchSupported()).WillByDefault(Return(true)); + } + +protected: + void setupScheduler(); + void testChangeRefreshRate(bool isDisplayActive, bool isRefreshRequired); + + sp<DisplayDevice> mDisplay; + mock::EventThread* mAppEventThread; + + const DisplayModeId kDisplayModeId60 = DisplayModeId(0); + const DisplayModePtr kDisplayMode60 = + DisplayMode::Builder(hal::HWConfigId(kDisplayModeId60.value())) + .setId(kDisplayModeId60) + .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get()) + .setVsyncPeriod((60_Hz).getPeriodNsecs()) + .setGroup(0) + .setHeight(1000) + .setWidth(1000) + .build(); + + const DisplayModeId kDisplayModeId90 = DisplayModeId(1); + const DisplayModePtr kDisplayMode90 = + DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90.value())) + .setId(kDisplayModeId90) + .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get()) + .setVsyncPeriod((90_Hz).getPeriodNsecs()) + .setGroup(1) + .setHeight(1000) + .setWidth(1000) + .build(); + + const DisplayModeId kDisplayModeId120 = DisplayModeId(2); + const DisplayModePtr kDisplayMode120 = + DisplayMode::Builder(hal::HWConfigId(kDisplayModeId120.value())) + .setId(kDisplayModeId120) + .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get()) + .setVsyncPeriod((120_Hz).getPeriodNsecs()) + .setGroup(2) + .setHeight(1000) + .setWidth(1000) + .build(); + + const DisplayModeId kDisplayModeId90DifferentResolution = DisplayModeId(3); + const DisplayModePtr kDisplayMode90DifferentResolution = + DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90DifferentResolution.value())) + .setId(kDisplayModeId90DifferentResolution) + .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get()) + .setVsyncPeriod((90_Hz).getPeriodNsecs()) + .setGroup(3) + .setHeight(2000) + .setWidth(2000) + .build(); +}; + +void DisplayModeSwitchingTest::setupScheduler() { + auto eventThread = std::make_unique<mock::EventThread>(); + mAppEventThread = eventThread.get(); + auto sfEventThread = std::make_unique<mock::EventThread>(); + + EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*eventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + 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(TestableSurfaceFlinger::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), /*callback*/ nullptr, + /*hasMultipleModes*/ true); +} + +TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) { + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60); + + mFlinger.onActiveDisplayChanged(mDisplay); + + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f); + + ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60); + + // Verify that next commit will call setActiveConfigWithConstraints in HWC + const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; + EXPECT_CALL(*mComposer, + setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, + hal::HWConfigId(kDisplayModeId90.value()), _, _)) + .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + + mFlinger.commit(); + + Mock::VerifyAndClearExpectations(mComposer); + ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60); + + // Verify that the next commit will complete the mode change and send + // a onModeChanged event to the framework. + + EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90)); + mFlinger.commit(); + Mock::VerifyAndClearExpectations(mAppEventThread); + + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90); +} + +TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) { + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); + + mFlinger.onActiveDisplayChanged(mDisplay); + + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + kDisplayModeId90.value(), true, 0.f, 120.f, 0.f, 120.f); + + ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60); + + // Verify that next commit will call setActiveConfigWithConstraints in HWC + // and complete the mode change. + const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; + EXPECT_CALL(*mComposer, + setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, + hal::HWConfigId(kDisplayModeId90.value()), _, _)) + .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + + EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90)); + + mFlinger.commit(); + + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90); +} + +TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { + // Test that if we call setDesiredDisplayModeSpecs while a previous mode change + // is still being processed the later call will be respected. + + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60); + + mFlinger.onActiveDisplayChanged(mDisplay); + + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f); + + const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; + EXPECT_CALL(*mComposer, + setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, + hal::HWConfigId(kDisplayModeId90.value()), _, _)) + .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + + mFlinger.commit(); + + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + kDisplayModeId120.value(), false, 0.f, 180.f, 0.f, 180.f); + + ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120); + + EXPECT_CALL(*mComposer, + setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, + hal::HWConfigId(kDisplayModeId120.value()), _, _)) + .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + + mFlinger.commit(); + + ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120); + + mFlinger.commit(); + + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId120); +} + +TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) { + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60); + + mFlinger.onActiveDisplayChanged(mDisplay); + + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + kDisplayModeId90DifferentResolution.value(), false, 0.f, + 120.f, 0.f, 120.f); + + ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90DifferentResolution); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60); + + // Verify that next commit will call setActiveConfigWithConstraints in HWC + // and complete the mode change. + const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; + EXPECT_CALL(*mComposer, + setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, + hal::HWConfigId( + kDisplayModeId90DifferentResolution.value()), + _, _)) + .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + + EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true)); + + // Misc expecations. We don't need to enforce these method calls, but since the helper methods + // already set expectations we should add new ones here, otherwise the test will fail. + EXPECT_CALL(*mConsumer, setDefaultBufferSize(2000, 2000)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(hal::Error::NONE)); + + // Create a new native surface to be used by the recreated display. + mNativeWindowSurface = nullptr; + injectFakeNativeWindowSurfaceFactory(); + PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this); + + const auto displayToken = mDisplay->getDisplayToken().promote(); + + mFlinger.commit(); + + // The DisplayDevice will be destroyed and recreated, + // so we need to update with the new instance. + mDisplay = mFlinger.getDisplay(displayToken); + + ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); + ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90DifferentResolution); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp index 69e0501bd6..ec7e8a7f82 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp @@ -17,6 +17,9 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <chrono> +#include <thread> + #include "DisplayTransactionTestHelpers.h" #include <android/hardware/power/Boost.h> @@ -27,6 +30,8 @@ namespace { using android::hardware::power::Boost; TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) { + using namespace std::chrono_literals; + mFlinger.scheduler()->replaceTouchTimer(100); std::this_thread::sleep_for(10ms); // wait for callback to be triggered EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch @@ -47,4 +52,4 @@ TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) { } } // namespace -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 9d1fc981aa..364d8f1411 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -28,22 +28,20 @@ #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" -namespace android { +namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(std::shared_ptr<scheduler::RefreshRateConfigs> configs, - ISchedulerCallback& callback) + TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback) : TestableScheduler(std::make_unique<mock::VsyncController>(), std::make_unique<mock::VSyncTracker>(), std::move(configs), callback) {} - TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, - std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, - std::shared_ptr<scheduler::RefreshRateConfigs> configs, - ISchedulerCallback& callback) - : Scheduler(*this, callback, {.useContentDetection = true}) { - mVsyncSchedule = {std::move(vsyncController), std::move(vsyncTracker), nullptr}; + TestableScheduler(std::unique_ptr<VsyncController> controller, + std::unique_ptr<VSyncTracker> tracker, + std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback) + : Scheduler(*this, callback, Feature::kContentDetection) { + mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); setRefreshRateConfigs(std::move(configs)); ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) { @@ -69,11 +67,16 @@ public: auto& mutableLayerHistory() { return mLayerHistory; } - size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mLayerInfos.size(); } - size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mActiveLayersEnd; } + size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { + return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size(); + } auto refreshRateConfigs() { return holdRefreshRateConfigs(); } + size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS { + return mLayerHistory.mActiveLayerInfos.size(); + } + void replaceTouchTimer(int64_t millis) { if (mTouchTimer) { mTouchTimer.reset(); @@ -86,33 +89,24 @@ public: } bool isTouchActive() { - std::lock_guard<std::mutex> lock(mFeatureStateLock); - return mFeatures.touch == Scheduler::TouchState::Active; + std::lock_guard<std::mutex> lock(mPolicyLock); + return mPolicy.touch == Scheduler::TouchState::Active; } void dispatchCachedReportedMode() { - std::lock_guard<std::mutex> lock(mFeatureStateLock); + std::lock_guard<std::mutex> lock(mPolicyLock); return Scheduler::dispatchCachedReportedMode(); } - void clearOptionalFieldsInFeatures() { - std::lock_guard<std::mutex> lock(mFeatureStateLock); - mFeatures.cachedModeChangedParams.reset(); + void clearCachedReportedMode() { + std::lock_guard<std::mutex> lock(mPolicyLock); + mPolicy.cachedModeChangedParams.reset(); } void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); } - ~TestableScheduler() { - // All these pointer and container clears help ensure that GMock does - // not report a leaked object, since the Scheduler instance may - // still be referenced by something despite our best efforts to destroy - // it after each test is done. - mVsyncSchedule.controller.reset(); - mConnections.clear(); - } - private: // ICompositor overrides: bool commit(nsecs_t, int64_t, nsecs_t) override { return false; } @@ -120,4 +114,4 @@ private: void sample() override {} }; -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 4c5789e47f..100a78d854 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -24,6 +24,7 @@ #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/mock/DisplaySurface.h> #include <gui/ScreenCaptureResults.h> +#include <algorithm> #include "BufferQueueLayer.h" #include "BufferStateLayer.h" @@ -169,12 +170,12 @@ public: } // namespace surfaceflinger::test -class TestableSurfaceFlinger final : private ISchedulerCallback { +class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback { public: using HotplugEvent = SurfaceFlinger::HotplugEvent; SurfaceFlinger* flinger() { return mFlinger.get(); } - TestableScheduler* scheduler() { return mScheduler; } + scheduler::TestableScheduler* scheduler() { return mScheduler; } // Extend this as needed for accessing SurfaceFlinger private (and public) // functions. @@ -197,7 +198,8 @@ public: std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, - ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) { + scheduler::ISchedulerCallback* callback = nullptr, + bool hasMultipleModes = false) { DisplayModes modes{DisplayMode::Builder(0) .setId(DisplayModeId(0)) .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) @@ -224,17 +226,18 @@ public: std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps, /*powerMode=*/hal::PowerMode::OFF); - mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), - mRefreshRateConfigs, *(callback ?: this)); + mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), + std::move(vsyncTracker), mRefreshRateConfigs, + *(callback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); resetScheduler(mScheduler); } - void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } + void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } - TestableScheduler& mutableScheduler() const { return *mScheduler; } + scheduler::TestableScheduler& mutableScheduler() const { return *mScheduler; } using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction; void setCreateBufferQueueFunction(CreateBufferQueueFunction f) { @@ -305,6 +308,11 @@ public: return mFlinger->destroyDisplay(displayToken); } + auto getDisplay(const sp<IBinder>& displayToken) { + Mutex::Autolock lock(mFlinger->mStateLock); + return mFlinger->getDisplayDeviceLocked(displayToken); + } + void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); } auto setupNewDisplayDeviceInternal( @@ -380,7 +388,7 @@ public: listenerCallbacks, transactionId); } - auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); }; + auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); }; auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { return mFlinger->onTransact(code, data, reply, flags); @@ -393,6 +401,21 @@ public: return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } + auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, + bool allowGroupSwitching, float primaryRefreshRateMin, + float primaryRefreshRateMax, float appRequestRefreshRateMin, + float appRequestRefreshRateMax) { + return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching, + primaryRefreshRateMin, primaryRefreshRateMax, + appRequestRefreshRateMin, + appRequestRefreshRateMax); + } + + void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) { + Mutex::Autolock lock(mFlinger->mStateLock); + mFlinger->onActiveDisplayChangedLocked(activeDisplay); + } + /* ------------------------------------------------------------------------ * Read-only access to private data to assert post-conditions. */ @@ -480,7 +503,7 @@ public: static constexpr hal::HWDisplayId DEFAULT_HWC_DISPLAY_ID = 1000; static constexpr int32_t DEFAULT_WIDTH = 1920; static constexpr int32_t DEFAULT_HEIGHT = 1280; - static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'666; + static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667; static constexpr int32_t DEFAULT_CONFIG_GROUP = 7; static constexpr int32_t DEFAULT_DPI = 320; static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0; @@ -634,10 +657,10 @@ public: mCreationArgs.connectionType = connectionType; mCreationArgs.isPrimary = isPrimary; - mActiveModeId = DisplayModeId(0); + mCreationArgs.activeModeId = DisplayModeId(0); DisplayModePtr activeMode = DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG) - .setId(mActiveModeId) + .setId(mCreationArgs.activeModeId) .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH) .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT) @@ -673,7 +696,7 @@ public: auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; } auto& setActiveMode(DisplayModeId mode) { - mActiveModeId = mode; + mCreationArgs.activeModeId = mode; return *this; } @@ -728,14 +751,29 @@ public: const auto physicalId = PhysicalDisplayId::tryCast(*displayId); LOG_ALWAYS_FATAL_IF(!physicalId); LOG_ALWAYS_FATAL_IF(!mHwcDisplayId); - state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId}; + + const DisplayModePtr activeModePtr = + *std::find_if(mCreationArgs.supportedModes.begin(), + mCreationArgs.supportedModes.end(), [&](DisplayModePtr mode) { + return mode->getId() == mCreationArgs.activeModeId; + }); + state.physical = {.id = *physicalId, + .type = *type, + .hwcDisplayId = *mHwcDisplayId, + .deviceProductInfo = {}, + .supportedModes = mCreationArgs.supportedModes, + .activeMode = activeModePtr}; } state.isSecure = mCreationArgs.isSecure; + mCreationArgs.refreshRateConfigs = + std::make_shared<scheduler::RefreshRateConfigs>(mCreationArgs.supportedModes, + mCreationArgs.activeModeId); + sp<DisplayDevice> device = new DisplayDevice(mCreationArgs); if (!device->isVirtual()) { - device->setActiveMode(mActiveModeId); + device->setActiveMode(mCreationArgs.activeModeId); } mFlinger.mutableDisplays().emplace(mDisplayToken, device); mFlinger.mutableCurrentState().displays.add(mDisplayToken, state); @@ -753,19 +791,18 @@ public: sp<BBinder> mDisplayToken = new BBinder(); DisplayDeviceCreationArgs mCreationArgs; const std::optional<hal::HWDisplayId> mHwcDisplayId; - DisplayModeId mActiveModeId; }; private: void scheduleComposite(FrameHint) override {} void setVsyncEnabled(bool) override {} - void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override {} + void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {} void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() {} surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization); - TestableScheduler* mScheduler = nullptr; + scheduler::TestableScheduler* mScheduler = nullptr; std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; }; diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 1487a962f9..0ef8456739 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -70,9 +70,9 @@ using SurfaceflingerStatsLayerInfoWrapper = #define NUM_LAYERS 1 #define NUM_LAYERS_INVALID "INVALID" -const constexpr Fps kRefreshRate0 = 61_Hz; -const constexpr Fps kRenderRate0 = 31_Hz; -static constexpr int32_t kGameMode = TimeStatsHelper::GameModeUnsupported; +constexpr Fps kRefreshRate0 = 61_Hz; +constexpr Fps kRenderRate0 = 31_Hz; +constexpr GameMode kGameMode = GameMode::Unsupported; enum InputCommand : int32_t { ENABLE = 0, @@ -145,14 +145,14 @@ public: std::string inputCommand(InputCommand cmd, bool useProto); void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts, - TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode); + TimeStats::SetFrameRateVote, GameMode); int32_t genRandomInt32(int32_t begin, int32_t end); template <size_t N> void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber, nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}, - int32_t gameMode = kGameMode) { + GameMode gameMode = kGameMode) { for (size_t i = 0; i < N; i++, ts += 1000000) { setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote, gameMode); } @@ -203,7 +203,7 @@ static std::string genLayerName(int32_t layerId) { } void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts, - TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode) { + TimeStats::SetFrameRateVote frameRateVote, GameMode gameMode) { switch (type) { case TimeStamp::POST: ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), @@ -1168,8 +1168,7 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000; EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, - TimeStatsHelper::GameModeStandard); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, GameMode::Standard); for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) { mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire); } @@ -1182,42 +1181,39 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple, .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired, }; - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60, - TimeStatsHelper::GameModeStandard); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60, GameMode::Standard); - mTimeStats->incrementJankyFrames( - {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerCpuDeadlineMissed, - DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); - mTimeStats->incrementJankyFrames( - {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerGpuDeadlineMissed, - DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - TimeStatsHelper::GameModeStandard, JankType::DisplayHAL, + GameMode::Standard, JankType::SurfaceFlingerCpuDeadlineMissed, DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - TimeStatsHelper::GameModeStandard, - JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA, - DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); + GameMode::Standard, JankType::SurfaceFlingerGpuDeadlineMissed, + DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, + APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - TimeStatsHelper::GameModeStandard, - JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA, - DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS}); + GameMode::Standard, JankType::DisplayHAL, + DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, + APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - TimeStatsHelper::GameModeStandard, JankType::PredictionError, + GameMode::Standard, JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, - APP_DEADLINE_DELTA_2MS}); + APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - TimeStatsHelper::GameModeStandard, - JankType::AppDeadlineMissed | JankType::BufferStuffing, - DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS, + GameMode::Standard, JankType::SurfaceFlingerScheduling, + DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - TimeStatsHelper::GameModeStandard, JankType::None, + GameMode::Standard, JankType::PredictionError, DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, - APP_DEADLINE_DELTA_3MS}); + APP_DEADLINE_DELTA_2MS}); + mTimeStats->incrementJankyFrames( + {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), GameMode::Standard, + JankType::AppDeadlineMissed | JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA, + APP_DEADLINE_DELTA_2MS, APP_DEADLINE_DELTA_2MS}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); std::string pulledData; EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); @@ -1293,22 +1289,18 @@ TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) { constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3; EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, - TimeStatsHelper::GameModeStandard); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, GameMode::Standard); for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) { mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire); } for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) { mTimeStats->incrementBadDesiredPresent(LAYER_ID_0); } - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {}, - TimeStatsHelper::GameModeStandard); - - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, - TimeStatsHelper::GameModePerformance); - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, TimeStatsHelper::GameModeBattery); - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, TimeStatsHelper::GameModeBattery); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {}, GameMode::Standard); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery); std::string pulledData; EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index ec19100c2b..16d4b59250 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -26,7 +26,7 @@ #include <log/log.h> #include <ui/MockFence.h> #include <utils/String8.h> -#include "TestableScheduler.h" + #include "TestableSurfaceFlinger.h" #include "mock/MockEventThread.h" #include "mock/MockVsyncController.h" @@ -85,11 +85,8 @@ public: std::move(eventThread), std::move(sfEventThread)); } - TestableScheduler* mScheduler; TestableSurfaceFlinger mFlinger; - std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>(); - mock::VsyncController* mVsyncController = new mock::VsyncController(); mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker(); mock::MockFence* mFenceUnsignaled = new mock::MockFence(); diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp index cebd4519b1..6e00748b45 100644 --- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp @@ -69,16 +69,16 @@ TEST(TransactionProtoParserTest, parse) { t1.displays.add(display); } - std::function<int32_t(const sp<IBinder>&)> getLayerIdFn = [&](const sp<IBinder>& handle) { + TransactionProtoParser::LayerHandleToIdFn getLayerIdFn = [&](const sp<IBinder>& handle) { return (handle == layerHandle) ? 42 : -1; }; - std::function<int32_t(const sp<IBinder>&)> getDisplayIdFn = [&](const sp<IBinder>& handle) { + TransactionProtoParser::DisplayHandleToIdFn getDisplayIdFn = [&](const sp<IBinder>& handle) { return (handle == displayHandle) ? 43 : -1; }; - std::function<sp<IBinder>(int32_t)> getLayerHandleFn = [&](int32_t id) { + TransactionProtoParser::LayerIdToHandleFn getLayerHandleFn = [&](int32_t id) { return (id == 42) ? layerHandle : nullptr; }; - std::function<sp<IBinder>(int32_t)> getDisplayHandleFn = [&](int32_t id) { + TransactionProtoParser::DisplayIdToHandleFn getDisplayHandleFn = [&](int32_t id) { return (id == 43) ? displayHandle : nullptr; }; diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp new file mode 100644 index 0000000000..4e49c18059 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -0,0 +1,296 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <gui/SurfaceComposerClient.h> + +#include "Tracing/RingBuffer.h" +#include "Tracing/TransactionTracing.h" + +using namespace android::surfaceflinger; + +namespace android { + +class TransactionTracingTest : public testing::Test { +protected: + static constexpr size_t SMALL_BUFFER_SIZE = 1024; + std::unique_ptr<android::TransactionTracing> mTracing; + void SetUp() override { mTracing = std::make_unique<android::TransactionTracing>(); } + + void TearDown() override { + mTracing->disable(); + mTracing.reset(); + } + + auto getCommittedTransactions() { + std::scoped_lock<std::mutex> lock(mTracing->mMainThreadLock); + return mTracing->mCommittedTransactions; + } + + auto getQueuedTransactions() { + std::scoped_lock<std::mutex> lock(mTracing->mTraceLock); + return mTracing->mQueuedTransactions; + } + + auto getUsedBufferSize() { + std::scoped_lock<std::mutex> lock(mTracing->mTraceLock); + return mTracing->mBuffer->used(); + } + + auto flush(int64_t vsyncId) { return mTracing->flush(vsyncId); } + + auto bufferFront() { + std::scoped_lock<std::mutex> lock(mTracing->mTraceLock); + return mTracing->mBuffer->front(); + } + + bool threadIsJoinable() { + std::scoped_lock lock(mTracing->mMainThreadLock); + return mTracing->mThread.joinable(); + } + + proto::TransactionTraceFile writeToProto() { return mTracing->writeToProto(); } + + auto getCreatedLayers() { + std::scoped_lock<std::mutex> lock(mTracing->mTraceLock); + return mTracing->mCreatedLayers; + } + + auto getStartingStates() { + std::scoped_lock<std::mutex> lock(mTracing->mTraceLock); + return mTracing->mStartingStates; + } + + void queueAndCommitTransaction(int64_t vsyncId) { + 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); + flush(vsyncId); + } + + // Test that we clean up the tracing thread and free any memory allocated. + void verifyDisabledTracingState() { + EXPECT_FALSE(mTracing->isEnabled()); + EXPECT_FALSE(threadIsJoinable()); + EXPECT_EQ(getCommittedTransactions().size(), 0u); + EXPECT_EQ(getQueuedTransactions().size(), 0u); + EXPECT_EQ(getUsedBufferSize(), 0u); + EXPECT_EQ(getStartingStates().size(), 0u); + } + + void verifyEntry(const proto::TransactionTraceEntry& actualProto, + const std::vector<TransactionState> expectedTransactions, + int64_t expectedVsyncId) { + EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId); + EXPECT_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); + } + } +}; + +TEST_F(TransactionTracingTest, enable) { + EXPECT_FALSE(mTracing->isEnabled()); + mTracing->enable(); + EXPECT_TRUE(mTracing->isEnabled()); + mTracing->disable(); + verifyDisabledTracingState(); +} + +TEST_F(TransactionTracingTest, addTransactions) { + mTracing->enable(); + std::vector<TransactionState> transactions; + transactions.reserve(100); + for (uint64_t i = 0; i < 100; i++) { + TransactionState transaction; + transaction.id = i; + transaction.originPid = static_cast<int32_t>(i); + transactions.emplace_back(transaction); + mTracing->addQueuedTransaction(transaction); + } + + // 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); + + int64_t secondTransactionSetVsyncId = 43; + std::vector<TransactionState> secondTransactionSet = + std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50); + mTracing->addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId); + flush(secondTransactionSetVsyncId); + + proto::TransactionTraceFile proto = writeToProto(); + EXPECT_EQ(proto.entry().size(), 3); + // skip starting entry + verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId); + verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId); + + mTracing->disable(); + verifyDisabledTracingState(); +} + +class TransactionTracingLayerHandlingTest : public TransactionTracingTest { +protected: + void SetUp() override { + TransactionTracingTest::SetUp(); + mTracing->enable(); + // add layers + mTracing->setBufferSize(SMALL_BUFFER_SIZE); + const sp<IBinder> fakeLayerHandle = new BBinder(); + mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent", + 123 /* flags */, -1 /* parentId */); + const sp<IBinder> fakeChildLayerHandle = new BBinder(); + mTracing->onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child", + 456 /* flags */, mParentLayerId); + + // add some layer transaction + { + TransactionState transaction; + transaction.id = 50; + ComposerState layerState; + layerState.state.surface = fakeLayerHandle; + layerState.state.what = layer_state_t::eLayerChanged; + layerState.state.z = 42; + transaction.states.add(layerState); + ComposerState childState; + childState.state.surface = fakeChildLayerHandle; + childState.state.what = layer_state_t::eLayerChanged; + childState.state.z = 43; + transaction.states.add(childState); + mTracing->addQueuedTransaction(transaction); + + std::vector<TransactionState> transactions; + transactions.emplace_back(transaction); + VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId; + mTracing->addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE); + flush(VSYNC_ID_FIRST_LAYER_CHANGE); + } + + // add transactions that modify the layer state further so we can test that layer state + // gets merged + { + TransactionState transaction; + transaction.id = 51; + ComposerState layerState; + layerState.state.surface = fakeLayerHandle; + layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged; + layerState.state.z = 41; + layerState.state.x = 22; + transaction.states.add(layerState); + mTracing->addQueuedTransaction(transaction); + + std::vector<TransactionState> transactions; + transactions.emplace_back(transaction); + VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId; + mTracing->addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE); + flush(VSYNC_ID_SECOND_LAYER_CHANGE); + } + + // remove child layer + mTracing->onLayerRemoved(2); + VSYNC_ID_CHILD_LAYER_REMOVED = ++mVsyncId; + queueAndCommitTransaction(VSYNC_ID_CHILD_LAYER_REMOVED); + + // remove layer + mTracing->onLayerRemoved(1); + queueAndCommitTransaction(++mVsyncId); + } + + void TearDown() override { + mTracing->disable(); + verifyDisabledTracingState(); + TransactionTracingTest::TearDown(); + } + + int mParentLayerId = 1; + int mChildLayerId = 2; + int64_t mVsyncId = 0; + int64_t VSYNC_ID_FIRST_LAYER_CHANGE; + int64_t VSYNC_ID_SECOND_LAYER_CHANGE; + int64_t VSYNC_ID_CHILD_LAYER_REMOVED; +}; + +TEST_F(TransactionTracingLayerHandlingTest, addStartingState) { + // add transactions until we drop the transaction with the first layer change + while (bufferFront().vsync_id() <= VSYNC_ID_FIRST_LAYER_CHANGE) { + queueAndCommitTransaction(++mVsyncId); + } + proto::TransactionTraceFile proto = writeToProto(); + // verify we can still retrieve the layer change from the first entry containing starting + // states. + EXPECT_GT(proto.entry().size(), 0); + EXPECT_GT(proto.entry(0).transactions().size(), 0); + EXPECT_GT(proto.entry(0).added_layers().size(), 0); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).layer_id(), mChildLayerId); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43); +} + +TEST_F(TransactionTracingLayerHandlingTest, updateStartingState) { + // add transactions until we drop the transaction with the second layer change + while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) { + queueAndCommitTransaction(++mVsyncId); + } + proto::TransactionTraceFile proto = writeToProto(); + // verify starting states are updated correctly + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 41); +} + +TEST_F(TransactionTracingLayerHandlingTest, removeStartingState) { + // add transactions until we drop the transaction which removes the child layer + while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) { + queueAndCommitTransaction(++mVsyncId); + } + proto::TransactionTraceFile proto = writeToProto(); + // verify the child layer has been removed from the trace + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId); +} + +TEST_F(TransactionTracingLayerHandlingTest, startingStateSurvivesBufferFlush) { + // add transactions until we drop the transaction with the second layer change + while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) { + queueAndCommitTransaction(++mVsyncId); + } + proto::TransactionTraceFile proto = writeToProto(); + // verify we have two starting states + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2); + + // Continue adding transactions until child layer is removed + while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) { + queueAndCommitTransaction(++mVsyncId); + } + proto = writeToProto(); + // verify we still have the parent layer state + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h index e241dc903f..849e3083c4 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -20,25 +20,22 @@ #include "Scheduler/Scheduler.h" -namespace android::mock { +namespace android::scheduler::mock { struct SchedulerCallback final : ISchedulerCallback { MOCK_METHOD(void, scheduleComposite, (FrameHint), (override)); - MOCK_METHOD1(setVsyncEnabled, void(bool)); - MOCK_METHOD2(changeRefreshRate, - void(const scheduler::RefreshRateConfigs::RefreshRate&, - scheduler::RefreshRateConfigEvent)); - MOCK_METHOD1(kernelTimerChanged, void(bool)); - MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void()); + MOCK_METHOD(void, setVsyncEnabled, (bool), (override)); + MOCK_METHOD(void, changeRefreshRate, (const RefreshRate&, DisplayModeEvent), (override)); + MOCK_METHOD(void, kernelTimerChanged, (bool), (override)); + MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override)); }; struct NoOpSchedulerCallback final : ISchedulerCallback { void scheduleComposite(FrameHint) override {} void setVsyncEnabled(bool) override {} - void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&, - scheduler::RefreshRateConfigEvent) override {} + void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {} void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() {} }; -} // namespace android::mock +} // namespace android::scheduler::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h index 5aebd2f20e..0a69b562ab 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -41,19 +41,21 @@ public: MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t)); MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t)); MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&)); - MOCK_METHOD6(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t, int32_t)); + MOCK_METHOD(void, setPostTime, + (int32_t, uint64_t, const std::string&, uid_t, nsecs_t, GameMode), (override)); MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason)); MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId)); MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t)); MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t)); MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t)); MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&)); - MOCK_METHOD7(setPresentTime, - void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote, - int32_t)); - MOCK_METHOD7(setPresentFence, - void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>, - SetFrameRateVote, int32_t)); + MOCK_METHOD(void, setPresentTime, + (int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote, GameMode), + (override)); + MOCK_METHOD(void, setPresentFence, + (int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>, + SetFrameRateVote, GameMode), + (override)); MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&)); MOCK_METHOD1(onDestroy, void(int32_t)); MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h index 94d99665ce..314f681545 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h @@ -27,7 +27,7 @@ public: VsyncController(); ~VsyncController() override; - MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&)); + 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)); diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp index d1cd397da4..fa3b2601a4 100644 --- a/vulkan/libvulkan/api.cpp +++ b/vulkan/libvulkan/api.cpp @@ -965,6 +965,13 @@ VkResult LayerChain::ValidateExtensions(VkPhysicalDevice physical_dev, VkResult result = EnumerateDeviceExtensionProperties(physical_dev, nullptr, &count, nullptr); if (result == VK_SUCCESS && count) { + // Work-around a race condition during Android start-up, that can result + // in the second call to EnumerateDeviceExtensionProperties having + // another extension. That causes the second call to return + // VK_INCOMPLETE. A work-around is to add 1 to "count" and ask for one + // more extension property. See: http://anglebug.com/6715 and + // internal-to-Google b/206733351. + count++; driver_extensions_ = AllocateDriverExtensionArray(count); result = (driver_extensions_) ? EnumerateDeviceExtensionProperties( diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index e89a49b04c..b5a0bdfe6f 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -537,30 +537,6 @@ android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) { } } -int get_min_buffer_count(ANativeWindow* window, - uint32_t* out_min_buffer_count) { - constexpr int kExtraBuffers = 2; - - int err; - int min_undequeued_buffers; - err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &min_undequeued_buffers); - if (err != android::OK || min_undequeued_buffers < 0) { - ALOGE( - "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " - "value=%d", - strerror(-err), err, min_undequeued_buffers); - if (err == android::OK) { - err = android::UNKNOWN_ERROR; - } - return err; - } - - *out_min_buffer_count = - static_cast<uint32_t>(min_undequeued_buffers + kExtraBuffers); - return android::OK; -} - } // anonymous namespace VKAPI_ATTR @@ -675,7 +651,7 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } - capabilities->minImageCount = max_buffer_count == 1 ? 1 : 2; + capabilities->minImageCount = std::min(max_buffer_count, 3); capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); capabilities->currentExtent = @@ -720,10 +696,10 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, if (err) { return VK_ERROR_SURFACE_LOST_KHR; } - ALOGV("wide_color_support is: %d", wide_color_support); - wide_color_support = - wide_color_support && + bool swapchain_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); + ALOGV("wide_color_support is: %d", wide_color_support); + wide_color_support = wide_color_support && swapchain_ext; AHardwareBuffer_Desc desc = {}; desc.width = 1; @@ -736,8 +712,12 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, // We must support R8G8B8A8 std::vector<VkSurfaceFormatKHR> all_formats = { {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}}; + {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; + + if (swapchain_ext) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); + } if (wide_color_support) { all_formats.emplace_back(VkSurfaceFormatKHR{ @@ -873,13 +853,18 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, int err; int query_value; - uint32_t min_buffer_count; ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); - err = get_min_buffer_count(window, &min_buffer_count); - if (err != android::OK) { + err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &query_value); + if (err != android::OK || query_value < 0) { + ALOGE( + "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " + "value=%d", + strerror(-err), err, query_value); return VK_ERROR_SURFACE_LOST_KHR; } + uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); if (err != android::OK || query_value < 0) { @@ -890,7 +875,7 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, uint32_t max_buffer_count = static_cast<uint32_t>(query_value); std::vector<VkPresentModeKHR> present_modes; - if (min_buffer_count < max_buffer_count) + if (min_undequeued_buffers + 1 < max_buffer_count) present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); @@ -1211,14 +1196,19 @@ VkResult CreateSwapchainKHR(VkDevice device, } } - uint32_t min_buffer_count; - err = get_min_buffer_count(window, &min_buffer_count); - if (err != android::OK) { + int query_value; + err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &query_value); + if (err != android::OK || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, + query_value); return VK_ERROR_SURFACE_LOST_KHR; } - - uint32_t num_images = - std::max(min_buffer_count, create_info->minImageCount); + uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); + const auto mailbox_num_images = std::max(3u, create_info->minImageCount); + const auto requested_images = + swap_interval ? create_info->minImageCount : mailbox_num_images; + uint32_t num_images = requested_images - 1 + min_undequeued_buffers; // Lower layer insists that we have at least two buffers. This is wasteful // and we'd like to relax it in the shared case, but not all the pieces are @@ -1232,12 +1222,6 @@ VkResult CreateSwapchainKHR(VkDevice device, return VK_ERROR_SURFACE_LOST_KHR; } - // In shared mode the num_images must be one regardless of how many - // buffers were allocated for the buffer queue. - if (swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID) { - num_images = 1; - } - int32_t legacy_usage = 0; if (dispatch.GetSwapchainGrallocUsage2ANDROID) { uint64_t consumer_usage, producer_usage; |