Merge "When user presses the power key while playing a local video, the surfaceflinger will crash."
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 8da1352..1c3a4f2 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -11,286 +11,6 @@
# 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
@@ -315,6 +35,8 @@
chmod 0666 /sys/kernel/tracing/events/sched/sched_pi_setprio/enable
chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable
chmod 0666 /sys/kernel/tracing/events/sched/sched_process_exit/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_free/enable
+ chmod 0666 /sys/kernel/tracing/events/sched/sched_process_free/enable
chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_waking/enable
chmod 0666 /sys/kernel/tracing/events/sched/sched_waking/enable
chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 32e680d..e97949e 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1802,8 +1802,8 @@
// Add linker configuration directory
ds.AddDir(LINKERCONFIG_DIR, true);
- /* Dump cgroupfs */
- ds.AddDir(CGROUPFS_DIR, true);
+ /* Dump frozen cgroupfs */
+ dump_frozen_cgroupfs();
if (ds.dump_pool_) {
WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
@@ -4169,6 +4169,63 @@
fclose(fp);
}
+void dump_frozen_cgroupfs(const char *dir, int level,
+ int (*dump_from_fd)(const char* title, const char* path, int fd)) {
+ DIR *dirp;
+ struct dirent *d;
+ char *newpath = nullptr;
+
+ dirp = opendir(dir);
+ if (dirp == nullptr) {
+ MYLOGE("%s: %s\n", dir, strerror(errno));
+ return;
+ }
+
+ for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) {
+ if ((d->d_name[0] == '.')
+ && (((d->d_name[1] == '.') && (d->d_name[2] == '\0'))
+ || (d->d_name[1] == '\0'))) {
+ continue;
+ }
+ if (d->d_type == DT_DIR) {
+ asprintf(&newpath, "%s/%s/", dir, d->d_name);
+ if (!newpath) {
+ continue;
+ }
+ if (level == 0 && !strncmp(d->d_name, "uid_", 4)) {
+ dump_frozen_cgroupfs(newpath, 1, dump_from_fd);
+ } else if (level == 1 && !strncmp(d->d_name, "pid_", 4)) {
+ char *freezer = nullptr;
+ asprintf(&freezer, "%s/%s", newpath, "cgroup.freeze");
+ if (freezer) {
+ FILE* fp = fopen(freezer, "r");
+ if (fp != NULL) {
+ int frozen;
+ fscanf(fp, "%d", &frozen);
+ if (frozen > 0) {
+ dump_files("", newpath, skip_none, dump_from_fd);
+ }
+ fclose(fp);
+ }
+ free(freezer);
+ }
+ }
+ }
+ }
+ closedir(dirp);
+}
+
+void dump_frozen_cgroupfs() {
+ if (!ds.IsZipping()) {
+ MYLOGD("Not adding cgroupfs because it's not a zipped bugreport\n");
+ return;
+ }
+ MYLOGD("Adding frozen processes from %s\n", CGROUPFS_DIR);
+ DurationReporter duration_reporter("FROZEN CGROUPFS");
+ if (PropertiesHelper::IsDryRun()) return;
+ dump_frozen_cgroupfs(CGROUPFS_DIR, 0, _add_file_from_fd);
+}
+
void Dumpstate::UpdateProgress(int32_t delta_sec) {
if (progress_ == nullptr) {
MYLOGE("UpdateProgress: progress_ not set\n");
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 773e292..852b9a8 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -637,6 +637,9 @@
/* Prints the contents of all the routing tables, both IPv4 and IPv6. */
void dump_route_tables();
+/* Dump subdirectories of cgroupfs if the corresponding process is frozen */
+void dump_frozen_cgroupfs();
+
/* Play a sound via Stagefright */
void play_sound(const char *path);
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 0712c0a..28e5ee2 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -240,11 +240,11 @@
EXPECT_GE(st.st_size, 1000000 /* 1MB */);
}
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
- EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
- << duration.count() << " s.";
+TEST_F(ZippedBugreportGenerationTest, TakesBetween20And300Seconds) {
+ EXPECT_GE(duration, 20s) << "Expected completion in more than 20s. Actual time "
+ << duration.count() << " ms.";
EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
- << duration.count() << " s.";
+ << duration.count() << " ms.";
}
/**
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index faa8485..00babc3 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -28,6 +28,7 @@
"dexopt.cpp",
"execv_helper.cpp",
"globals.cpp",
+ "restorable_file.cpp",
"run_dex2oat.cpp",
"unique_file.cpp",
"utils.cpp",
@@ -80,7 +81,7 @@
"-cert-err58-cpp",
],
tidy_flags: [
- "-warnings-as-errors=clang-analyzer-security*,cert-*"
+ "-warnings-as-errors=clang-analyzer-security*,cert-*",
],
}
@@ -132,7 +133,10 @@
"unique_file.cpp",
"execv_helper.cpp",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"server_configurable_flags",
@@ -170,7 +174,7 @@
// Needs to be wherever installd is as it's execed by
// installd.
- required: [ "migrate_legacy_obb_data.sh" ],
+ required: ["migrate_legacy_obb_data.sh"],
}
// OTA chroot tool
@@ -194,7 +198,7 @@
"libutils",
],
required: [
- "apexd"
+ "apexd",
],
}
@@ -213,7 +217,7 @@
name: "libotapreoptparameters",
cflags: [
"-Wall",
- "-Werror"
+ "-Werror",
],
srcs: ["otapreopt_parameters.cpp"],
@@ -237,7 +241,7 @@
name: "otapreopt",
cflags: [
"-Wall",
- "-Werror"
+ "-Werror",
],
srcs: [
@@ -246,6 +250,7 @@
"globals.cpp",
"otapreopt.cpp",
"otapreopt_utils.cpp",
+ "restorable_file.cpp",
"run_dex2oat.cpp",
"unique_file.cpp",
"utils.cpp",
@@ -296,5 +301,5 @@
// Script to migrate legacy obb data.
sh_binary {
name: "migrate_legacy_obb_data.sh",
- src: "migrate_legacy_obb_data.sh"
+ src: "migrate_legacy_obb_data.sh",
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 94c4c8c..c3256fc 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -419,10 +419,17 @@
int res = 0;
char* before = nullptr;
char* after = nullptr;
+ if (!existing) {
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
+ SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
+ PLOG(ERROR) << "Failed recursive restorecon for " << path;
+ goto fail;
+ }
+ return res;
+ }
// Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
// libselinux. Not needed here.
-
if (lgetfilecon(path.c_str(), &before) < 0) {
PLOG(ERROR) << "Failed before getfilecon for " << path;
goto fail;
@@ -459,12 +466,6 @@
return res;
}
-static int restorecon_app_data_lazy(const std::string& parent, const char* name,
- const std::string& seInfo, uid_t uid, bool existing) {
- return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid,
- existing);
-}
-
static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
PLOG(ERROR) << "Failed to prepare " << path;
@@ -610,8 +611,14 @@
int32_t uid, int32_t* previousUid, int32_t cacheGid,
const std::string& seInfo, mode_t targetMode) {
struct stat st{};
- bool existing = (stat(path.c_str(), &st) == 0);
- if (existing) {
+ bool parent_dir_exists = (stat(path.c_str(), &st) == 0);
+
+ auto cache_path = StringPrintf("%s/%s", path.c_str(), "cache");
+ auto code_cache_path = StringPrintf("%s/%s", path.c_str(), "code_cache");
+ bool cache_exists = (access(cache_path.c_str(), F_OK) == 0);
+ bool code_cache_exists = (access(code_cache_path.c_str(), F_OK) == 0);
+
+ if (parent_dir_exists) {
if (*previousUid < 0) {
// If previousAppId is -1 in CreateAppDataArgs, we will assume the current owner
// of the directory as previousUid. This is required because it is not always possible
@@ -625,6 +632,7 @@
}
}
+ // Prepare only the parent app directory
if (prepare_app_dir(path, targetMode, uid) ||
prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
@@ -632,12 +640,23 @@
}
// Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+ if (restorecon_app_data_lazy(path, seInfo, uid, parent_dir_exists)) {
return error("Failed to restorecon " + path);
}
+ // If the parent dir exists, the restorecon would already have been done
+ // as a part of the recursive restorecon above
+ if (parent_dir_exists && !cache_exists
+ && restorecon_app_data_lazy(cache_path, seInfo, uid, false)) {
+ return error("Failed to restorecon " + cache_path);
+ }
+
+ // If the parent dir exists, the restorecon would already have been done
+ // as a part of the recursive restorecon above
+ if (parent_dir_exists && !code_cache_exists
+ && restorecon_app_data_lazy(code_cache_path, seInfo, uid, false)) {
+ return error("Failed to restorecon " + code_cache_path);
+ }
return ok();
}
@@ -1112,16 +1131,15 @@
}
static int32_t copy_directory_recursive(const char* from, const char* to) {
- char *argv[] = {
- (char*) kCpPath,
- (char*) "-F", /* delete any existing destination file first (--remove-destination) */
- (char*) "-p", /* preserve timestamps, ownership, and permissions */
- (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
- (char*) "-P", /* Do not follow symlinks [default] */
- (char*) "-d", /* don't dereference symlinks */
- (char*) from,
- (char*) to
- };
+ char* argv[] =
+ {(char*)kCpPath,
+ (char*)"-F", /* delete any existing destination file first (--remove-destination) */
+ (char*)"--preserve=mode,ownership,timestamps,xattr", /* preserve properties */
+ (char*)"-R", /* recurse into subdirectories (DEST must be a directory) */
+ (char*)"-P", /* Do not follow symlinks [default] */
+ (char*)"-d", /* don't dereference symlinks */
+ (char*)from,
+ (char*)to};
LOG(DEBUG) << "Copying " << from << " to " << to;
return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 2bcf2d4..c796da6 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -58,9 +58,10 @@
#include "dexopt_return_codes.h"
#include "execv_helper.h"
#include "globals.h"
-#include "installd_deps.h"
#include "installd_constants.h"
+#include "installd_deps.h"
#include "otapreopt_utils.h"
+#include "restorable_file.h"
#include "run_dex2oat.h"
#include "unique_file.h"
#include "utils.h"
@@ -243,7 +244,7 @@
// The location is the profile name for primary apks or the dex path for secondary dex files.
bool clear_primary_current_profiles(const std::string& package_name, const std::string& location) {
bool success = true;
- // For secondary dex files, we don't really need the user but we use it for sanity checks.
+ // For secondary dex files, we don't really need the user but we use it for validity checks.
std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
for (auto user : users) {
success &= clear_current_profile(package_name, location, user, /*is_secondary_dex*/false);
@@ -309,12 +310,6 @@
return profile_boot_class_path == "true";
}
-static void UnlinkIgnoreResult(const std::string& path) {
- if (unlink(path.c_str()) < 0) {
- PLOG(ERROR) << "Failed to unlink " << path;
- }
-}
-
/*
* Whether dexopt should use a swap file when compiling an APK.
*
@@ -473,7 +468,7 @@
*reference_profile_fd = open_reference_profile(uid, package_name, location,
/*read_write*/ true, is_secondary_dex);
- // For secondary dex files, we don't really need the user but we use it for sanity checks.
+ // For secondary dex files, we don't really need the user but we use it for validity checks.
// Note: the user owning the dex file should be the current user.
std::vector<userid_t> users;
if (is_secondary_dex){
@@ -988,42 +983,34 @@
}
// (re)Creates the app image if needed.
-UniqueFile maybe_open_app_image(const std::string& out_oat_path,
- bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
-
+RestorableFile maybe_open_app_image(const std::string& out_oat_path, bool generate_app_image,
+ bool is_public, int uid, bool is_secondary_dex) {
const std::string image_path = create_image_filename(out_oat_path);
if (image_path.empty()) {
// Happens when the out_oat_path has an unknown extension.
- return UniqueFile();
+ return RestorableFile();
}
- // In case there is a stale image, remove it now. Ignore any error.
- unlink(image_path.c_str());
-
// Not enabled, exit.
if (!generate_app_image) {
- return UniqueFile();
+ RestorableFile::RemoveAllFiles(image_path);
+ return RestorableFile();
}
std::string app_image_format = GetProperty("dalvik.vm.appimageformat", "");
if (app_image_format.empty()) {
- return UniqueFile();
+ RestorableFile::RemoveAllFiles(image_path);
+ return RestorableFile();
}
- // Recreate is true since we do not want to modify a mapped image. If the app is
- // already running and we modify the image file, it can cause crashes (b/27493510).
- UniqueFile image_file(
- open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
- image_path,
- UnlinkIgnoreResult);
+ // If the app is already running and we modify the image file, it can cause crashes
+ // (b/27493510).
+ RestorableFile image_file = RestorableFile::CreateWritableFile(image_path,
+ /*permissions*/ 0600);
if (image_file.fd() < 0) {
// Could not create application image file. Go on since we can compile without it.
LOG(ERROR) << "installd could not create '" << image_path
<< "' for image file during dexopt";
- // If we have a valid image file path but no image fd, explicitly erase the image file.
- if (unlink(image_path.c_str()) < 0) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "Couldn't unlink image file " << image_path;
- }
- }
+ // If we have a valid image file path but cannot create tmp file, reset it.
+ image_file.reset();
} else if (!set_permissions_and_ownership(
image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
@@ -1097,9 +1084,9 @@
// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
// out_vdex_wrapper. Returns true for success or false in case of errors.
bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed,
- const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
- bool profile_guided, UniqueFile* in_vdex_wrapper,
- UniqueFile* out_vdex_wrapper) {
+ const char* instruction_set, bool is_public, int uid,
+ bool is_secondary_dex, bool profile_guided,
+ UniqueFile* in_vdex_wrapper, RestorableFile* out_vdex_wrapper) {
CHECK(in_vdex_wrapper != nullptr);
CHECK(out_vdex_wrapper != nullptr);
// Open the existing VDEX. We do this before creating the new output VDEX, which will
@@ -1114,6 +1101,14 @@
return false;
}
+ // Create work file first. All files will be deleted when it fails.
+ *out_vdex_wrapper = RestorableFile::CreateWritableFile(out_vdex_path_str,
+ /*permissions*/ 0644);
+ if (out_vdex_wrapper->fd() < 0) {
+ ALOGE("installd cannot open vdex '%s' during dexopt\n", out_vdex_path_str.c_str());
+ return false;
+ }
+
bool update_vdex_in_place = false;
if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
// Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
@@ -1145,41 +1140,19 @@
(dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) &&
!profile_guided;
if (update_vdex_in_place) {
+ // dex2oat marks it invalid anyway. So delete it and set work file fd.
+ unlink(in_vdex_path_str.c_str());
// Open the file read-write to be able to update it.
- in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0),
- in_vdex_path_str);
- if (in_vdex_wrapper->fd() == -1) {
- // If we failed to open the file, we cannot update it in place.
- update_vdex_in_place = false;
- }
+ in_vdex_wrapper->reset(out_vdex_wrapper->fd(), in_vdex_path_str);
+ // Disable auto close for the in wrapper fd (it will be done when destructing the out
+ // wrapper).
+ in_vdex_wrapper->DisableAutoClose();
} else {
in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0),
in_vdex_path_str);
}
}
- // If we are updating the vdex in place, we do not need to recreate a vdex,
- // and can use the same existing one.
- if (update_vdex_in_place) {
- // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
- // have bogus stale vdex files.
- out_vdex_wrapper->reset(
- in_vdex_wrapper->fd(),
- out_vdex_path_str,
- UnlinkIgnoreResult);
- // Disable auto close for the in wrapper fd (it will be done when destructing the out
- // wrapper).
- in_vdex_wrapper->DisableAutoClose();
- } else {
- out_vdex_wrapper->reset(
- open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
- out_vdex_path_str,
- UnlinkIgnoreResult);
- if (out_vdex_wrapper->fd() < 0) {
- ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
- return false;
- }
- }
if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid,
out_vdex_path_str.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
@@ -1191,16 +1164,13 @@
}
// Opens the output oat file for the given apk.
-UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir,
- bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) {
+RestorableFile open_oat_out_file(const char* apk_path, const char* oat_dir, bool is_public, int uid,
+ const char* instruction_set, bool is_secondary_dex) {
char out_oat_path[PKG_PATH_MAX];
if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
- return UniqueFile();
+ return RestorableFile();
}
- UniqueFile oat(
- open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
- out_oat_path,
- UnlinkIgnoreResult);
+ RestorableFile oat = RestorableFile::CreateWritableFile(out_oat_path, /*permissions*/ 0644);
if (oat.fd() < 0) {
PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path;
} else if (!set_permissions_and_ownership(
@@ -1839,6 +1809,7 @@
if (sec_dex_result == kSecondaryDexOptProcessOk) {
oat_dir = oat_dir_str.c_str();
if (dexopt_needed == NO_DEXOPT_NEEDED) {
+ *completed = true;
return 0; // Nothing to do, report success.
}
} else if (sec_dex_result == kSecondaryDexOptProcessCancelled) {
@@ -1874,8 +1845,8 @@
}
// Create the output OAT file.
- UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid,
- instruction_set, is_secondary_dex);
+ RestorableFile out_oat =
+ open_oat_out_file(dex_path, oat_dir, is_public, uid, instruction_set, is_secondary_dex);
if (out_oat.fd() < 0) {
*error_msg = "Could not open out oat file.";
return -1;
@@ -1883,7 +1854,7 @@
// Open vdex files.
UniqueFile in_vdex;
- UniqueFile out_vdex;
+ RestorableFile out_vdex;
if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed,
instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex,
&out_vdex)) {
@@ -1919,8 +1890,8 @@
}
// Create the app image file if needed.
- UniqueFile out_image = maybe_open_app_image(
- out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex);
+ RestorableFile out_image = maybe_open_app_image(out_oat.path(), generate_app_image, is_public,
+ uid, is_secondary_dex);
UniqueFile dex_metadata;
if (dex_metadata_path != nullptr) {
@@ -1953,30 +1924,18 @@
LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
RunDex2Oat runner(dex2oat_bin, execv_helper.get());
- runner.Initialize(out_oat,
- out_vdex,
- out_image,
- in_dex,
- in_vdex,
- dex_metadata,
- reference_profile,
- class_loader_context,
- join_fds(context_input_fds),
- swap_fd.get(),
- instruction_set,
- compiler_filter,
- debuggable,
- boot_complete,
- for_restore,
- target_sdk_version,
- enable_hidden_api_checks,
- generate_compact_dex,
- use_jitzygote_image,
+ runner.Initialize(out_oat.GetUniqueFile(), out_vdex.GetUniqueFile(), out_image.GetUniqueFile(),
+ in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context,
+ join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
+ debuggable, boot_complete, for_restore, target_sdk_version,
+ enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
compilation_reason);
bool cancelled = false;
pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
if (cancelled) {
+ *completed = false;
+ reference_profile.DisableCleanup();
return 0;
}
if (pid == 0) {
@@ -2004,6 +1963,7 @@
LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- cancelled";
// cancelled, not an error
*completed = false;
+ reference_profile.DisableCleanup();
return 0;
}
LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x"
@@ -2013,13 +1973,42 @@
}
}
- // TODO(b/156537504) Implement SWAP of completed files
- // We've been successful, don't delete output.
- out_oat.DisableCleanup();
- out_vdex.DisableCleanup();
- out_image.DisableCleanup();
+ // dex2oat ran successfully, so profile is safe to keep.
reference_profile.DisableCleanup();
+ // We've been successful, commit work files.
+ // If committing (=renaming tmp to regular) fails, try to restore backup files.
+ // If restoring fails as well, as a last resort, remove all files.
+ if (!out_oat.CreateBackupFile() || !out_vdex.CreateBackupFile() ||
+ !out_image.CreateBackupFile()) {
+ // Renaming failure can mean that the original file may not be accessible from installd.
+ LOG(ERROR) << "Cannot create backup file from existing file, file in wrong state?"
+ << ", out_oat:" << out_oat.path() << " ,out_vdex:" << out_vdex.path()
+ << " ,out_image:" << out_image.path();
+ out_oat.ResetAndRemoveAllFiles();
+ out_vdex.ResetAndRemoveAllFiles();
+ out_image.ResetAndRemoveAllFiles();
+ return -1;
+ }
+ if (!out_oat.CommitWorkFile() || !out_vdex.CommitWorkFile() || !out_image.CommitWorkFile()) {
+ LOG(ERROR) << "Cannot commit, out_oat:" << out_oat.path()
+ << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path();
+ if (!out_oat.RestoreBackupFile() || !out_vdex.RestoreBackupFile() ||
+ !out_image.RestoreBackupFile()) {
+ LOG(ERROR) << "Cannot cancel commit, out_oat:" << out_oat.path()
+ << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path();
+ // Restoring failed.
+ out_oat.ResetAndRemoveAllFiles();
+ out_vdex.ResetAndRemoveAllFiles();
+ out_image.ResetAndRemoveAllFiles();
+ }
+ return -1;
+ }
+ // Now remove remaining backup files.
+ out_oat.RemoveBackupFile();
+ out_vdex.RemoveBackupFile();
+ out_image.RemoveBackupFile();
+
*completed = true;
return 0;
}
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 6aa32b8..e978e79 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -140,8 +140,8 @@
PrepareEnvironmentVariables();
- if (!EnsureBootImageAndDalvikCache()) {
- LOG(ERROR) << "Bad boot image.";
+ if (!EnsureDalvikCache()) {
+ LOG(ERROR) << "Bad dalvik cache.";
return 5;
}
@@ -349,8 +349,8 @@
}
}
- // Ensure that we have the right boot image and cache file structures.
- bool EnsureBootImageAndDalvikCache() const {
+ // Ensure that we have the right cache file structures.
+ bool EnsureDalvikCache() const {
if (parameters_.instruction_set == nullptr) {
LOG(ERROR) << "Instruction set missing.";
return false;
@@ -376,15 +376,6 @@
}
}
- // Check whether we have a boot image.
- // TODO: check that the files are correct wrt/ jars.
- std::string preopted_boot_art_path =
- StringPrintf("/apex/com.android.art/javalib/%s/boot.art", isa);
- if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
- PLOG(ERROR) << "Bad access() to " << preopted_boot_art_path;
- return false;
- }
-
return true;
}
diff --git a/cmds/installd/restorable_file.cpp b/cmds/installd/restorable_file.cpp
new file mode 100644
index 0000000..fd54a23
--- /dev/null
+++ b/cmds/installd/restorable_file.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#include "restorable_file.h"
+
+#include <string>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace {
+
+constexpr char kTmpFileSuffix[] = ".tmp";
+constexpr char kBackupFileSuffix[] = ".backup";
+
+std::string GetTmpFilePath(const std::string& path) {
+ return android::base::StringPrintf("%s%s", path.c_str(), kTmpFileSuffix);
+}
+
+std::string GetBackupFilePath(const std::string& path) {
+ return android::base::StringPrintf("%s%s", path.c_str(), kBackupFileSuffix);
+}
+
+void UnlinkPossiblyNonExistingFile(const std::string& path) {
+ if (unlink(path.c_str()) < 0) {
+ if (errno != ENOENT && errno != EROFS) { // EROFS reported even if it does not exist.
+ PLOG(ERROR) << "Cannot unlink: " << path;
+ }
+ }
+}
+
+// Check if file for the given path exists
+bool FileExists(const std::string& path) {
+ struct stat st;
+ return ::stat(path.c_str(), &st) == 0;
+}
+
+} // namespace
+
+namespace android {
+namespace installd {
+
+RestorableFile::RestorableFile() : RestorableFile(-1, "") {}
+
+RestorableFile::RestorableFile(int value, const std::string& path) : unique_file_(value, path) {
+ // As cleanup is null, this does not make much difference but use unique_file_ only for closing
+ // tmp file.
+ unique_file_.DisableCleanup();
+}
+
+RestorableFile::~RestorableFile() {
+ reset();
+}
+
+void RestorableFile::reset() {
+ // need to copy before reset clears it.
+ std::string path(unique_file_.path());
+ unique_file_.reset();
+ if (!path.empty()) {
+ UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+ }
+}
+
+bool RestorableFile::CreateBackupFile() {
+ if (path().empty() || !FileExists(path())) {
+ return true;
+ }
+ std::string backup = GetBackupFilePath(path());
+ UnlinkPossiblyNonExistingFile(backup);
+ if (rename(path().c_str(), backup.c_str()) < 0) {
+ PLOG(ERROR) << "Cannot rename " << path() << " to " << backup;
+ return false;
+ }
+ return true;
+}
+
+bool RestorableFile::CommitWorkFile() {
+ std::string path(unique_file_.path());
+ // Keep the path with Commit for debugging purpose.
+ unique_file_.reset(-1, path);
+ if (!path.empty()) {
+ if (rename(GetTmpFilePath(path).c_str(), path.c_str()) < 0) {
+ PLOG(ERROR) << "Cannot rename " << GetTmpFilePath(path) << " to " << path;
+ // Remove both files as renaming can fail due to the original file as well.
+ UnlinkPossiblyNonExistingFile(path);
+ UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool RestorableFile::RestoreBackupFile() {
+ std::string backup = GetBackupFilePath(path());
+ if (path().empty() || !FileExists(backup)) {
+ return true;
+ }
+ UnlinkPossiblyNonExistingFile(path());
+ if (rename(backup.c_str(), path().c_str()) < 0) {
+ PLOG(ERROR) << "Cannot rename " << backup << " to " << path();
+ return false;
+ }
+ return true;
+}
+
+void RestorableFile::RemoveBackupFile() {
+ UnlinkPossiblyNonExistingFile(GetBackupFilePath(path()));
+}
+
+const UniqueFile& RestorableFile::GetUniqueFile() const {
+ return unique_file_;
+}
+
+void RestorableFile::ResetAndRemoveAllFiles() {
+ std::string path(unique_file_.path());
+ reset();
+ RemoveAllFiles(path);
+}
+
+RestorableFile RestorableFile::CreateWritableFile(const std::string& path, int permissions) {
+ std::string tmp_file_path = GetTmpFilePath(path);
+ // If old tmp file exists, delete it.
+ UnlinkPossiblyNonExistingFile(tmp_file_path);
+ int fd = -1;
+ if (!path.empty()) {
+ fd = open(tmp_file_path.c_str(), O_RDWR | O_CREAT, permissions);
+ if (fd < 0) {
+ PLOG(ERROR) << "Cannot create file: " << tmp_file_path;
+ }
+ }
+ RestorableFile rf(fd, path);
+ return rf;
+}
+
+void RestorableFile::RemoveAllFiles(const std::string& path) {
+ UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+ UnlinkPossiblyNonExistingFile(GetBackupFilePath(path));
+ UnlinkPossiblyNonExistingFile(path);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/restorable_file.h b/cmds/installd/restorable_file.h
new file mode 100644
index 0000000..eda2292
--- /dev/null
+++ b/cmds/installd/restorable_file.h
@@ -0,0 +1,107 @@
+/*
+ * 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_INSTALLD_RESTORABLE_FILE_H
+#define ANDROID_INSTALLD_RESTORABLE_FILE_H
+
+#include <functional>
+#include <string>
+
+#include "unique_file.h"
+
+namespace android {
+namespace installd {
+
+// This is a file abstraction which allows restoring to the original file while temporary work
+// file is updated.
+//
+// Typical flow for this API will be:
+// RestorableFile rf = RestorableFile::CreateWritableFile(...)
+// write to file using file descriptor acquired from: rf.fd()
+// Make work file into a regular file with: rf.CommitWorkFile()
+// Or throw away the work file by destroying the instance without calling CommitWorkFile().
+// The temporary work file is closed / removed when an instance is destroyed without calling
+// CommitWorkFile(). The original file, if CommitWorkFile() is not called, will be kept.
+//
+// For safer restoration of original file when commit fails, following 3 steps can be taken:
+// 1. CreateBackupFile(): This renames an existing regular file into a separate backup file.
+// 2. CommitWorkFile(): Rename the work file into the regular file.
+// 3. RemoveBackupFile(): Removes the backup file
+// If CommitWorkFile fails, client can call RestoreBackupFile() which will restore regular file from
+// the backup.
+class RestorableFile {
+public:
+ // Creates invalid instance with no fd (=-1) and empty path.
+ RestorableFile();
+ RestorableFile(RestorableFile&& other) = default;
+ ~RestorableFile();
+
+ // Passes all contents of other file into the current file.
+ // Files kept for the current file will be either deleted or committed depending on
+ // CommitWorkFile() and DisableCleanUp() calls made before this.
+ RestorableFile& operator=(RestorableFile&& other) = default;
+
+ // Gets file descriptor for backing work (=temporary) file. If work file does not exist, it will
+ // return -1.
+ int fd() const { return unique_file_.fd(); }
+
+ // Gets the path name for the regular file (not temporary file).
+ const std::string& path() const { return unique_file_.path(); }
+
+ // Closes work file, deletes it and resets all internal states into default states.
+ void reset();
+
+ // Closes work file and closes all files including work file, backup file and regular file.
+ void ResetAndRemoveAllFiles();
+
+ // Creates a backup file by renaming existing regular file. This will return false if renaming
+ // fails. If regular file for renaming does not exist, it will return true.
+ bool CreateBackupFile();
+
+ // Closes existing work file and makes it a regular file.
+ // Note that the work file is closed and fd() will return -1 after this. path() will still
+ // return the original path.
+ // This will return false when committing fails (=cannot rename). Both the regular file and tmp
+ // file will be deleted when it fails.
+ bool CommitWorkFile();
+
+ // Cancels the commit and restores the backup file into the regular one. If renaming fails,
+ // it will return false. This returns true if the backup file does not exist.
+ bool RestoreBackupFile();
+
+ // Removes the backup file.
+ void RemoveBackupFile();
+
+ // Gets UniqueFile with the same path and fd() pointing to the work file.
+ const UniqueFile& GetUniqueFile() const;
+
+ // Creates writable RestorableFile. This involves creating tmp file for writing.
+ static RestorableFile CreateWritableFile(const std::string& path, int permissions);
+
+ // Removes the specified file together with tmp file generated as RestorableFile.
+ static void RemoveAllFiles(const std::string& path);
+
+private:
+ RestorableFile(int value, const std::string& path);
+
+ // Used as a storage for work file fd and path string.
+ UniqueFile unique_file_;
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_RESTORABLE_FILE_H
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index b661684..51c4589 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -50,10 +50,6 @@
static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
-// Location of the JIT Zygote image.
-static const char* kJitZygoteImage =
- "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
-
std::vector<std::string> SplitBySpaces(const std::string& str) {
if (str.empty()) {
return {};
@@ -84,9 +80,9 @@
int target_sdk_version,
bool enable_hidden_api_checks,
bool generate_compact_dex,
- bool use_jitzygote_image,
+ bool use_jitzygote,
const char* compilation_reason) {
- PrepareBootImageFlags(use_jitzygote_image);
+ PrepareBootImageFlags(use_jitzygote);
PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
dex_metadata, profile, swap_fd, class_loader_context,
@@ -112,14 +108,14 @@
RunDex2Oat::~RunDex2Oat() {}
-void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote_image) {
- std::string boot_image;
- if (use_jitzygote_image) {
- boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
+void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote) {
+ if (use_jitzygote) {
+ // Don't pass a boot image because JIT Zygote should decide which image to use. Typically,
+ // it does not use any boot image on disk.
+ AddArg("--force-jit-zygote");
} else {
- boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
+ AddArg(MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"));
}
- AddArg(boot_image);
}
void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 475e124..559244f 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -50,13 +50,13 @@
int target_sdk_version,
bool enable_hidden_api_checks,
bool generate_compact_dex,
- bool use_jitzygote_image,
+ bool use_jitzygote,
const char* compilation_reason);
void Exec(int exit_code);
protected:
- void PrepareBootImageFlags(bool use_jitzygote_image);
+ void PrepareBootImageFlags(bool use_jitzygote);
void PrepareInputFileFlags(const UniqueFile& output_oat,
const UniqueFile& output_vdex,
const UniqueFile& output_image,
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 0a638cd..2a8135a 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -114,7 +114,7 @@
int target_sdk_version = 0;
bool enable_hidden_api_checks = false;
bool generate_compact_dex = true;
- bool use_jitzygote_image = false;
+ bool use_jitzygote = false;
const char* compilation_reason = nullptr;
};
@@ -175,6 +175,7 @@
default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
+ default_expected_flags_["--boot-image"] = FLAG_UNUSED;
// Arch
default_expected_flags_["--instruction-set"] = "=arm64";
@@ -190,6 +191,7 @@
default_expected_flags_["--max-image-block-size"] = FLAG_UNUSED;
default_expected_flags_["--very-large-app-threshold"] = FLAG_UNUSED;
default_expected_flags_["--resolve-startup-const-strings"] = FLAG_UNUSED;
+ default_expected_flags_["--force-jit-zygote"] = FLAG_UNUSED;
// Debug
default_expected_flags_["--debuggable"] = FLAG_UNUSED;
@@ -256,7 +258,7 @@
args->target_sdk_version,
args->enable_hidden_api_checks,
args->generate_compact_dex,
- args->use_jitzygote_image,
+ args->use_jitzygote,
args->compilation_reason);
runner.Exec(/*exit_code=*/ 0);
}
@@ -557,5 +559,22 @@
VerifyExpectedFlags();
}
+TEST_F(RunDex2OatTest, UseJitZygoteImage) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->use_jitzygote = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--force-jit-zygote", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, BootImage) {
+ setSystemProperty("dalvik.vm.boot-image", "foo.art:bar.art");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--boot-image", "=foo.art:bar.art");
+ VerifyExpectedFlags();
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 4cde7e3..51f7716 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -188,3 +188,23 @@
"libotapreoptparameters",
],
}
+
+cc_test {
+ name: "installd_file_test",
+ test_suites: ["device-tests"],
+ clang: true,
+ srcs: ["installd_file_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libutils",
+ ],
+ static_libs: [
+ "libinstalld",
+ "liblog",
+ ],
+}
diff --git a/cmds/installd/tests/installd_file_test.cpp b/cmds/installd/tests/installd_file_test.cpp
new file mode 100644
index 0000000..00fb308
--- /dev/null
+++ b/cmds/installd/tests/installd_file_test.cpp
@@ -0,0 +1,521 @@
+/*
+ * 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.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "restorable_file.h"
+#include "unique_file.h"
+#include "utils.h"
+
+#undef LOG_TAG
+#define LOG_TAG "installd_file_test"
+
+namespace {
+
+constexpr char kFileTestDir[] = "/data/local/tmp/installd_file_test_data";
+constexpr char kTmpFileSuffix[] = ".tmp";
+constexpr char kBackupFileSuffix[] = ".backup";
+
+void UnlinkWithAssert(const std::string& path) {
+ ASSERT_EQ(0, unlink(path.c_str()));
+}
+
+} // namespace
+
+namespace android {
+namespace installd {
+
+// Add these as macros as functions make it hard to tell where the failure has happened.
+#define ASSERT_FILE_NOT_EXISTING(path) \
+ { \
+ struct stat st; \
+ ASSERT_NE(0, ::stat(path.c_str(), &st)); \
+ }
+#define ASSERT_FILE_EXISTING(path) \
+ { \
+ struct stat st; \
+ ASSERT_EQ(0, ::stat(path.c_str(), &st)); \
+ }
+#define ASSERT_FILE_CONTENT(path, expectedContent) ASSERT_EQ(expectedContent, ReadTestFile(path))
+#define ASSERT_FILE_OPEN(path, fd) \
+ { \
+ fd = open(path.c_str(), O_RDWR); \
+ ASSERT_TRUE(fd >= 0); \
+ }
+#define ASSERT_WRITE_TO_FD(fd, content) \
+ ASSERT_TRUE(android::base::WriteStringToFd(content, android::base::borrowed_fd(fd)))
+
+class FileTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(nullptr);
+
+ ASSERT_EQ(0, create_dir_if_needed(kFileTestDir, 0777));
+ }
+
+ virtual void TearDown() {
+ system(android::base::StringPrintf("rm -rf %s", kFileTestDir).c_str());
+ }
+
+ std::string GetTestFilePath(const std::string& fileName) {
+ return android::base::StringPrintf("%s/%s", kFileTestDir, fileName.c_str());
+ }
+
+ void CreateTestFileWithContents(const std::string& path, const std::string& content) {
+ ALOGI("CreateTestFileWithContents:%s", path.c_str());
+ ASSERT_TRUE(android::base::WriteStringToFile(content, path));
+ }
+
+ std::string GetTestName() {
+ std::string name(testing::UnitTest::GetInstance()->current_test_info()->name());
+ return name;
+ }
+
+ std::string ReadTestFile(const std::string& path) {
+ std::string content;
+ bool r = android::base::ReadFileToString(path, &content);
+ if (!r) {
+ PLOG(ERROR) << "Cannot read file:" << path;
+ }
+ return content;
+ }
+};
+
+TEST_F(FileTest, TestUniqueFileMoveConstruction) {
+ const int fd = 101;
+ std::string testFile = GetTestFilePath(GetTestName());
+ UniqueFile uf1(fd, testFile);
+ uf1.DisableAutoClose();
+
+ UniqueFile uf2(std::move(uf1));
+
+ ASSERT_EQ(fd, uf2.fd());
+ ASSERT_EQ(testFile, uf2.path());
+}
+
+TEST_F(FileTest, TestUniqueFileAssignment) {
+ const int fd1 = 101;
+ const int fd2 = 102;
+ std::string testFile1 = GetTestFilePath(GetTestName());
+ std::string testFile2 = GetTestFilePath(GetTestName() + "2");
+
+ UniqueFile uf1(fd1, testFile1);
+ uf1.DisableAutoClose();
+
+ UniqueFile uf2(fd2, testFile2);
+ uf2.DisableAutoClose();
+
+ ASSERT_EQ(fd2, uf2.fd());
+ ASSERT_EQ(testFile2, uf2.path());
+
+ uf2 = std::move(uf1);
+
+ ASSERT_EQ(fd1, uf2.fd());
+ ASSERT_EQ(testFile1, uf2.path());
+}
+
+TEST_F(FileTest, TestUniqueFileCleanup) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ int fd;
+ ASSERT_FILE_OPEN(testFile, fd);
+
+ { UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); }
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+}
+
+TEST_F(FileTest, TestUniqueFileNoCleanup) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ int fd;
+ ASSERT_FILE_OPEN(testFile, fd);
+
+ {
+ UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert);
+ uf.DisableCleanup();
+ }
+
+ ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestUniqueFileFd) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ int fd;
+ ASSERT_FILE_OPEN(testFile, fd);
+
+ UniqueFile uf(fd, testFile, UnlinkWithAssert);
+
+ ASSERT_EQ(fd, uf.fd());
+
+ uf.reset();
+
+ ASSERT_EQ(-1, uf.fd());
+}
+
+TEST_F(FileTest, TestRestorableFileMoveConstruction) {
+ std::string testFile = GetTestFilePath(GetTestName());
+
+ RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile, 0600);
+ int fd = rf1.fd();
+
+ RestorableFile rf2(std::move(rf1));
+
+ ASSERT_EQ(fd, rf2.fd());
+ ASSERT_EQ(testFile, rf2.path());
+}
+
+TEST_F(FileTest, TestRestorableFileAssignment) {
+ std::string testFile1 = GetTestFilePath(GetTestName());
+ std::string testFile2 = GetTestFilePath(GetTestName() + "2");
+
+ RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile1, 0600);
+ int fd1 = rf1.fd();
+
+ RestorableFile rf2 = RestorableFile::CreateWritableFile(testFile2, 0600);
+ int fd2 = rf2.fd();
+
+ ASSERT_EQ(fd2, rf2.fd());
+ ASSERT_EQ(testFile2, rf2.path());
+
+ rf2 = std::move(rf1);
+
+ ASSERT_EQ(fd1, rf2.fd());
+ ASSERT_EQ(testFile1, rf2.path());
+}
+
+TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithReset) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+ ASSERT_FILE_EXISTING(tmpFile);
+
+ const UniqueFile& uf = rf.GetUniqueFile();
+
+ ASSERT_EQ(rf.fd(), uf.fd());
+ ASSERT_EQ(rf.path(), uf.path());
+
+ rf.reset();
+
+ ASSERT_EQ(rf.fd(), uf.fd());
+ ASSERT_EQ(rf.path(), uf.path());
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_TRUE(rf.path().empty());
+ }
+}
+
+TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithCommit) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+ ASSERT_FILE_EXISTING(tmpFile);
+
+ const UniqueFile& uf = rf.GetUniqueFile();
+
+ ASSERT_EQ(rf.fd(), uf.fd());
+ ASSERT_EQ(rf.path(), uf.path());
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+
+ rf.CommitWorkFile();
+
+ ASSERT_EQ(rf.fd(), uf.fd());
+ ASSERT_EQ(rf.path(), uf.path());
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_EQ(testFile, rf.path());
+ }
+}
+
+TEST_F(FileTest, TestRestorableFileNewFileNotCommitted) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+ ASSERT_FILE_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+}
+
+TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginal) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+ ASSERT_FILE_EXISTING(testFile);
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginalAndOldTmp) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+ CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+ ASSERT_FILE_EXISTING(testFile);
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestRestorableFileNewFileCommitted) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+ ASSERT_FILE_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+
+ ASSERT_TRUE(rf.CommitWorkFile());
+ rf.RemoveBackupFile();
+
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommittedWithOriginal) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_EXISTING(backupFile);
+
+ ASSERT_TRUE(rf.CommitWorkFile());
+
+ ASSERT_FILE_EXISTING(backupFile);
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+
+ rf.RemoveBackupFile();
+
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommittedWithOriginalAndOldTmp) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+ CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+ ASSERT_TRUE(rf.CommitWorkFile());
+
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommitFailureNoOriginal) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+
+ // Now remove tmp file to force commit failure.
+ close(rf.fd());
+ ASSERT_EQ(0, unlink(tmpFile.c_str()));
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+ ASSERT_FALSE(rf.CommitWorkFile());
+
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_EQ(testFile, rf.path());
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+ ASSERT_TRUE(rf.RestoreBackupFile());
+ }
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileCommitFailureAndRollback) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_EXISTING(backupFile);
+
+ // Now remove tmp file to force commit failure.
+ close(rf.fd());
+ ASSERT_EQ(0, unlink(tmpFile.c_str()));
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+ ASSERT_FALSE(rf.CommitWorkFile());
+
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_EQ(testFile, rf.path());
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_EXISTING(backupFile);
+
+ ASSERT_TRUE(rf.RestoreBackupFile());
+ }
+
+ ASSERT_FILE_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileResetAndRemoveAllFiles) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_EXISTING(backupFile);
+
+ rf.ResetAndRemoveAllFiles();
+
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentFile) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ RestorableFile::RemoveAllFiles(testFile);
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentAndTmpFile) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+ CreateTestFileWithContents(testFile + kTmpFileSuffix, "TmpContent");
+
+ RestorableFile::RemoveAllFiles(testFile);
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/tests/installd_file_test.xml b/cmds/installd/tests/installd_file_test.xml
new file mode 100644
index 0000000..5ec6e3f
--- /dev/null
+++ b/cmds/installd/tests/installd_file_test.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_file_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push"
+ value="installd_file_test->/data/local/tmp/installd_file_test" />
+ </target_preparer>
+
+ <!-- The test requires root for file access (rollback. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_file_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index c4ecd07..0f8a732 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -829,7 +829,7 @@
* to top level directories (i.e. have "..").
*/
static int validate_path(const std::string& dir, const std::string& path, int maxSubdirs) {
- // Argument sanity checking
+ // Argument check
if (dir.find('/') != 0 || dir.rfind('/') != dir.size() - 1
|| dir.find("..") != std::string::npos) {
LOG(ERROR) << "Invalid directory " << dir;
diff --git a/cmds/ip-up-vpn/Android.bp b/cmds/ip-up-vpn/Android.bp
new file mode 100644
index 0000000..c746f7f
--- /dev/null
+++ b/cmds/ip-up-vpn/Android.bp
@@ -0,0 +1,31 @@
+// Copyright 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "ip-up-vpn",
+
+ srcs: ["ip-up-vpn.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+}
diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk
deleted file mode 100644
index 396ae9d..0000000
--- a/cmds/ip-up-vpn/Android.mk
+++ /dev/null
@@ -1,30 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := ip-up-vpn.c
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_MODULE := ip-up-vpn
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index fe417a3..d5ca725 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -21,6 +21,7 @@
#include <cutils/ashmem.h>
#include <getopt.h>
+#include <libgen.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 80c0548..32922ca 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -47,6 +47,14 @@
}
cc_binary {
+ name: "servicemanager.microdroid",
+ defaults: ["servicemanager_defaults"],
+ init_rc: ["servicemanager.microdroid.rc"],
+ srcs: ["main.cpp"],
+ bootstrap: true,
+}
+
+cc_binary {
name: "servicemanager.recovery",
stem: "servicemanager",
recovery: true,
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 4374abe..555be1ed7 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -113,8 +113,8 @@
if (!found) {
// Although it is tested, explicitly rebuilding qualified name, in case it
// becomes something unexpected.
- LOG(ERROR) << "Could not find " << aname.package << "." << aname.iface << "/"
- << aname.instance << " in the VINTF manifest.";
+ LOG(INFO) << "Could not find " << aname.package << "." << aname.iface << "/"
+ << aname.instance << " in the VINTF manifest.";
}
return found;
diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc
new file mode 100644
index 0000000..e01f132
--- /dev/null
+++ b/cmds/servicemanager/servicemanager.microdroid.rc
@@ -0,0 +1,8 @@
+service servicemanager /system/bin/servicemanager.microdroid
+ class core
+ user system
+ group system readproc
+ critical
+ onrestart restart apexd
+ task_profiles ServiceCapacityLow
+ shutdown critical
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 0dd29e0..e5d689f 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -6,8 +6,8 @@
onrestart restart apexd
onrestart restart audioserver
onrestart restart gatekeeperd
- onrestart class_restart main
- onrestart class_restart hal
- onrestart class_restart early_hal
+ onrestart class_restart --only-enabled main
+ onrestart class_restart --only-enabled hal
+ onrestart class_restart --only-enabled early_hal
task_profiles ServiceCapacityLow
shutdown critical
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 931c5e3..31dee23 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -191,6 +191,12 @@
}
prebuilt_etc {
+ name: "android.hardware.wifi.direct.prebuilt.xml",
+ src: "android.hardware.wifi.direct.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.wifi.passpoint.prebuilt.xml",
src: "android.hardware.wifi.passpoint.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -239,6 +245,12 @@
}
prebuilt_etc {
+ name: "go_handheld_core_hardware.prebuilt.xml",
+ src: "go_handheld_core_hardware.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "handheld_core_hardware.prebuilt.xml",
src: "handheld_core_hardware.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.software.opengles.deqp.level-2022-03-01.xml b/data/etc/android.software.opengles.deqp.level-2022-03-01.xml
new file mode 100644
index 0000000..0a11835
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2022-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+ dEQP tests associated with date 2022-03-01 (0x07E60301). -->
+<permissions>
+ <feature name="android.software.opengles.deqp.level" version="132514561" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml
new file mode 100644
index 0000000..8deebc0
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
+ tests associated with date 2022-03-01 (0x07E60301). -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132514561" />
+</permissions>
diff --git a/include/android/input.h b/include/android/input.h
index bb98beb..7642215 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -947,9 +947,10 @@
* and {@link AMotionEvent_fromJava()}.
* After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
* The underlying Java object remains valid and does not change its state.
+ *
+ * Available since API level 31.
*/
-
-void AInputEvent_release(const AInputEvent* event);
+void AInputEvent_release(const AInputEvent* event) __INTRODUCED_IN(31);
/*** Accessors for key events only. ***/
@@ -1001,8 +1002,10 @@
* Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent.
* The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object
* returned by this function must be disposed using {@link AInputEvent_release()}.
+ *
+ * Available since API level 31.
*/
-const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
+const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent) __INTRODUCED_IN(31);
/*** Accessors for motion events only. ***/
@@ -1324,8 +1327,10 @@
* android.view.MotionEvent. The result may be used with generic and MotionEvent-specific
* AInputEvent_* functions. The object returned by this function must be disposed using
* {@link AInputEvent_release()}.
+ *
+ * Available since API level 31.
*/
-const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
+const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) __INTRODUCED_IN(31);
struct AInputQueue;
/**
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 509ee0e..4c83a14 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -216,6 +216,61 @@
*/
void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29);
+/*
+ * Set the socket tag and owning UID for traffic statistics on the specified
+ * socket.
+ *
+ * Subsequent calls always replace any existing parameters. The socket tag and
+ * uid (if set) are kept when the socket is sent to another process using binder
+ * IPCs or other mechanisms such as UNIX socket fd passing. Any app can accept
+ * blame for future traffic performed on a socket originally created by another
+ * app by calling this method with its own UID (or calling
+ * android_tag_socket(int sockfd, int tag)). However, only apps holding the
+ * android.Manifest.permission#UPDATE_DEVICE_STATS permission may assign blame
+ * to another UIDs. If unset (default) the socket tag is 0, and the uid is the
+ * socket creator's uid.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN(33);
+
+/*
+ * Set the socket tag for traffic statistics on the specified socket.
+ *
+ * This function tags the socket with the caller's UID (accepting blame for
+ * future traffic performed on this socket) even if the socket was originally
+ * opened by another UID or was previously tagged by another UID. Subsequent
+ * calls always replace any existing parameters. The socket tag is kept when the
+ * socket is sent to another process using binder IPCs or other mechanisms such
+ * as UNIX socket fd passing.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33);
+
+/*
+ * Untag a network socket.
+ *
+ * Future traffic on this socket will no longer be associated with any
+ * previously configured tag and uid. If the socket was created by another UID
+ * or was previously tagged by another UID, calling this function will clear the
+ * statistics parameters, and thus the UID blamed for traffic on the socket will
+ * be the UID that originally created the socket, even if the socket was
+ * subsequently tagged by a different UID.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_untag_socket(int sockfd) __INTRODUCED_IN(33);
+
__END_DECLS
#endif // ANDROID_MULTINETWORK_H
diff --git a/include/input/Input.h b/include/input/Input.h
index 2e326cb..dce6ccb 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -1015,6 +1015,25 @@
std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
};
+/*
+ * Describes a unique request to enable or disable Pointer Capture.
+ */
+struct PointerCaptureRequest {
+public:
+ inline PointerCaptureRequest() : enable(false), seq(0) {}
+ inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {}
+ inline bool operator==(const PointerCaptureRequest& other) const {
+ return enable == other.enable && seq == other.seq;
+ }
+ explicit inline operator bool() const { return enable; }
+
+ // True iff this is a request to enable Pointer Capture.
+ bool enable;
+
+ // The sequence number for the request.
+ uint32_t seq;
+};
+
} // namespace android
#endif // _LIBINPUT_INPUT_H
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d8101fa..7448308 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -202,7 +202,6 @@
sanitize: {
misc_undefined: ["integer"],
},
- min_sdk_version: "30",
tidy: true,
tidy_flags: [
@@ -225,10 +224,7 @@
"portability*",
],
- pgo: {
- sampling: true,
- profile_file: "libbinder/libbinder.profdata",
- },
+ afdo: true,
}
cc_defaults {
@@ -333,7 +329,6 @@
cc_library {
name: "libbinder_rpc_unstable",
srcs: ["libbinder_rpc_unstable.cpp"],
- defaults: ["libbinder_ndk_host_user"],
shared_libs: [
"libbase",
"libbinder",
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 9e04ffe..13f0a4c 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -352,11 +352,6 @@
return gDisableBackgroundScheduling.load(std::memory_order_relaxed);
}
-sp<ProcessState> IPCThreadState::process()
-{
- return mProcess;
-}
-
status_t IPCThreadState::clearLastError()
{
const status_t err = mLastError;
@@ -1410,23 +1405,6 @@
}
}
-status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, bool *sync_received, bool *async_received)
-{
- int ret = 0;
- binder_frozen_status_info info;
- info.pid = pid;
-
-#if defined(__ANDROID__)
- if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
- ret = -errno;
-#endif
- *sync_received = info.sync_recv;
- *async_received = info.async_recv;
-
- return ret;
-}
-
-#ifndef __ANDROID_VNDK__
status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
uint32_t *async_received)
{
@@ -1443,7 +1421,6 @@
return ret;
}
-#endif
status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
struct binder_freeze_info info;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 7027a4b..f84f639 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -739,6 +739,17 @@
}
}
+binder::Status Parcel::enforceNoDataAvail() const {
+ const auto n = dataAvail();
+ if (n == 0) {
+ return binder::Status::ok();
+ }
+ return binder::Status::
+ fromExceptionCode(binder::Status::Exception::EX_BAD_PARCELABLE,
+ String8::format("Parcel data not fully consumed, unread size: %zu",
+ n));
+}
+
size_t Parcel::objectsCount() const
{
return mObjectsSize;
@@ -2202,12 +2213,14 @@
type == BINDER_TYPE_FD)) {
// We should never receive other types (eg BINDER_TYPE_FDA) as long as we don't support
// them in libbinder. If we do receive them, it probably means a kernel bug; try to
- // recover gracefully by clearing out the objects, and releasing the objects we do
- // know about.
+ // recover gracefully by clearing out the objects.
android_errorWriteLog(0x534e4554, "135930648");
+ android_errorWriteLog(0x534e4554, "203847542");
ALOGE("%s: unsupported type object (%" PRIu32 ") at offset %" PRIu64 "\n",
__func__, type, (uint64_t)offset);
- releaseObjects();
+
+ // WARNING: callers of ipcSetDataReference need to make sure they
+ // don't rely on mObjectsSize in their release_func.
mObjectsSize = 0;
break;
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4f21cda..269b086 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -176,6 +176,11 @@
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
+ if (mMaxThreads == 0) {
+ ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
+ "*startThreadPool when zero threads are requested.");
+ }
+
mThreadPoolStarted = true;
spawnPooledThread(true);
}
@@ -315,11 +320,6 @@
//
// Note that this is not race-free if the context manager
// dies while this code runs.
- //
- // TODO: add a driver API to wait for context manager, or
- // stop special casing handle 0 for context manager and add
- // a driver API to get a handle to the context manager with
- // proper reference counting.
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 93ed50e..ace5cd5 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -287,8 +287,8 @@
RpcConnectionHeader header;
if (status == OK) {
- status = client->interruptableReadFully(server->mShutdownTrigger.get(), &header,
- sizeof(header), {});
+ iovec iov{&header, sizeof(header)};
+ status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {});
if (status != OK) {
ALOGE("Failed to read ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -301,8 +301,9 @@
if (header.sessionIdSize > 0) {
if (header.sessionIdSize == kSessionIdBytes) {
sessionId.resize(header.sessionIdSize);
- status = client->interruptableReadFully(server->mShutdownTrigger.get(),
- sessionId.data(), sessionId.size(), {});
+ iovec iov{sessionId.data(), sessionId.size()};
+ status =
+ client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {});
if (status != OK) {
ALOGE("Failed to read session ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -331,8 +332,8 @@
.version = protocolVersion,
};
- status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &response,
- sizeof(response), {});
+ iovec iov{&response, sizeof(response)};
+ status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1, {});
if (status != OK) {
ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
// still need to cleanup before we can return
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index a5a2bb1..b84395e 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -615,8 +615,9 @@
header.options |= RPC_CONNECTION_OPTION_INCOMING;
}
+ iovec headerIov{&header, sizeof(header)};
auto sendHeaderStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header), {});
+ server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, {});
if (sendHeaderStatus != OK) {
ALOGE("Could not write connection header to socket: %s",
statusToString(sendHeaderStatus).c_str());
@@ -624,9 +625,10 @@
}
if (sessionId.size() > 0) {
+ iovec sessionIov{const_cast<void*>(static_cast<const void*>(sessionId.data())),
+ sessionId.size()};
auto sendSessionIdStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), sessionId.data(),
- sessionId.size(), {});
+ server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, {});
if (sendSessionIdStatus != OK) {
ALOGE("Could not write session ID ('%s') to socket: %s",
base::HexString(sessionId.data(), sessionId.size()).c_str(),
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 09b3d68..4ddbce7 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -19,6 +19,7 @@
#include "RpcState.h"
#include <android-base/hex.h>
+#include <android-base/macros.h>
#include <android-base/scopeguard.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
@@ -309,22 +310,18 @@
}
status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, const void* data,
- size_t size, const std::function<status_t()>& altPoll) {
- LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
- android::base::HexString(data, size).c_str());
-
- if (size > std::numeric_limits<ssize_t>::max()) {
- ALOGE("Cannot send %s at size %zu (too big)", what, size);
- (void)session->shutdownAndWait(false);
- return BAD_VALUE;
+ const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs,
+ const std::function<status_t()>& altPoll) {
+ for (int i = 0; i < niovs; i++) {
+ LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+ android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
}
if (status_t status =
connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(),
- data, size, altPoll);
+ iovs, niovs, altPoll);
status != OK) {
- LOG_RPC_DETAIL("Failed to write %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
+ LOG_RPC_DETAIL("Failed to write %s (%d iovs) on RpcTransport %p, error: %s", what, niovs,
connection->rpcTransport.get(), statusToString(status).c_str());
(void)session->shutdownAndWait(false);
return status;
@@ -334,34 +331,29 @@
}
status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, void* data,
- size_t size) {
- if (size > std::numeric_limits<ssize_t>::max()) {
- ALOGE("Cannot rec %s at size %zu (too big)", what, size);
- (void)session->shutdownAndWait(false);
- return BAD_VALUE;
- }
-
+ const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) {
if (status_t status =
connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
- data, size, {});
+ iovs, niovs, {});
status != OK) {
- LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
+ LOG_RPC_DETAIL("Failed to read %s (%d iovs) on RpcTransport %p, error: %s", what, niovs,
connection->rpcTransport.get(), statusToString(status).c_str());
(void)session->shutdownAndWait(false);
return status;
}
- LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
- android::base::HexString(data, size).c_str());
+ for (int i = 0; i < niovs; i++) {
+ LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+ android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
+ }
return OK;
}
status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, uint32_t* version) {
RpcNewSessionResponse response;
- if (status_t status =
- rpcRec(connection, session, "new session response", &response, sizeof(response));
+ iovec iov{&response, sizeof(response)};
+ if (status_t status = rpcRec(connection, session, "new session response", &iov, 1);
status != OK) {
return status;
}
@@ -374,14 +366,15 @@
RpcOutgoingConnectionInit init{
.msg = RPC_CONNECTION_INIT_OKAY,
};
- return rpcSend(connection, session, "connection init", &init, sizeof(init));
+ iovec iov{&init, sizeof(init)};
+ return rpcSend(connection, session, "connection init", &iov, 1);
}
status_t RpcState::readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session) {
RpcOutgoingConnectionInit init;
- if (status_t status = rpcRec(connection, session, "connection init", &init, sizeof(init));
- status != OK)
+ iovec iov{&init, sizeof(init)};
+ if (status_t status = rpcRec(connection, session, "connection init", &iov, 1); status != OK)
return status;
static_assert(sizeof(init.msg) == sizeof(RPC_CONNECTION_INIT_OKAY));
@@ -514,17 +507,6 @@
.flags = flags,
.asyncNumber = asyncNumber,
};
- CommandData transactionData(sizeof(RpcWireHeader) + sizeof(RpcWireTransaction) +
- data.dataSize());
- if (!transactionData.valid()) {
- return NO_MEMORY;
- }
-
- memcpy(transactionData.data() + 0, &command, sizeof(RpcWireHeader));
- memcpy(transactionData.data() + sizeof(RpcWireHeader), &transaction,
- sizeof(RpcWireTransaction));
- memcpy(transactionData.data() + sizeof(RpcWireHeader) + sizeof(RpcWireTransaction), data.data(),
- data.dataSize());
constexpr size_t kWaitMaxUs = 1000000;
constexpr size_t kWaitLogUs = 10000;
@@ -550,8 +532,13 @@
return drainCommands(connection, session, CommandType::CONTROL_ONLY);
};
- if (status_t status = rpcSend(connection, session, "transaction", transactionData.data(),
- transactionData.size(), drainRefs);
+ iovec iovs[]{
+ {&command, sizeof(RpcWireHeader)},
+ {&transaction, sizeof(RpcWireTransaction)},
+ {const_cast<uint8_t*>(data.data()), data.dataSize()},
+ };
+ if (status_t status =
+ rpcSend(connection, session, "transaction", iovs, arraysize(iovs), drainRefs);
status != OK) {
// TODO(b/167966510): need to undo onBinderLeaving - we know the
// refcount isn't successfully transferred.
@@ -584,8 +571,8 @@
const sp<RpcSession>& session, Parcel* reply) {
RpcWireHeader command;
while (true) {
- if (status_t status = rpcRec(connection, session, "command header (for reply)", &command,
- sizeof(command));
+ iovec iov{&command, sizeof(command)};
+ if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1);
status != OK)
return status;
@@ -599,8 +586,8 @@
CommandData data(command.bodySize);
if (!data.valid()) return NO_MEMORY;
- if (status_t status = rpcRec(connection, session, "reply body", data.data(), command.bodySize);
- status != OK)
+ iovec iov{data.data(), command.bodySize};
+ if (status_t status = rpcRec(connection, session, "reply body", &iov, 1); status != OK)
return status;
if (command.bodySize < sizeof(RpcWireReply)) {
@@ -653,11 +640,8 @@
.command = RPC_COMMAND_DEC_STRONG,
.bodySize = sizeof(RpcDecStrong),
};
- if (status_t status = rpcSend(connection, session, "dec ref header", &cmd, sizeof(cmd));
- status != OK)
- return status;
-
- return rpcSend(connection, session, "dec ref body", &body, sizeof(body));
+ iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}};
+ return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs));
}
status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
@@ -665,8 +649,8 @@
LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
RpcWireHeader command;
- if (status_t status = rpcRec(connection, session, "command header (for server)", &command,
- sizeof(command));
+ iovec iov{&command, sizeof(command)};
+ if (status_t status = rpcRec(connection, session, "command header (for server)", &iov, 1);
status != OK)
return status;
@@ -726,9 +710,8 @@
if (!transactionData.valid()) {
return NO_MEMORY;
}
- if (status_t status = rpcRec(connection, session, "transaction body", transactionData.data(),
- transactionData.size());
- status != OK)
+ iovec iov{transactionData.data(), transactionData.size()};
+ if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1); status != OK)
return status;
return processTransactInternal(connection, session, std::move(transactionData));
@@ -965,16 +948,12 @@
.status = replyStatus,
};
- CommandData replyData(sizeof(RpcWireHeader) + sizeof(RpcWireReply) + reply.dataSize());
- if (!replyData.valid()) {
- return NO_MEMORY;
- }
- memcpy(replyData.data() + 0, &cmdReply, sizeof(RpcWireHeader));
- memcpy(replyData.data() + sizeof(RpcWireHeader), &rpcReply, sizeof(RpcWireReply));
- memcpy(replyData.data() + sizeof(RpcWireHeader) + sizeof(RpcWireReply), reply.data(),
- reply.dataSize());
-
- return rpcSend(connection, session, "reply", replyData.data(), replyData.size());
+ iovec iovs[]{
+ {&cmdReply, sizeof(RpcWireHeader)},
+ {&rpcReply, sizeof(RpcWireReply)},
+ {const_cast<uint8_t*>(reply.data()), reply.dataSize()},
+ };
+ return rpcSend(connection, session, "reply", iovs, arraysize(iovs));
}
status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connection,
@@ -985,9 +964,8 @@
if (!commandData.valid()) {
return NO_MEMORY;
}
- if (status_t status =
- rpcRec(connection, session, "dec ref body", commandData.data(), commandData.size());
- status != OK)
+ iovec iov{commandData.data(), commandData.size()};
+ if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK)
return status;
if (command.bodySize != sizeof(RpcDecStrong)) {
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index dba0a43..f4a0894 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -24,6 +24,8 @@
#include <optional>
#include <queue>
+#include <sys/uio.h>
+
namespace android {
struct RpcWireHeader;
@@ -177,12 +179,11 @@
};
[[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what,
- const void* data, size_t size,
- const std::function<status_t()>& altPoll = nullptr);
+ const sp<RpcSession>& session, const char* what, iovec* iovs,
+ int niovs, const std::function<status_t()>& altPoll = nullptr);
[[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const char* what, void* data,
- size_t size);
+ const sp<RpcSession>& session, const char* what, iovec* iovs,
+ int niovs);
[[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, Parcel* reply);
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 7669518..636e5d0 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -43,40 +43,79 @@
return ret;
}
- template <typename Buffer, typename SendOrReceive>
- status_t interruptableReadOrWrite(FdTrigger* fdTrigger, Buffer buffer, size_t size,
+ template <typename SendOrReceive>
+ status_t interruptableReadOrWrite(FdTrigger* fdTrigger, iovec* iovs, int niovs,
SendOrReceive sendOrReceiveFun, const char* funName,
int16_t event, const std::function<status_t()>& altPoll) {
- const Buffer end = buffer + size;
-
MAYBE_WAIT_IN_FLAKE_MODE;
+ if (niovs < 0) {
+ return BAD_VALUE;
+ }
+
// Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we
// may never know we should be shutting down.
if (fdTrigger->isTriggered()) {
return DEAD_OBJECT;
}
+ // If iovs has one or more empty vectors at the end and
+ // we somehow advance past all the preceding vectors and
+ // pass some or all of the empty ones to sendmsg/recvmsg,
+ // the call will return processSize == 0. In that case
+ // we should be returning OK but instead return DEAD_OBJECT.
+ // To avoid this problem, we make sure here that the last
+ // vector at iovs[niovs - 1] has a non-zero length.
+ while (niovs > 0 && iovs[niovs - 1].iov_len == 0) {
+ niovs--;
+ }
+ if (niovs == 0) {
+ // The vectors are all empty, so we have nothing to send.
+ return OK;
+ }
+
bool havePolled = false;
while (true) {
- ssize_t processSize = TEMP_FAILURE_RETRY(
- sendOrReceiveFun(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+ ssize_t processSize =
+ TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL));
if (processSize < 0) {
int savedErrno = errno;
// Still return the error on later passes, since it would expose
// a problem with polling
- if (havePolled ||
- (!havePolled && savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) {
+ if (havePolled || (savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) {
LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno));
return -savedErrno;
}
} else if (processSize == 0) {
return DEAD_OBJECT;
} else {
- buffer += processSize;
- if (buffer == end) {
+ while (processSize > 0 && niovs > 0) {
+ auto& iov = iovs[0];
+ if (static_cast<size_t>(processSize) < iov.iov_len) {
+ // Advance the base of the current iovec
+ iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize;
+ iov.iov_len -= processSize;
+ break;
+ }
+
+ // The current iovec was fully written
+ processSize -= iov.iov_len;
+ iovs++;
+ niovs--;
+ }
+ if (niovs == 0) {
+ LOG_ALWAYS_FATAL_IF(processSize > 0,
+ "Reached the end of iovecs "
+ "with %zd bytes remaining",
+ processSize);
return OK;
}
}
@@ -95,16 +134,16 @@
}
}
- status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size,
+ status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) override {
- return interruptableReadOrWrite(fdTrigger, reinterpret_cast<const uint8_t*>(data), size,
- send, "send", POLLOUT, altPoll);
+ return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT,
+ altPoll);
}
- status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+ status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) override {
- return interruptableReadOrWrite(fdTrigger, reinterpret_cast<uint8_t*>(data), size, recv,
- "recv", POLLIN, altPoll);
+ return interruptableReadOrWrite(fdTrigger, iovs, niovs, recvmsg, "recvmsg", POLLIN,
+ altPoll);
}
private:
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 7f810b1..3936204 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -275,9 +275,9 @@
RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
: mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
Result<size_t> peek(void* buf, size_t size) override;
- status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size,
+ status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) override;
- status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+ status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) override;
private:
@@ -303,68 +303,87 @@
return ret;
}
-status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, const void* data,
- size_t size,
+status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) {
- auto buffer = reinterpret_cast<const uint8_t*>(data);
- const uint8_t* end = buffer + size;
-
MAYBE_WAIT_IN_FLAKE_MODE;
+ if (niovs < 0) return BAD_VALUE;
+
// Before doing any I/O, check trigger once. This ensures the trigger is checked at least
// once. The trigger is also checked via triggerablePoll() after every SSL_write().
if (fdTrigger->isTriggered()) return DEAD_OBJECT;
- while (buffer < end) {
- size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
- auto [writeSize, errorQueue] = mSsl.call(SSL_write, buffer, todo);
- if (writeSize > 0) {
- buffer += writeSize;
- errorQueue.clear();
+ size_t size = 0;
+ for (int i = 0; i < niovs; i++) {
+ const iovec& iov = iovs[i];
+ if (iov.iov_len == 0) {
continue;
}
- // SSL_write() should never return 0 unless BIO_write were to return 0.
- int sslError = mSsl.getError(writeSize);
- // TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be
- // triggerablePoll()-ed. Then additionalEvent is no longer necessary.
- status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
- "SSL_write", POLLIN, altPoll);
- if (pollStatus != OK) return pollStatus;
- // Do not advance buffer. Try SSL_write() again.
+ size += iov.iov_len;
+
+ auto buffer = reinterpret_cast<const uint8_t*>(iov.iov_base);
+ const uint8_t* end = buffer + iov.iov_len;
+ while (buffer < end) {
+ size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
+ auto [writeSize, errorQueue] = mSsl.call(SSL_write, buffer, todo);
+ if (writeSize > 0) {
+ buffer += writeSize;
+ errorQueue.clear();
+ continue;
+ }
+ // SSL_write() should never return 0 unless BIO_write were to return 0.
+ int sslError = mSsl.getError(writeSize);
+ // TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be
+ // triggerablePoll()-ed. Then additionalEvent is no longer necessary.
+ status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+ "SSL_write", POLLIN, altPoll);
+ if (pollStatus != OK) return pollStatus;
+ // Do not advance buffer. Try SSL_write() again.
+ }
}
LOG_TLS_DETAIL("TLS: Sent %zu bytes!", size);
return OK;
}
-status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) {
- auto buffer = reinterpret_cast<uint8_t*>(data);
- uint8_t* end = buffer + size;
-
MAYBE_WAIT_IN_FLAKE_MODE;
+ if (niovs < 0) return BAD_VALUE;
+
// Before doing any I/O, check trigger once. This ensures the trigger is checked at least
// once. The trigger is also checked via triggerablePoll() after every SSL_write().
if (fdTrigger->isTriggered()) return DEAD_OBJECT;
- while (buffer < end) {
- size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
- auto [readSize, errorQueue] = mSsl.call(SSL_read, buffer, todo);
- if (readSize > 0) {
- buffer += readSize;
- errorQueue.clear();
+ size_t size = 0;
+ for (int i = 0; i < niovs; i++) {
+ const iovec& iov = iovs[i];
+ if (iov.iov_len == 0) {
continue;
}
- if (readSize == 0) {
- // SSL_read() only returns 0 on EOF.
- errorQueue.clear();
- return DEAD_OBJECT;
+ size += iov.iov_len;
+
+ auto buffer = reinterpret_cast<uint8_t*>(iov.iov_base);
+ const uint8_t* end = buffer + iov.iov_len;
+ while (buffer < end) {
+ size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
+ auto [readSize, errorQueue] = mSsl.call(SSL_read, buffer, todo);
+ if (readSize > 0) {
+ buffer += readSize;
+ errorQueue.clear();
+ continue;
+ }
+ if (readSize == 0) {
+ // SSL_read() only returns 0 on EOF.
+ errorQueue.clear();
+ return DEAD_OBJECT;
+ }
+ int sslError = mSsl.getError(readSize);
+ status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+ "SSL_read", 0, altPoll);
+ if (pollStatus != OK) return pollStatus;
+ // Do not advance buffer. Try SSL_read() again.
}
- int sslError = mSsl.getError(readSize);
- status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
- "SSL_read", 0, altPoll);
- if (pollStatus != OK) return pollStatus;
- // Do not advance buffer. Try SSL_read() again.
}
LOG_TLS_DETAIL("TLS: Received %zu bytes!", size);
return OK;
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index bffab5e..949835b 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -27,7 +27,5 @@
@utf8InCpp String diskImagePath;
long versionCode;
@utf8InCpp String versionName;
- boolean hasBootClassPathJars;
- boolean hasDex2OatBootClassPathJars;
- boolean hasSystemServerClassPathJars;
+ boolean hasClassPathJars;
}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7d14315..f295417 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -93,20 +93,20 @@
// ----------------------------------------------------------------------
-#define DECLARE_META_INTERFACE(INTERFACE) \
-public: \
- static const ::android::String16 descriptor; \
- static ::android::sp<I##INTERFACE> asInterface( \
- const ::android::sp<::android::IBinder>& obj); \
- virtual const ::android::String16& getInterfaceDescriptor() const; \
- I##INTERFACE(); \
- virtual ~I##INTERFACE(); \
- static bool setDefaultImpl(std::unique_ptr<I##INTERFACE> impl); \
- static const std::unique_ptr<I##INTERFACE>& getDefaultImpl(); \
-private: \
- static std::unique_ptr<I##INTERFACE> default_impl; \
-public: \
-
+#define DECLARE_META_INTERFACE(INTERFACE) \
+public: \
+ static const ::android::String16 descriptor; \
+ static ::android::sp<I##INTERFACE> asInterface(const ::android::sp<::android::IBinder>& obj); \
+ virtual const ::android::String16& getInterfaceDescriptor() const; \
+ I##INTERFACE(); \
+ virtual ~I##INTERFACE(); \
+ static bool setDefaultImpl(::android::sp<I##INTERFACE> impl); \
+ static const ::android::sp<I##INTERFACE>& getDefaultImpl(); \
+ \
+private: \
+ static ::android::sp<I##INTERFACE> default_impl; \
+ \
+public:
#define __IINTF_CONCAT(x, y) (x ## y)
@@ -142,8 +142,8 @@
} \
return intr; \
} \
- std::unique_ptr<ITYPE> ITYPE::default_impl; \
- bool ITYPE::setDefaultImpl(std::unique_ptr<ITYPE> impl) { \
+ ::android::sp<ITYPE> ITYPE::default_impl; \
+ bool ITYPE::setDefaultImpl(::android::sp<ITYPE> impl) { \
/* Only one user of this interface can use this function */ \
/* at a time. This is a heuristic to detect if two different */ \
/* users in the same process use this function. */ \
@@ -154,7 +154,7 @@
} \
return false; \
} \
- const std::unique_ptr<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \
+ const ::android::sp<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \
ITYPE::INAME() {} \
ITYPE::~INAME() {}
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 065e6e3..bf02099 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -51,17 +51,9 @@
static status_t freeze(pid_t pid, bool enabled, uint32_t timeout_ms);
// Provide information about the state of a frozen process
- static status_t getProcessFreezeInfo(pid_t pid, bool *sync_received,
- bool *async_received);
-
- // TODO: Remove the above legacy duplicated function in next version
-#ifndef __ANDROID_VNDK__
static status_t getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
uint32_t *async_received);
-#endif
- sp<ProcessState> process();
-
status_t clearLastError();
/**
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9670d7b..450e388 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -16,6 +16,7 @@
#pragma once
+#include <array>
#include <map> // for legacy reasons
#include <string>
#include <type_traits>
@@ -53,6 +54,9 @@
class RpcSession;
class String8;
class TextOutput;
+namespace binder {
+class Status;
+}
class Parcel {
friend class IPCThreadState;
@@ -130,6 +134,10 @@
IPCThreadState* threadState = nullptr) const;
bool checkInterface(IBinder*) const;
+ // Verify there are no bytes left to be read on the Parcel.
+ // Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed.
+ binder::Status enforceNoDataAvail() const;
+
void freeData();
size_t objectsCount() const;
@@ -224,6 +232,15 @@
return writeData(val);
}
+ template <typename T, size_t N>
+ status_t writeFixedArray(const std::array<T, N>& val) {
+ return writeData(val);
+ }
+ template <typename T, size_t N>
+ status_t writeFixedArray(const std::optional<std::array<T, N>>& val) {
+ return writeData(val);
+ }
+
// Write an Enum vector with underlying type int8_t.
// Does not use padding; each byte is contiguous.
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
@@ -487,6 +504,15 @@
std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
+ template <typename T, size_t N>
+ status_t readFixedArray(std::array<T, N>* val) const {
+ return readData(val);
+ }
+ template <typename T, size_t N>
+ status_t readFixedArray(std::optional<std::array<T, N>>* val) const {
+ return readData(val);
+ }
+
template<typename T>
status_t read(Flattenable<T>& val) const;
@@ -818,6 +844,16 @@
|| is_specialization_v<T, std::unique_ptr>
|| is_specialization_v<T, std::shared_ptr>;
+ // Tells if T is a fixed-size array.
+ template <typename T>
+ struct is_fixed_array : std::false_type {};
+
+ template <typename T, size_t N>
+ struct is_fixed_array<std::array<T, N>> : std::true_type {};
+
+ template <typename T>
+ static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value;
+
// special int32 value to indicate NonNull or Null parcelables
// This is fixed to be only 0 or 1 by contract, do not change.
static constexpr int32_t kNonNullParcelableFlag = 1;
@@ -922,7 +958,9 @@
if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
} else if constexpr (std::is_base_of_v<Parcelable, T>) {
if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag));
- } else /* constexpr */ { // could define this, but raise as error.
+ } else if constexpr (is_fixed_array_v<T>) {
+ if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
+ } else /* constexpr */ { // could define this, but raise as error.
static_assert(dependent_false_v<CT>);
}
return writeData(*c);
@@ -961,6 +999,23 @@
return OK;
}
+ template <typename T, size_t N>
+ status_t writeData(const std::array<T, N>& val) {
+ static_assert(N <= std::numeric_limits<int32_t>::max());
+ status_t status = writeData(static_cast<int32_t>(N));
+ if (status != OK) return status;
+ if constexpr (is_pointer_equivalent_array_v<T>) {
+ static_assert(N <= std::numeric_limits<size_t>::max() / sizeof(T));
+ return write(val.data(), val.size() * sizeof(T));
+ } else /* constexpr */ {
+ for (const auto& t : val) {
+ status = writeData(t);
+ if (status != OK) return status;
+ }
+ return OK;
+ }
+ }
+
// readData function overloads.
// Implementation detail: Function overloading improves code readability over
// template overloading, but prevents readData<T> from being used for those types.
@@ -1053,9 +1108,8 @@
int32_t peek;
status_t status = readData(&peek);
if (status != OK) return status;
- if constexpr (is_specialization_v<T, std::vector>
- || std::is_same_v<T, String16>
- || std::is_same_v<T, std::string>) {
+ if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T> ||
+ std::is_same_v<T, String16> || std::is_same_v<T, std::string>) {
if (peek == kNullVectorSize) {
c->reset();
return OK;
@@ -1065,12 +1119,15 @@
c->reset();
return OK;
}
- } else /* constexpr */ { // could define this, but raise as error.
+ } else /* constexpr */ { // could define this, but raise as error.
static_assert(dependent_false_v<CT>);
}
// create a new object.
if constexpr (is_specialization_v<CT, std::optional>) {
- c->emplace();
+ // Call default constructor explicitly
+ // - Clang bug: https://bugs.llvm.org/show_bug.cgi?id=35748
+ // std::optional::emplace() doesn't work with nested types.
+ c->emplace(T());
} else /* constexpr */ {
T* const t = new (std::nothrow) T; // contents read from Parcel below.
if (t == nullptr) return NO_MEMORY;
@@ -1079,7 +1136,7 @@
// rewind data ptr to reread (this is pretty quick), otherwise we could
// pass an optional argument to readData to indicate a peeked value.
setDataPosition(startPos);
- if constexpr (is_specialization_v<T, std::vector>) {
+ if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T>) {
return readData(&**c, READ_FLAG_SP_NULLABLE); // nullable sp<> allowed now
} else {
return readData(&**c);
@@ -1142,6 +1199,41 @@
return OK;
}
+ template <typename T, size_t N>
+ status_t readData(std::array<T, N>* val, ReadFlags readFlags = READ_FLAG_NONE) const {
+ static_assert(N <= std::numeric_limits<int32_t>::max());
+ int32_t size;
+ status_t status = readInt32(&size);
+ if (status != OK) return status;
+ if (size < 0) return UNEXPECTED_NULL;
+ if (size != static_cast<int32_t>(N)) return BAD_VALUE;
+ if constexpr (is_pointer_equivalent_array_v<T>) {
+ auto data = reinterpret_cast<const T*>(readInplace(N * sizeof(T)));
+ if (data == nullptr) return BAD_VALUE;
+ memcpy(val->data(), data, N * sizeof(T));
+ } else if constexpr (is_specialization_v<T, sp>) {
+ for (auto& t : *val) {
+ if (readFlags & READ_FLAG_SP_NULLABLE) {
+ status = readNullableStrongBinder(&t); // allow nullable
+ } else {
+ status = readStrongBinder(&t);
+ }
+ if (status != OK) return status;
+ }
+ } else if constexpr (is_fixed_array_v<T>) { // pass readFlags down to nested arrays
+ for (auto& t : *val) {
+ status = readData(&t, readFlags);
+ if (status != OK) return status;
+ }
+ } else /* constexpr */ {
+ for (auto& t : *val) {
+ status = readData(&t);
+ if (status != OK) return status;
+ }
+ }
+ return OK;
+ }
+
//-----------------------------------------------------------------------------
private:
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index db8b5e9..ade2d94 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -28,6 +28,8 @@
#include <binder/RpcCertificateFormat.h>
+#include <sys/uio.h>
+
namespace android {
class FdTrigger;
@@ -44,6 +46,9 @@
/**
* Read (or write), but allow to be interrupted by a trigger.
*
+ * iovs - array of iovecs to perform the operation on. The elements
+ * of the array may be modified by this method.
+ *
* altPoll - function to be called instead of polling, when needing to wait
* to read/write data. If this returns an error, that error is returned from
* this function.
@@ -53,10 +58,10 @@
* error - interrupted (failure or trigger)
*/
[[nodiscard]] virtual status_t interruptableWriteFully(
- FdTrigger *fdTrigger, const void *buf, size_t size,
+ FdTrigger *fdTrigger, iovec *iovs, int niovs,
const std::function<status_t()> &altPoll) = 0;
[[nodiscard]] virtual status_t interruptableReadFully(
- FdTrigger *fdTrigger, void *buf, size_t size,
+ FdTrigger *fdTrigger, iovec *iovs, int niovs,
const std::function<status_t()> &altPoll) = 0;
protected:
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 4289574..77493b3 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -32,17 +32,10 @@
],
}
+// TODO(b/211908498): remove this
cc_defaults {
name: "libbinder_ndk_host_user",
target: {
- host: {
- cflags: [
- "-D__INTRODUCED_IN(n)=",
- "-D__assert(a,b,c)=do { syslog(LOG_ERR, a \": \" c); abort(); } while(false)",
- // We want all the APIs to be available on the host.
- "-D__ANDROID_API__=10000",
- ],
- },
darwin: {
enabled: false,
},
@@ -52,7 +45,6 @@
cc_library {
name: "libbinder_ndk",
- defaults: ["libbinder_ndk_host_user"],
host_supported: true,
recovery_available: true,
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 6949c2c..28e3ff4 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -799,3 +799,12 @@
void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) {
binder->asABBinder()->setMinSchedulerPolicy(policy, priority);
}
+
+void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) {
+ ABBinder* localBinder = binder->asABBinder();
+ if (localBinder == nullptr) {
+ LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder";
+ }
+
+ localBinder->setInheritRt(inheritRt);
+}
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 c903998..2c471c6 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -163,6 +163,15 @@
const T get() const { return mT; }
/**
+ * Release the underlying resource.
+ */
+ [[nodiscard]] T release() {
+ T a = mT;
+ mT = DEFAULT;
+ return a;
+ }
+
+ /**
* This allows the value in this class to be set from beneath it. If you call this method and
* then change the value of T*, you must take ownership of the value you are replacing and add
* ownership to the object that is put in here.
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index c1487c0..0bf1e3d 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -31,6 +31,7 @@
#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
+#include <array>
#include <optional>
#include <string>
#include <type_traits>
@@ -86,9 +87,87 @@
(is_specialization_v<T, std::optional> ||
is_specialization_v<T, std::unique_ptr>);
+// Tells if T is a fixed-size array.
+template <typename T>
+struct is_fixed_array : std::false_type {};
+
+template <typename T, size_t N>
+struct is_fixed_array<std::array<T, N>> : std::true_type {};
+
+template <typename T>
+static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value;
+
+template <typename T>
+static inline constexpr bool dependent_false_v = false;
} // namespace
/**
+ * This checks the length against the array size and retrieves the buffer. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_stdArrayAllocator(void* arrayData, int32_t length, T** outBuffer) {
+ if (length < 0) return false;
+
+ if (length != static_cast<int32_t>(N)) {
+ return false;
+ }
+
+ std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+ *outBuffer = arr->data();
+ return true;
+}
+
+/**
+ * This checks the length against the array size and retrieves the buffer. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_nullableStdArrayAllocator(void* arrayData, int32_t length,
+ T** outBuffer) {
+ std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+ if (length < 0) {
+ *arr = std::nullopt;
+ return true;
+ }
+
+ if (length != static_cast<int32_t>(N)) {
+ return false;
+ }
+
+ arr->emplace();
+ *outBuffer = (*arr)->data();
+ return true;
+}
+
+/**
+ * This checks the length against the array size. No allocation required.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayExternalAllocator(void* arrayData, int32_t length) {
+ (void)arrayData;
+ return length == static_cast<int32_t>(N);
+}
+
+/**
+ * This checks the length against the array size. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_nullableStdArrayExternalAllocator(void* arrayData, int32_t length) {
+ std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+
+ if (length < 0) {
+ *arr = std::nullopt;
+ return true;
+ }
+
+ if (length != static_cast<int32_t>(N)) {
+ return false;
+ }
+
+ arr->emplace();
+ return true;
+}
+
+/**
* This retrieves and allocates a vector to size 'length' and returns the underlying buffer.
*/
template <typename T>
@@ -397,6 +476,118 @@
}
/**
+ * This retrieves the underlying value in a std::array which may not be contiguous at index from a
+ * corresponding arrData.
+ */
+template <typename T, size_t N>
+static inline T AParcel_stdArrayGetter(const void* arrData, size_t index) {
+ const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrData);
+ return (*arr)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding arrData which may not be contiguous at
+ * index.
+ */
+template <typename T, size_t N>
+static inline void AParcel_stdArraySetter(void* arrData, size_t index, T value) {
+ std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrData);
+ (*arr)[index] = value;
+}
+
+/**
+ * This retrieves the underlying value in a std::array which may not be contiguous at index from a
+ * corresponding arrData.
+ */
+template <typename T, size_t N>
+static inline T AParcel_nullableStdArrayGetter(const void* arrData, size_t index) {
+ const std::optional<std::array<T, N>>* arr =
+ static_cast<const std::optional<std::array<T, N>>*>(arrData);
+ return (*arr)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding arrData which may not be contiguous at
+ * index.
+ */
+template <typename T, size_t N>
+static inline void AParcel_nullableStdArraySetter(void* arrData, size_t index, T value) {
+ std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrData);
+ (*arr)->at(index) = value;
+}
+
+/**
+ * Allocates a std::string inside of std::array<std::string, N> at index 'index' to size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayStringElementAllocator(void* arrData, size_t index,
+ int32_t length, char** buffer) {
+ std::array<std::string, N>* arr = static_cast<std::array<std::string, N>*>(arrData);
+ std::string& element = arr->at(index);
+ return AParcel_stdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
+ * This gets the length and buffer of a std::string inside of a std::array<std::string, N> at index
+ * 'index'.
+ */
+template <size_t N>
+static const char* AParcel_stdArrayStringElementGetter(const void* arrData, size_t index,
+ int32_t* outLength) {
+ const std::array<std::string, N>* arr = static_cast<const std::array<std::string, N>*>(arrData);
+ const std::string& element = arr->at(index);
+
+ *outLength = static_cast<int32_t>(element.size());
+ return element.c_str();
+}
+
+/**
+ * Allocates a std::string inside of std::array<std::optional<std::string>, N> at index 'index' to
+ * size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayNullableStringElementAllocator(void* arrData, size_t index,
+ int32_t length, char** buffer) {
+ std::array<std::optional<std::string>, N>* arr =
+ static_cast<std::array<std::optional<std::string>, N>*>(arrData);
+ std::optional<std::string>& element = arr->at(index);
+ return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
+ * This gets the length and buffer of a std::string inside of a
+ * std::array<std::optional<std::string>, N> at index 'index'.
+ */
+template <size_t N>
+static const char* AParcel_stdArrayNullableStringElementGetter(const void* arrData, size_t index,
+ int32_t* outLength) {
+ const std::array<std::optional<std::string>, N>* arr =
+ static_cast<const std::array<std::optional<std::string>, N>*>(arrData);
+ const std::optional<std::string>& element = arr->at(index);
+
+ if (!element) {
+ *outLength = -1;
+ return nullptr;
+ }
+
+ *outLength = static_cast<int32_t>(element->size());
+ return element->c_str();
+}
+
+/**
+ * Allocates a std::string inside of std::optional<std::array<std::optional<std::string>, N>> at
+ * index 'index' to size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_nullableStdArrayStringElementAllocator(void* arrData, size_t index,
+ int32_t length, char** buffer) {
+ std::optional<std::array<std::optional<std::string>, N>>* arr =
+ static_cast<std::optional<std::array<std::optional<std::string>, N>>*>(arrData);
+ std::optional<std::string>& element = (*arr)->at(index);
+ return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
* Convenience API for writing a std::string.
*/
static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) {
@@ -482,9 +673,7 @@
template <typename P>
static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
if constexpr (is_interface_v<P>) {
- if (!p) {
- return STATUS_UNEXPECTED_NULL;
- }
+ // Legacy behavior: allow null
return first_template_type_t<P>::writeToParcel(parcel, p);
} else {
static_assert(is_parcelable_v<P>);
@@ -502,13 +691,8 @@
template <typename P>
static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
if constexpr (is_interface_v<P>) {
- binder_status_t status = first_template_type_t<P>::readFromParcel(parcel, p);
- if (status == STATUS_OK) {
- if (!*p) {
- return STATUS_UNEXPECTED_NULL;
- }
- }
- return status;
+ // Legacy behavior: allow null
+ return first_template_type_t<P>::readFromParcel(parcel, p);
} else {
static_assert(is_parcelable_v<P>);
int32_t null;
@@ -578,6 +762,64 @@
}
}
+// Forward decls
+template <typename T>
+static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value);
+template <typename T>
+static inline binder_status_t AParcel_writeNullableData(AParcel* parcel, const T& value);
+template <typename T>
+static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value);
+template <typename T>
+static inline binder_status_t AParcel_readNullableData(const AParcel* parcel, T* value);
+
+/**
+ * Reads an object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_readStdArrayData(const AParcel* parcel, void* arrayData, size_t index) {
+ std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+ return AParcel_readData(parcel, &arr->at(index));
+}
+
+/**
+ * Reads a nullable object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_readStdArrayNullableData(const AParcel* parcel, void* arrayData,
+ size_t index) {
+ std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+ return AParcel_readNullableData(parcel, &arr->at(index));
+}
+
+/**
+ * Reads a nullable object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_readNullableStdArrayNullableData(const AParcel* parcel, void* arrayData,
+ size_t index) {
+ std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+ return AParcel_readNullableData(parcel, &(*arr)->at(index));
+}
+
+/**
+ * Writes an object of type T inside a std::array<T, N> at index 'index' to 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_writeStdArrayData(AParcel* parcel, const void* arrayData, size_t index) {
+ const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrayData);
+ return AParcel_writeData(parcel, arr->at(index));
+}
+
+/**
+ * Writes a nullable object of type T inside a std::array<T, N> at index 'index' to 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_writeStdArrayNullableData(AParcel* parcel, const void* arrayData,
+ size_t index) {
+ const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrayData);
+ return AParcel_writeNullableData(parcel, arr->at(index));
+}
+
/**
* Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
*/
@@ -721,9 +963,25 @@
*/
template <typename P>
static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<P>& vec) {
- const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
- AParcel_writeStdVectorParcelableElement<P>);
+ if constexpr (std::is_enum_v<P>) {
+ if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+ return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()),
+ static_cast<int32_t>(vec.size()));
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+ return AParcel_writeInt32Array(parcel, reinterpret_cast<const int32_t*>(vec.data()),
+ static_cast<int32_t>(vec.size()));
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+ return AParcel_writeInt64Array(parcel, reinterpret_cast<const int64_t*>(vec.data()),
+ static_cast<int32_t>(vec.size()));
+ } else {
+ static_assert(dependent_false_v<P>, "unrecognized type");
+ }
+ } else {
+ static_assert(!std::is_same_v<P, std::string>, "specialization should be used");
+ const void* vectorData = static_cast<const void*>(&vec);
+ return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
+ AParcel_writeStdVectorParcelableElement<P>);
+ }
}
/**
@@ -731,9 +989,24 @@
*/
template <typename P>
static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<P>* vec) {
- void* vectorData = static_cast<void*>(vec);
- return AParcel_readParcelableArray(parcel, vectorData, AParcel_stdVectorExternalAllocator<P>,
- AParcel_readStdVectorParcelableElement<P>);
+ if constexpr (std::is_enum_v<P>) {
+ void* vectorData = static_cast<void*>(vec);
+ if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+ return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+ return AParcel_readInt32Array(parcel, vectorData, AParcel_stdVectorAllocator<int32_t>);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+ return AParcel_readInt64Array(parcel, vectorData, AParcel_stdVectorAllocator<int64_t>);
+ } else {
+ static_assert(dependent_false_v<P>, "unrecognized type");
+ }
+ } else {
+ static_assert(!std::is_same_v<P, std::string>, "specialization should be used");
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readParcelableArray(parcel, vectorData,
+ AParcel_stdVectorExternalAllocator<P>,
+ AParcel_readStdVectorParcelableElement<P>);
+ }
}
/**
@@ -742,10 +1015,30 @@
template <typename P>
static inline binder_status_t AParcel_writeVector(AParcel* parcel,
const std::optional<std::vector<P>>& vec) {
- if (!vec) return AParcel_writeInt32(parcel, -1);
- const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()),
- AParcel_writeNullableStdVectorParcelableElement<P>);
+ if constexpr (std::is_enum_v<P>) {
+ if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+ return AParcel_writeByteArray(
+ parcel, vec ? reinterpret_cast<const int8_t*>(vec->data()) : nullptr,
+ vec ? static_cast<int32_t>(vec->size()) : -1);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+ return AParcel_writeInt32Array(
+ parcel, vec ? reinterpret_cast<const int32_t*>(vec->data()) : nullptr,
+ vec ? static_cast<int32_t>(vec->size()) : -1);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+ return AParcel_writeInt64Array(
+ parcel, vec ? reinterpret_cast<const int64_t*>(vec->data()) : nullptr,
+ vec ? static_cast<int32_t>(vec->size()) : -1);
+ } else {
+ static_assert(dependent_false_v<P>, "unrecognized type");
+ }
+ } else {
+ static_assert(!std::is_same_v<P, std::optional<std::string>>,
+ "specialization should be used");
+ if (!vec) return AParcel_writeInt32(parcel, -1);
+ const void* vectorData = static_cast<const void*>(&vec);
+ return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()),
+ AParcel_writeNullableStdVectorParcelableElement<P>);
+ }
}
/**
@@ -754,10 +1047,28 @@
template <typename P>
static inline binder_status_t AParcel_readVector(const AParcel* parcel,
std::optional<std::vector<P>>* vec) {
- void* vectorData = static_cast<void*>(vec);
- return AParcel_readParcelableArray(parcel, vectorData,
- AParcel_nullableStdVectorExternalAllocator<P>,
- AParcel_readNullableStdVectorParcelableElement<P>);
+ if constexpr (std::is_enum_v<P>) {
+ void* vectorData = static_cast<void*>(vec);
+ if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+ return AParcel_readByteArray(parcel, vectorData,
+ AParcel_nullableStdVectorAllocator<int8_t>);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+ return AParcel_readInt32Array(parcel, vectorData,
+ AParcel_nullableStdVectorAllocator<int32_t>);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+ return AParcel_readInt64Array(parcel, vectorData,
+ AParcel_nullableStdVectorAllocator<int64_t>);
+ } else {
+ static_assert(dependent_false_v<P>, "unrecognized type");
+ }
+ } else {
+ static_assert(!std::is_same_v<P, std::optional<std::string>>,
+ "specialization should be used");
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readParcelableArray(parcel, vectorData,
+ AParcel_nullableStdVectorExternalAllocator<P>,
+ AParcel_readNullableStdVectorParcelableElement<P>);
+ }
}
// @START
@@ -1139,6 +1450,294 @@
return STATUS_OK;
}
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeFixedArray(AParcel* parcel,
+ const std::array<T, N>& arr) {
+ if constexpr (std::is_same_v<T, bool>) {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeBoolArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_stdArrayGetter<T, N>);
+ } else if constexpr (std::is_same_v<T, uint8_t>) {
+ return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(arr.data()),
+ static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, char16_t>) {
+ return AParcel_writeCharArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return AParcel_writeInt32Array(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return AParcel_writeInt64Array(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, float>) {
+ return AParcel_writeFloatArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, double>) {
+ return AParcel_writeDoubleArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, std::string>) {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeStringArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_stdArrayStringElementGetter<N>);
+ } else {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeParcelableArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_writeStdArrayData<T, N>);
+ }
+}
+
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeFixedArrayWithNullableData(AParcel* parcel,
+ const std::array<T, N>& arr) {
+ if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> ||
+ std::is_same_v<T, char16_t> || std::is_same_v<T, int32_t> ||
+ std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
+ std::is_same_v<T, double> || std::is_same_v<T, std::string>) {
+ return AParcel_writeFixedArray(parcel, arr);
+ } else if constexpr (std::is_same_v<T, std::optional<std::string>>) {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeStringArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_stdArrayNullableStringElementGetter<N>);
+ } else {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeParcelableArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_writeStdArrayNullableData<T, N>);
+ }
+}
+
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeNullableFixedArrayWithNullableData(
+ AParcel* parcel, const std::optional<std::array<T, N>>& arr) {
+ if (!arr) return AParcel_writeInt32(parcel, -1);
+ return AParcel_writeFixedArrayWithNullableData(parcel, arr.value());
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readFixedArray(const AParcel* parcel, std::array<T, N>* arr) {
+ void* arrayData = static_cast<void*>(arr);
+ if constexpr (std::is_same_v<T, bool>) {
+ return AParcel_readBoolArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_stdArraySetter<T, N>);
+ } else if constexpr (std::is_same_v<T, uint8_t>) {
+ return AParcel_readByteArray(parcel, arrayData, &AParcel_stdArrayAllocator<int8_t, N>);
+ } else if constexpr (std::is_same_v<T, char16_t>) {
+ return AParcel_readCharArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return AParcel_readInt32Array(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return AParcel_readInt64Array(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, float>) {
+ return AParcel_readFloatArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, double>) {
+ return AParcel_readDoubleArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, std::string>) {
+ return AParcel_readStringArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_stdArrayStringElementAllocator<N>);
+ } else {
+ return AParcel_readParcelableArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_readStdArrayData<T, N>);
+ }
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readFixedArrayWithNullableData(const AParcel* parcel,
+ std::array<T, N>* arr) {
+ void* arrayData = static_cast<void*>(arr);
+ if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> ||
+ std::is_same_v<T, char16_t> || std::is_same_v<T, int32_t> ||
+ std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
+ std::is_same_v<T, double> || std::is_same_v<T, std::string>) {
+ return AParcel_readFixedArray(parcel, arr);
+ } else if constexpr (std::is_same_v<T, std::optional<std::string>>) {
+ return AParcel_readStringArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_stdArrayNullableStringElementAllocator<N>);
+ } else {
+ return AParcel_readParcelableArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_readStdArrayNullableData<T, N>);
+ }
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readNullableFixedArrayWithNullableData(
+ const AParcel* parcel, std::optional<std::array<T, N>>* arr) {
+ void* arrayData = static_cast<void*>(arr);
+ if constexpr (std::is_same_v<T, bool>) {
+ return AParcel_readBoolArray(parcel, arrayData,
+ &AParcel_nullableStdArrayExternalAllocator<T, N>,
+ &AParcel_nullableStdArraySetter<T, N>);
+ } else if constexpr (std::is_same_v<T, uint8_t>) {
+ return AParcel_readByteArray(parcel, arrayData,
+ &AParcel_nullableStdArrayAllocator<int8_t, N>);
+ } else if constexpr (std::is_same_v<T, char16_t>) {
+ return AParcel_readCharArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return AParcel_readInt32Array(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return AParcel_readInt64Array(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, float>) {
+ return AParcel_readFloatArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, double>) {
+ return AParcel_readDoubleArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, std::string>) {
+ return AParcel_readStringArray(parcel, arrayData,
+ &AParcel_nullableStdArrayExternalAllocator<N>,
+ &AParcel_nullableStdArrayStringElementAllocator<N>);
+ } else {
+ return AParcel_readParcelableArray(parcel, arrayData,
+ &AParcel_nullableStdArrayExternalAllocator<T, N>,
+ &AParcel_readStdArrayNullableData<T, N>);
+ }
+}
+
+/**
+ * Convenience API for writing a value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value) {
+ if constexpr (is_specialization_v<T, std::vector>) {
+ return AParcel_writeVector(parcel, value);
+ } else if constexpr (is_fixed_array_v<T>) {
+ return AParcel_writeFixedArray(parcel, value);
+ } else if constexpr (std::is_same_v<std::string, T>) {
+ return AParcel_writeString(parcel, value);
+ } else if constexpr (std::is_same_v<bool, T>) {
+ return AParcel_writeBool(parcel, value);
+ } else if constexpr (std::is_same_v<int8_t, T> || std::is_same_v<uint8_t, T>) {
+ return AParcel_writeByte(parcel, value);
+ } else if constexpr (std::is_same_v<char16_t, T>) {
+ return AParcel_writeChar(parcel, value);
+ } else if constexpr (std::is_same_v<int32_t, T>) {
+ return AParcel_writeInt32(parcel, value);
+ } else if constexpr (std::is_same_v<int64_t, T>) {
+ return AParcel_writeInt64(parcel, value);
+ } else if constexpr (std::is_same_v<float, T>) {
+ return AParcel_writeFloat(parcel, value);
+ } else if constexpr (std::is_same_v<double, T>) {
+ return AParcel_writeDouble(parcel, value);
+ } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+ return AParcel_writeRequiredParcelFileDescriptor(parcel, value);
+ } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+ return AParcel_writeRequiredStrongBinder(parcel, value);
+ } else if constexpr (std::is_enum_v<T>) {
+ return AParcel_writeData(parcel, static_cast<std::underlying_type_t<T>>(value));
+ } else if constexpr (is_interface_v<T>) {
+ return AParcel_writeParcelable(parcel, value);
+ } else if constexpr (is_parcelable_v<T>) {
+ return AParcel_writeParcelable(parcel, value);
+ } else {
+ static_assert(dependent_false_v<T>, "unrecognized type");
+ return STATUS_OK;
+ }
+}
+
+/**
+ * Convenience API for writing a nullable value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_writeNullableData(AParcel* parcel, const T& value) {
+ if constexpr (is_specialization_v<T, std::optional> &&
+ is_specialization_v<first_template_type_t<T>, std::vector>) {
+ return AParcel_writeVector(parcel, value);
+ } else if constexpr (is_specialization_v<T, std::optional> &&
+ is_fixed_array_v<first_template_type_t<T>>) {
+ return AParcel_writeNullableFixedArrayWithNullableData(parcel, value);
+ } else if constexpr (is_fixed_array_v<T>) { // happens with a nullable multi-dimensional array.
+ return AParcel_writeFixedArrayWithNullableData(parcel, value);
+ } else if constexpr (is_specialization_v<T, std::optional> &&
+ std::is_same_v<first_template_type_t<T>, std::string>) {
+ return AParcel_writeString(parcel, value);
+ } else if constexpr (is_nullable_parcelable_v<T> || is_interface_v<T>) {
+ return AParcel_writeNullableParcelable(parcel, value);
+ } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+ return AParcel_writeNullableParcelFileDescriptor(parcel, value);
+ } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+ return AParcel_writeNullableStrongBinder(parcel, value);
+ } else {
+ return AParcel_writeData(parcel, value);
+ }
+}
+
+/**
+ * Convenience API for reading a value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value) {
+ if constexpr (is_specialization_v<T, std::vector>) {
+ return AParcel_readVector(parcel, value);
+ } else if constexpr (is_fixed_array_v<T>) {
+ return AParcel_readFixedArray(parcel, value);
+ } else if constexpr (std::is_same_v<std::string, T>) {
+ return AParcel_readString(parcel, value);
+ } else if constexpr (std::is_same_v<bool, T>) {
+ return AParcel_readBool(parcel, value);
+ } else if constexpr (std::is_same_v<int8_t, T> || std::is_same_v<uint8_t, T>) {
+ return AParcel_readByte(parcel, value);
+ } else if constexpr (std::is_same_v<char16_t, T>) {
+ return AParcel_readChar(parcel, value);
+ } else if constexpr (std::is_same_v<int32_t, T>) {
+ return AParcel_readInt32(parcel, value);
+ } else if constexpr (std::is_same_v<int64_t, T>) {
+ return AParcel_readInt64(parcel, value);
+ } else if constexpr (std::is_same_v<float, T>) {
+ return AParcel_readFloat(parcel, value);
+ } else if constexpr (std::is_same_v<double, T>) {
+ return AParcel_readDouble(parcel, value);
+ } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+ return AParcel_readRequiredParcelFileDescriptor(parcel, value);
+ } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+ return AParcel_readRequiredStrongBinder(parcel, value);
+ } else if constexpr (std::is_enum_v<T>) {
+ return AParcel_readData(parcel, reinterpret_cast<std::underlying_type_t<T>*>(value));
+ } else if constexpr (is_interface_v<T>) {
+ return AParcel_readParcelable(parcel, value);
+ } else if constexpr (is_parcelable_v<T>) {
+ return AParcel_readParcelable(parcel, value);
+ } else {
+ static_assert(dependent_false_v<T>, "unrecognized type");
+ return STATUS_OK;
+ }
+}
+
+/**
+ * Convenience API for reading a nullable value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_readNullableData(const AParcel* parcel, T* value) {
+ if constexpr (is_specialization_v<T, std::optional> &&
+ is_specialization_v<first_template_type_t<T>, std::vector>) {
+ return AParcel_readVector(parcel, value);
+ } else if constexpr (is_specialization_v<T, std::optional> &&
+ is_fixed_array_v<first_template_type_t<T>>) {
+ return AParcel_readNullableFixedArrayWithNullableData(parcel, value);
+ } else if constexpr (is_fixed_array_v<T>) { // happens with a nullable multi-dimensional array.
+ return AParcel_readFixedArrayWithNullableData(parcel, value);
+ } else if constexpr (is_specialization_v<T, std::optional> &&
+ std::is_same_v<first_template_type_t<T>, std::string>) {
+ return AParcel_readString(parcel, value);
+ } else if constexpr (is_nullable_parcelable_v<T> || is_interface_v<T>) {
+ return AParcel_readNullableParcelable(parcel, value);
+ } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+ return AParcel_readNullableParcelFileDescriptor(parcel, value);
+ } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+ return AParcel_readNullableStrongBinder(parcel, value);
+ } else {
+ return AParcel_readData(parcel, value);
+ }
+}
+
} // namespace ndk
/** @} */
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index aa3b978..972eca7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -46,6 +46,18 @@
AParcelableHolder() = delete;
explicit AParcelableHolder(parcelable_stability_t stability)
: mParcel(AParcel_create()), mStability(stability) {}
+
+#if __ANDROID_API__ >= 31
+ AParcelableHolder(const AParcelableHolder& other)
+ : mParcel(AParcel_create()), mStability(other.mStability) {
+ // AParcelableHolder has been introduced in 31.
+ if (__builtin_available(android 31, *)) {
+ AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
+ AParcel_getDataSize(other.mParcel.get()));
+ }
+ }
+#endif
+
AParcelableHolder(AParcelableHolder&& other) = default;
virtual ~AParcelableHolder() = default;
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 6f1fdfc..76c7aac 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -32,11 +32,26 @@
__BEGIN_DECLS
-#ifndef __ANDROID_API__
-#error Android builds must be compiled against a specific API. If this is an \
- android platform host build, you must use libbinder_ndk_host_user.
+#ifndef __BIONIC__
+
+#ifndef __INTRODUCED_IN
+#define __INTRODUCED_IN(n)
#endif
+#ifndef __assert
+#define __assert(a, b, c) \
+ do { \
+ syslog(LOG_ERR, a ": " c); \
+ abort(); \
+ } while (false)
+#endif
+
+#ifndef __ANDROID_API__
+#define __ANDROID_API__ 10000
+#endif
+
+#endif // __BIONIC__
+
/**
* Low-level status types for use in binder. This is the least preferable way to
* return an error for binder services (where binder_exception_t should be used,
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index b0217c4..89f21dd 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -68,4 +68,16 @@
*/
void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) __INTRODUCED_IN(33);
+/**
+ * Allow the binder to inherit realtime scheduling policies from its caller.
+ *
+ * This must be called before the object is sent to another process. Not thread
+ * safe.
+ *
+ * \param binder local server binder to set the policy for
+ * \param inheritRt whether to inherit realtime scheduling policies (default is
+ * false).
+ */
+void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) __INTRODUCED_IN(33);
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 197c0a1..3824a1b 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -146,6 +146,7 @@
AIBinder_Class_disableInterfaceTokenHeader;
AIBinder_DeathRecipient_setOnUnlinked;
AIBinder_isHandlingTransaction;
+ AIBinder_setInheritRt; # llndk
AIBinder_setMinSchedulerPolicy; # llndk
AParcel_marshal;
AParcel_unmarshal;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 499f88e..357b454 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -505,6 +505,31 @@
}
};
+TEST(NdkBinder, SetInheritRt) {
+ // functional test in binderLibTest
+ sp<IFoo> foo = sp<MyTestFoo>::make();
+ AIBinder* binder = foo->getBinder();
+
+ // does not abort
+ AIBinder_setInheritRt(binder, true);
+ AIBinder_setInheritRt(binder, false);
+ AIBinder_setInheritRt(binder, true);
+
+ AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, SetInheritRtNonLocal) {
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(binder, nullptr);
+
+ ASSERT_TRUE(AIBinder_isRemote(binder));
+
+ EXPECT_DEATH(AIBinder_setInheritRt(binder, true), "");
+ EXPECT_DEATH(AIBinder_setInheritRt(binder, false), "");
+
+ AIBinder_decStrong(binder);
+}
+
TEST(NdkBinder, AddNullService) {
EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name"));
}
@@ -727,6 +752,29 @@
ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass));
}
+static void addOne(int* to) {
+ if (!to) return;
+ ++(*to);
+}
+struct FakeResource : public ndk::impl::ScopedAResource<int*, addOne, nullptr> {
+ explicit FakeResource(int* a) : ScopedAResource(a) {}
+};
+
+TEST(NdkBinder_ScopedAResource, GetDelete) {
+ int deleteCount = 0;
+ { FakeResource resource(&deleteCount); }
+ EXPECT_EQ(deleteCount, 1);
+}
+
+TEST(NdkBinder_ScopedAResource, Release) {
+ int deleteCount = 0;
+ {
+ FakeResource resource(&deleteCount);
+ (void)resource.release();
+ }
+ EXPECT_EQ(deleteCount, 0);
+}
+
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index e2fc18d..355b3b4 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -32,7 +32,7 @@
"com.android.uwb",
"com.android.virt",
],
- min_sdk_version: "current",
+ min_sdk_version: "Tiramisu",
}
rust_library {
@@ -44,6 +44,7 @@
"libtokio",
],
host_supported: true,
+ vendor_available: true,
target: {
darwin: {
enabled: false,
@@ -52,8 +53,10 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.uwb",
"com.android.virt",
],
+ min_sdk_version: "Tiramisu",
}
rust_library {
@@ -79,7 +82,7 @@
"com.android.uwb",
"com.android.virt",
],
- min_sdk_version: "current",
+ min_sdk_version: "Tiramisu",
lints: "none",
clippy_lints: "none",
}
@@ -127,14 +130,6 @@
// Currently necessary for host builds
// TODO(b/31559095): bionic on host should define this
target: {
- host: {
- cflags: [
- "-D__INTRODUCED_IN(n)=",
- "-D__assert(a,b,c)=",
- // We want all the APIs to be available on the host.
- "-D__ANDROID_API__=10000",
- ],
- },
darwin: {
enabled: false,
},
@@ -145,7 +140,7 @@
"com.android.uwb",
"com.android.virt",
],
- min_sdk_version: "current",
+ min_sdk_version: "Tiramisu",
}
// TODO(b/184872979): remove once the Rust API is created.
@@ -162,7 +157,7 @@
"com.android.uwb",
"com.android.virt",
],
- min_sdk_version: "current",
+ min_sdk_version: "Tiramisu",
}
rust_test {
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 91047be..9dcef42 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -28,8 +28,8 @@
//!
//! [`Tokio`]: crate::Tokio
-use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
-use binder::{FromIBinder, StatusCode};
+use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong};
+use binder::binder_impl::BinderAsyncRuntime;
use std::future::Future;
/// Retrieve an existing service for a particular interface, sleeping for a few
@@ -37,12 +37,12 @@
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);
+ return binder::get_interface::<T>(name);
}
let name = name.to_string();
let res = tokio::task::spawn_blocking(move || {
- binder::public_api::get_interface::<T>(&name)
+ binder::get_interface::<T>(&name)
}).await;
// The `is_panic` branch is not actually reachable in Android as we compile
@@ -61,12 +61,12 @@
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);
+ return binder::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)
+ binder::wait_for_interface::<T>(&name)
}).await;
// The `is_panic` branch is not actually reachable in Android as we compile
@@ -120,3 +120,24 @@
}
}
}
+
+/// Wrapper around Tokio runtime types for providing a runtime to a binder server.
+pub struct TokioRuntime<R>(pub R);
+
+impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Runtime> {
+ fn block_on<F: Future>(&self, future: F) -> F::Output {
+ self.0.block_on(future)
+ }
+}
+
+impl BinderAsyncRuntime for TokioRuntime<std::sync::Arc<tokio::runtime::Runtime>> {
+ fn block_on<F: Future>(&self, future: F) -> F::Output {
+ self.0.block_on(future)
+ }
+}
+
+impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Handle> {
+ fn block_on<F: Future>(&self, future: F) -> F::Output {
+ self.0.block_on(future)
+ }
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 4d6b294..467e51e 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -192,9 +192,6 @@
/// Is this object still alive?
fn is_binder_alive(&self) -> bool;
- /// Send a ping transaction to this object
- fn ping_binder(&mut self) -> Result<()>;
-
/// Indicate that the service intends to receive caller security contexts.
#[cfg(not(android_vndk))]
fn set_requesting_sid(&mut self, enable: bool);
@@ -270,6 +267,9 @@
/// The recipient will no longer be called if this object
/// dies.
fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+
+ /// Send a ping transaction to this object
+ fn ping_binder(&mut self) -> Result<()>;
}
/// Opaque reference to the type of a Binder interface.
@@ -536,13 +536,13 @@
/// ```
macro_rules! binder_fn_get_class {
($class:ty) => {
- binder_fn_get_class!($crate::InterfaceClass::new::<$class>());
+ binder_fn_get_class!($crate::binder_impl::InterfaceClass::new::<$class>());
};
($constructor:expr) => {
- fn get_class() -> $crate::InterfaceClass {
+ fn get_class() -> $crate::binder_impl::InterfaceClass {
static CLASS_INIT: std::sync::Once = std::sync::Once::new();
- static mut CLASS: Option<$crate::InterfaceClass> = None;
+ static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
CLASS_INIT.call_once(|| unsafe {
// Safety: This assignment is guarded by the `CLASS_INIT` `Once`
@@ -772,7 +772,7 @@
native: $native($on_transact),
proxy: $proxy {},
$(async: $async_interface,)?
- stability: $crate::Stability::default(),
+ stability: $crate::binder_impl::Stability::default(),
}
}
};
@@ -811,7 +811,7 @@
$($fname: $fty = $finit),*
},
$(async: $async_interface,)?
- stability: $crate::Stability::default(),
+ stability: $crate::binder_impl::Stability::default(),
}
}
};
@@ -828,9 +828,9 @@
} => {
$crate::declare_binder_interface! {
$interface[$descriptor] {
- @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
+ @doc[concat!("A binder [`Remotable`]($crate::binder_impl::Remotable) that holds an [`", stringify!($interface), "`] object.")]
native: $native($on_transact),
- @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
+ @doc[concat!("A binder [`Proxy`]($crate::binder_impl::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
proxy: $proxy {
$($fname: $fty = $finit),*
},
@@ -867,7 +867,7 @@
}
}
- impl $crate::Proxy for $proxy
+ impl $crate::binder_impl::Proxy for $proxy
where
$proxy: $interface,
{
@@ -875,7 +875,7 @@
$descriptor
}
- fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> {
+ fn from_binder(mut binder: $crate::SpIBinder) -> std::result::Result<Self, $crate::StatusCode> {
Ok(Self { binder, $($fname: $finit),* })
}
}
@@ -887,19 +887,19 @@
impl $native {
/// Create a new binder service.
pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
- let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+ let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability);
#[cfg(not(android_vndk))]
- $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
+ $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
}
- impl $crate::Remotable for $native {
+ impl $crate::binder_impl::Remotable for $native {
fn get_descriptor() -> &'static str {
$descriptor
}
- fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> {
+ fn on_transact(&self, code: $crate::binder_impl::TransactionCode, data: &$crate::binder_impl::BorrowedParcel<'_>, reply: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
match $on_transact(&*self.0, code, data, reply) {
// The C++ backend converts UNEXPECTED_NULL into an exception
Err($crate::StatusCode::UNEXPECTED_NULL) => {
@@ -913,19 +913,19 @@
}
}
- fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> $crate::Result<()> {
+ fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
self.0.dump(file, args)
}
- fn get_class() -> $crate::InterfaceClass {
+ fn get_class() -> $crate::binder_impl::InterfaceClass {
static CLASS_INIT: std::sync::Once = std::sync::Once::new();
- static mut CLASS: Option<$crate::InterfaceClass> = None;
+ static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
CLASS_INIT.call_once(|| unsafe {
// Safety: This assignment is guarded by the `CLASS_INIT` `Once`
// variable, and therefore is thread-safe, as it can only occur
// once.
- CLASS = Some($crate::InterfaceClass::new::<$crate::Binder<$native>>());
+ CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>());
});
unsafe {
// Safety: The `CLASS` variable can only be mutated once, above,
@@ -936,25 +936,25 @@
}
impl $crate::FromIBinder for dyn $interface {
- fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> {
- use $crate::AssociateClass;
+ fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $interface>, $crate::StatusCode> {
+ use $crate::binder_impl::AssociateClass;
let existing_class = ibinder.get_class();
if let Some(class) = existing_class {
- if class != <$native as $crate::Remotable>::get_class() &&
- class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+ if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+ class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
{
// The binder object's descriptor string matches what we
// expect. We still need to treat this local or already
// associated object as remote, because we can't cast it
// into a Rust service object without a matching class
// pointer.
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
}
- if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
- let service: $crate::Result<$crate::Binder<$native>> =
+ if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+ let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
std::convert::TryFrom::try_from(ibinder.clone());
if let Ok(service) = service {
// We were able to associate with our expected class and
@@ -962,7 +962,7 @@
return Ok($crate::Strong::new(Box::new(service)));
} else {
// Service is remote
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
}
@@ -970,18 +970,18 @@
}
}
- impl $crate::parcel::Serialize for dyn $interface + '_
+ impl $crate::binder_impl::Serialize for dyn $interface + '_
where
dyn $interface: $crate::Interface
{
- fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
let binder = $crate::Interface::as_binder(self);
parcel.write(&binder)
}
}
- impl $crate::parcel::SerializeOption for dyn $interface + '_ {
- fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl $crate::binder_impl::SerializeOption for dyn $interface + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&this.map($crate::Interface::as_binder))
}
}
@@ -1004,25 +1004,25 @@
$(
// Async interface trait implementations.
impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
- fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> {
- use $crate::AssociateClass;
+ fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> {
+ use $crate::binder_impl::AssociateClass;
let existing_class = ibinder.get_class();
if let Some(class) = existing_class {
- if class != <$native as $crate::Remotable>::get_class() &&
- class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+ if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+ class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
{
// The binder object's descriptor string matches what we
// expect. We still need to treat this local or already
// associated object as remote, because we can't cast it
// into a Rust service object without a matching class
// pointer.
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
}
- if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
- let service: $crate::Result<$crate::Binder<$native>> =
+ if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+ let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
std::convert::TryFrom::try_from(ibinder.clone());
if let Ok(service) = service {
// We were able to associate with our expected class and
@@ -1031,7 +1031,7 @@
//return Ok($crate::Strong::new(Box::new(service)));
} else {
// Service is remote
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
}
@@ -1039,15 +1039,15 @@
}
}
- impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ {
- fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
+ fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
let binder = $crate::Interface::as_binder(self);
parcel.write(&binder)
}
}
- impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ {
- fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&this.map($crate::Interface::as_binder))
}
}
@@ -1067,11 +1067,11 @@
}
}
- impl<P: $crate::BinderAsyncPool> $crate::ToAsyncInterface<P> for dyn $interface {
+ impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
type Target = dyn $async_interface<P>;
}
- impl<P: $crate::BinderAsyncPool> $crate::ToSyncInterface for dyn $async_interface<P> {
+ impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
type Target = dyn $interface;
}
)?
@@ -1103,29 +1103,29 @@
}
}
- impl $crate::parcel::Serialize for $enum {
- fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl $crate::binder_impl::Serialize for $enum {
+ fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&self.0)
}
}
- impl $crate::parcel::SerializeArray for $enum {
- fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl $crate::binder_impl::SerializeArray for $enum {
+ fn serialize_array(slice: &[Self], parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
- <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
+ <$backing as $crate::binder_impl::SerializeArray>::serialize_array(&v[..], parcel)
}
}
- impl $crate::parcel::Deserialize for $enum {
- fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> {
+ impl $crate::binder_impl::Deserialize for $enum {
+ fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Self, $crate::StatusCode> {
parcel.read().map(Self)
}
}
- impl $crate::parcel::DeserializeArray for $enum {
- fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> {
+ impl $crate::binder_impl::DeserializeArray for $enum {
+ fn deserialize_array(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Option<Vec<Self>>, $crate::StatusCode> {
let v: Option<Vec<$backing>> =
- <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
+ <$backing as $crate::binder_impl::DeserializeArray>::deserialize_array(parcel)?;
Ok(v.map(|v| v.into_iter().map(Self).collect()))
}
}
diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs
index 214c0b5..579f9f9 100644
--- a/libs/binder/rust/src/binder_async.rs
+++ b/libs/binder/rust/src/binder_async.rs
@@ -53,3 +53,9 @@
B: Send + 'a,
E: From<crate::StatusCode>;
}
+
+/// A runtime for executing an async binder server.
+pub trait BinderAsyncRuntime {
+ /// Block on the provided future, running it to completion and returning its output.
+ fn block_on<F: Future>(&self, future: F) -> F::Output;
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 7c04a72..1d7de98 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -101,45 +101,50 @@
mod binder_async;
mod error;
mod native;
+mod parcel;
mod state;
use binder_ndk_sys as sys;
-pub mod parcel;
-
-pub use crate::binder::{
- BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
- Stability, Strong, ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, Weak,
- FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
+pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
+pub use error::{ExceptionCode, Status, StatusCode};
+pub use native::{
+ add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
};
-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, 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};
+pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
+pub use proxy::{
+ get_interface, get_service, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder,
+ WpIBinder,
+};
pub use state::{ProcessState, ThreadState};
+/// Binder result containing a [`Status`] on error.
+pub type Result<T> = std::result::Result<T, Status>;
+
+/// Advanced Binder APIs needed internally by AIDL or when manually using Binder
+/// without AIDL.
+pub mod binder_impl {
+ pub use crate::binder::{
+ IBinderInternal, InterfaceClass, Remotable, Stability, ToAsyncInterface, ToSyncInterface,
+ TransactionCode, TransactionFlags, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
+ FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+ };
+ pub use crate::binder_async::BinderAsyncRuntime;
+ pub use crate::error::status_t;
+ pub use crate::native::Binder;
+ pub use crate::parcel::{
+ BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Parcel,
+ ParcelableMetadata, Serialize, SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG,
+ NULL_PARCELABLE_FLAG,
+ };
+ pub use crate::proxy::{AssociateClass, Proxy};
+}
+
/// Unstable, in-development API that only allowlisted clients are allowed to use.
+#[doc(hidden)]
pub mod unstable_api {
pub use crate::binder::AsNative;
pub use crate::proxy::unstable_api::new_spibinder;
pub use crate::sys::AIBinder;
}
-
-/// The public API usable outside AIDL-generated interface crates.
-pub mod public_api {
- pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder};
- pub use super::{
- add_service, force_lazy_services_persist, get_interface, register_lazy_service,
- wait_for_interface,
- };
- pub use super::{
- BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder,
- Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak,
- WpIBinder,
- };
-
- /// Binder result containing a [`Status`] on error.
- pub type Result<T> = std::result::Result<T, Status>;
-}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 206b90c..256fa8b 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -496,7 +496,7 @@
{
let start = self.get_data_position();
let parcelable_size: i32 = self.read()?;
- if parcelable_size < 0 {
+ if parcelable_size < 4 {
return Err(StatusCode::BAD_VALUE);
}
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index b0dea94..4d3d59a 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -60,6 +60,16 @@
}
}
+impl PartialEq for ParcelFileDescriptor {
+ // Since ParcelFileDescriptors own the FD, if this function ever returns true (and it is used to
+ // compare two different objects), then it would imply that an FD is double-owned.
+ fn eq(&self, other: &Self) -> bool {
+ self.as_raw_fd() == other.as_raw_fd()
+ }
+}
+
+impl Eq for ParcelFileDescriptor {}
+
impl Serialize for ParcelFileDescriptor {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let fd = self.0.as_raw_fd();
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 9007cba..0c7e48d 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -652,6 +652,39 @@
}
}
+impl<T: SerializeArray, const N: usize> Serialize for [T; N] {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ // forwards to T::serialize_array.
+ SerializeArray::serialize_array(self, parcel)
+ }
+}
+
+impl<T: SerializeArray, const N: usize> SerializeOption for [T; N] {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ SerializeOption::serialize_option(this.map(|arr| &arr[..]), parcel)
+ }
+}
+
+impl<T: SerializeArray, const N: usize> SerializeArray for [T; N] {}
+
+impl<T: DeserializeArray, const N: usize> Deserialize for [T; N] {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
+ let vec = DeserializeArray::deserialize_array(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))?;
+ vec.try_into().or(Err(StatusCode::BAD_VALUE))
+ }
+}
+
+impl<T: DeserializeArray, const N: usize> DeserializeOption for [T; N] {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
+ let vec = DeserializeArray::deserialize_array(parcel)?;
+ vec.map(|v| v.try_into().or(Err(StatusCode::BAD_VALUE))).transpose()
+ }
+}
+
+impl<T: DeserializeArray, const N: usize> DeserializeArray for [T; N] {}
+
impl Serialize for Stability {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
i32::from(*self).serialize(parcel)
@@ -769,35 +802,32 @@
#[macro_export]
macro_rules! impl_serialize_for_parcelable {
($parcelable:ident) => {
- impl $crate::parcel::Serialize for $parcelable {
+ impl $crate::binder_impl::Serialize for $parcelable {
fn serialize(
&self,
- parcel: &mut $crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<()> {
- <Self as $crate::parcel::SerializeOption>::serialize_option(
- Some(self),
- parcel,
- )
+ parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
+ <Self as $crate::binder_impl::SerializeOption>::serialize_option(Some(self), parcel)
}
}
- impl $crate::parcel::SerializeArray for $parcelable {}
+ impl $crate::binder_impl::SerializeArray for $parcelable {}
- impl $crate::parcel::SerializeOption for $parcelable {
+ impl $crate::binder_impl::SerializeOption for $parcelable {
fn serialize_option(
this: Option<&Self>,
- parcel: &mut $crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<()> {
+ parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
if let Some(this) = this {
- use $crate::parcel::Parcelable;
- parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?;
+ use $crate::Parcelable;
+ parcel.write(&$crate::binder_impl::NON_NULL_PARCELABLE_FLAG)?;
this.write_to_parcel(parcel)
} else {
- parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG)
+ parcel.write(&$crate::binder_impl::NULL_PARCELABLE_FLAG)
}
}
}
- }
+ };
}
/// Implement `Deserialize` trait and friends for a parcelable
@@ -809,54 +839,54 @@
#[macro_export]
macro_rules! impl_deserialize_for_parcelable {
($parcelable:ident) => {
- impl $crate::parcel::Deserialize for $parcelable {
+ impl $crate::binder_impl::Deserialize for $parcelable {
fn deserialize(
- parcel: &$crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<Self> {
- $crate::parcel::DeserializeOption::deserialize_option(parcel)
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<Self, $crate::StatusCode> {
+ $crate::binder_impl::DeserializeOption::deserialize_option(parcel)
.transpose()
.unwrap_or(Err($crate::StatusCode::UNEXPECTED_NULL))
}
fn deserialize_from(
&mut self,
- parcel: &$crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<()> {
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
let status: i32 = parcel.read()?;
- if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+ if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
Err($crate::StatusCode::UNEXPECTED_NULL)
} else {
- use $crate::parcel::Parcelable;
+ use $crate::Parcelable;
self.read_from_parcel(parcel)
}
}
}
- impl $crate::parcel::DeserializeArray for $parcelable {}
+ impl $crate::binder_impl::DeserializeArray for $parcelable {}
- impl $crate::parcel::DeserializeOption for $parcelable {
+ impl $crate::binder_impl::DeserializeOption for $parcelable {
fn deserialize_option(
- parcel: &$crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<Option<Self>> {
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<Option<Self>, $crate::StatusCode> {
let mut result = None;
Self::deserialize_option_from(&mut result, parcel)?;
Ok(result)
}
fn deserialize_option_from(
this: &mut Option<Self>,
- parcel: &$crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<()> {
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
let status: i32 = parcel.read()?;
- if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+ if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
*this = None;
Ok(())
} else {
- use $crate::parcel::Parcelable;
+ use $crate::Parcelable;
this.get_or_insert_with(Self::default)
.read_from_parcel(parcel)
}
}
}
- }
+ };
}
impl<T: Serialize> Serialize for Box<T> {
@@ -885,7 +915,7 @@
#[cfg(test)]
mod tests {
- use crate::Parcel;
+ use crate::parcel::Parcel;
use super::*;
#[test]
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index b4282b2..d58e839 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -15,9 +15,11 @@
*/
use crate::binder::Stability;
-use crate::error::{Result, StatusCode};
-use crate::parcel::{Parcel, BorrowedParcel, Parcelable};
-use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
+use crate::error::StatusCode;
+use crate::parcel::{
+ BorrowedParcel, Deserialize, Parcel, Parcelable, Serialize, NON_NULL_PARCELABLE_FLAG,
+ NULL_PARCELABLE_FLAG,
+};
use downcast_rs::{impl_downcast, DowncastSync};
use std::any::Any;
@@ -53,12 +55,6 @@
Parcel(Parcel),
}
-impl Default for ParcelableHolderData {
- fn default() -> Self {
- ParcelableHolderData::Empty
- }
-}
-
/// A container that can hold any arbitrary `Parcelable`.
///
/// This type is currently used for AIDL parcelable fields.
@@ -66,7 +62,7 @@
/// `ParcelableHolder` is currently not thread-safe (neither
/// `Send` nor `Sync`), mainly because it internally contains
/// a `Parcel` which in turn is not thread-safe.
-#[derive(Debug, Default)]
+#[derive(Debug)]
pub struct ParcelableHolder {
// This is a `Mutex` because of `get_parcelable`
// which takes `&self` for consistency with C++.
@@ -97,7 +93,7 @@
}
/// Set the parcelable contained in this `ParcelableHolder`.
- pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<()>
+ pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<(), StatusCode>
where
T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
{
@@ -126,7 +122,7 @@
/// * `Ok(None)` if the holder is empty or the descriptor does not match
/// * `Ok(Some(_))` if the object holds a parcelable of type `T`
/// with the correct descriptor
- pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>>
+ pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>, StatusCode>
where
T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug + Send + Sync,
{
@@ -176,11 +172,28 @@
}
}
-impl_serialize_for_parcelable!(ParcelableHolder);
-impl_deserialize_for_parcelable!(ParcelableHolder);
+impl Serialize for ParcelableHolder {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
+ parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
+ self.write_to_parcel(parcel)
+ }
+}
+
+impl Deserialize for ParcelableHolder {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self, StatusCode> {
+ let status: i32 = parcel.read()?;
+ if status == NULL_PARCELABLE_FLAG {
+ Err(StatusCode::UNEXPECTED_NULL)
+ } else {
+ let mut parcelable = ParcelableHolder::new(Default::default());
+ parcelable.read_from_parcel(parcel)?;
+ Ok(parcelable)
+ }
+ }
+}
impl Parcelable for ParcelableHolder {
- fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
parcel.write(&self.stability)?;
let mut data = self.data.lock().unwrap();
@@ -219,7 +232,7 @@
}
}
- fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
+ fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> {
self.stability = parcel.read()?;
let data_size: i32 = parcel.read()?;
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 760d862..12bfde7 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -312,17 +312,6 @@
}
}
- fn ping_binder(&mut self) -> Result<()> {
- let status = unsafe {
- // Safety: `SpIBinder` guarantees that `self` always contains a
- // valid pointer to an `AIBinder`.
- //
- // This call does not affect ownership of its pointer parameter.
- sys::AIBinder_ping(self.as_native_mut())
- };
- status_result(status)
- }
-
#[cfg(not(android_vndk))]
fn set_requesting_sid(&mut self, enable: bool) {
unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
@@ -412,6 +401,17 @@
)
})
}
+
+ fn ping_binder(&mut self) -> Result<()> {
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`.
+ //
+ // This call does not affect ownership of its pointer parameter.
+ sys::AIBinder_ping(self.as_native_mut())
+ };
+ status_result(status)
+ }
}
impl Serialize for SpIBinder {
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 80dc476..50daf1c 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -17,11 +17,13 @@
//! Rust Binder crate integration tests
use binder::{declare_binder_enum, declare_binder_interface};
-use binder::parcel::BorrowedParcel;
-use binder::{
- Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
- FIRST_CALL_TRANSACTION,
+use binder::{BinderFeatures, Interface, StatusCode, ThreadState};
+// Import from internal API for testing only, do not use this module in
+// production.
+use binder::binder_impl::{
+ Binder, BorrowedParcel, IBinderInternal, TransactionCode, FIRST_CALL_TRANSACTION,
};
+
use std::convert::{TryFrom, TryInto};
use std::ffi::CStr;
use std::fs::File;
@@ -120,7 +122,7 @@
}
impl Interface for TestService {
- fn dump(&self, _file: &File, args: &[&CStr]) -> binder::Result<()> {
+ fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> {
let mut dump_args = self.dump_args.lock().unwrap();
dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned()));
Ok(())
@@ -128,22 +130,22 @@
}
impl ITest for TestService {
- fn test(&self) -> binder::Result<String> {
+ fn test(&self) -> Result<String, StatusCode> {
Ok(self.s.clone())
}
- fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+ fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
let args = self.dump_args.lock().unwrap().clone();
Ok(args)
}
- fn get_selinux_context(&self) -> binder::Result<String> {
+ fn get_selinux_context(&self) -> Result<String, StatusCode> {
let sid =
ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
sid.ok_or(StatusCode::UNEXPECTED_NULL)
}
- fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
Ok(binder::is_handling_transaction())
}
}
@@ -151,31 +153,31 @@
/// Trivial testing binder interface
pub trait ITest: Interface {
/// Returns a test string
- fn test(&self) -> binder::Result<String>;
+ fn test(&self) -> Result<String, StatusCode>;
/// Return the arguments sent via dump
- fn get_dump_args(&self) -> binder::Result<Vec<String>>;
+ fn get_dump_args(&self) -> Result<Vec<String>, StatusCode>;
/// Returns the caller's SELinux context
- fn get_selinux_context(&self) -> binder::Result<String>;
+ fn get_selinux_context(&self) -> Result<String, StatusCode>;
/// Returns the value of calling `is_handling_transaction`.
- fn get_is_handling_transaction(&self) -> binder::Result<bool>;
+ fn get_is_handling_transaction(&self) -> Result<bool, StatusCode>;
}
/// Async trivial testing binder interface
pub trait IATest<P>: Interface {
/// Returns a test string
- fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+ fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
/// Return the arguments sent via dump
- fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>;
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>>;
/// Returns the caller's SELinux context
- fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
/// Returns the value of calling `is_handling_transaction`.
- fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>>;
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>>;
}
declare_binder_interface! {
@@ -193,7 +195,7 @@
code: TransactionCode,
_data: &BorrowedParcel<'_>,
reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
match code.try_into()? {
TestTransactionCode::Test => reply.write(&service.test()?),
TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?),
@@ -203,21 +205,21 @@
}
impl ITest for BpTest {
- fn test(&self) -> binder::Result<String> {
+ fn test(&self) -> Result<String, StatusCode> {
let reply =
self.binder
.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
reply.read()
}
- fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+ fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
let reply =
self.binder
.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(()))?;
reply.read()
}
- fn get_selinux_context(&self) -> binder::Result<String> {
+ fn get_selinux_context(&self) -> Result<String, StatusCode> {
let reply = self.binder.transact(
TestTransactionCode::GetSelinuxContext as TransactionCode,
0,
@@ -226,7 +228,7 @@
reply.read()
}
- fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
let reply = self.binder.transact(
TestTransactionCode::GetIsHandlingTransaction as TransactionCode,
0,
@@ -237,7 +239,7 @@
}
impl<P: binder::BinderAsyncPool> IATest<P> for BpTest {
- fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
let binder = self.binder.clone();
P::spawn(
move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())),
@@ -245,7 +247,7 @@
)
}
- fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
let binder = self.binder.clone();
P::spawn(
move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())),
@@ -253,7 +255,7 @@
)
}
- fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
let binder = self.binder.clone();
P::spawn(
move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())),
@@ -261,7 +263,7 @@
)
}
- fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
let binder = self.binder.clone();
P::spawn(
move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())),
@@ -271,40 +273,40 @@
}
impl ITest for Binder<BnTest> {
- fn test(&self) -> binder::Result<String> {
+ fn test(&self) -> Result<String, StatusCode> {
self.0.test()
}
- fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+ fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
self.0.get_dump_args()
}
- fn get_selinux_context(&self) -> binder::Result<String> {
+ fn get_selinux_context(&self) -> Result<String, StatusCode> {
self.0.get_selinux_context()
}
- fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
self.0.get_is_handling_transaction()
}
}
impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> {
- fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
let res = self.0.test();
Box::pin(async move { res })
}
- fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
let res = self.0.get_dump_args();
Box::pin(async move { res })
}
- fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
let res = self.0.get_selinux_context();
Box::pin(async move { res })
}
- fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
let res = self.0.get_is_handling_transaction();
Box::pin(async move { res })
}
@@ -325,7 +327,7 @@
_code: TransactionCode,
_data: &BorrowedParcel<'_>,
_reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
Ok(())
}
@@ -363,9 +365,12 @@
use std::time::Duration;
use binder::{
- Binder, BinderFeatures, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface,
- SpIBinder, StatusCode, Strong,
+ BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode,
+ Strong,
};
+ // Import from impl API for testing only, should not be necessary as long as
+ // you are using AIDL.
+ use binder::binder_impl::{Binder, IBinderInternal, TransactionCode};
use binder_tokio::Tokio;
@@ -743,8 +748,7 @@
let _process = ScopedServiceProcess::new(service_name);
let test_client: Strong<dyn ITest> =
- binder::get_interface(service_name)
- .expect("Did not get test binder service");
+ binder::get_interface(service_name).expect("Did not get test binder service");
let mut remote = test_client.as_binder();
assert!(remote.is_binder_alive());
remote.ping_binder().expect("Could not ping remote service");
@@ -925,7 +929,7 @@
let service2 = service2.as_binder();
let parcel = service1.prepare_transact().unwrap();
- let res = service2.submit_transact(super::TestTransactionCode::Test as binder::TransactionCode, parcel, 0);
+ let res = service2.submit_transact(super::TestTransactionCode::Test as TransactionCode, parcel, 0);
match res {
Ok(_) => panic!("submit_transact should fail"),
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 1fc761e..b62da7b 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -18,11 +18,12 @@
//! access.
use binder::declare_binder_interface;
-use binder::parcel::ParcelFileDescriptor;
use binder::{
- Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status,
- StatusCode, TransactionCode,
+ BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode,
};
+// Import from impl API for testing only, should not be necessary as long as you
+// are using AIDL.
+use binder::binder_impl::{BorrowedParcel, Binder, TransactionCode};
use std::ffi::{c_void, CStr, CString};
use std::sync::Once;
@@ -113,7 +114,7 @@
code: TransactionCode,
parcel: &BorrowedParcel<'_>,
reply: &mut BorrowedParcel<'_>,
-) -> Result<()> {
+) -> Result<(), StatusCode> {
match code {
bindings::Transaction_TEST_BOOL => {
assert_eq!(parcel.read::<bool>()?, true);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 4d316f7..63a4b2c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -112,7 +112,7 @@
BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_ECHO_VECTOR,
- BINDER_LIB_TEST_REJECT_BUF,
+ BINDER_LIB_TEST_REJECT_OBJECTS,
BINDER_LIB_TEST_CAN_GET_SID,
};
@@ -496,7 +496,7 @@
EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
- bool sync_received, async_received;
+ uint32_t sync_received, async_received;
EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received,
&async_received));
@@ -504,15 +504,7 @@
EXPECT_EQ(sync_received, 1);
EXPECT_EQ(async_received, 0);
- uint32_t sync_received2, async_received2;
-
- EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received2,
- &async_received2));
-
- EXPECT_EQ(sync_received2, 1);
- EXPECT_EQ(async_received2, 0);
-
- EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
}
@@ -1174,13 +1166,53 @@
memcpy(parcelData, &obj, sizeof(obj));
data.setDataSize(sizeof(obj));
+ EXPECT_EQ(data.objectsCount(), 1);
+
// Either the kernel should reject this transaction (if it's correct), but
// if it's not, the server implementation should return an error if it
// finds an object in the received Parcel.
- EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply),
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_OBJECTS, data, &reply),
Not(StatusEq(NO_ERROR)));
}
+TEST_F(BinderLibTest, WeakRejected) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+
+ auto binder = sp<BBinder>::make();
+ wp<BBinder> wpBinder(binder);
+ flat_binder_object obj{
+ .hdr = {.type = BINDER_TYPE_WEAK_BINDER},
+ .flags = 0,
+ .binder = reinterpret_cast<uintptr_t>(wpBinder.get_refs()),
+ .cookie = reinterpret_cast<uintptr_t>(wpBinder.unsafe_get()),
+ };
+ data.setDataCapacity(1024);
+ // Write a bogus object at offset 0 to get an entry in the offset table
+ data.writeFileDescriptor(0);
+ EXPECT_EQ(data.objectsCount(), 1);
+ uint8_t *parcelData = const_cast<uint8_t *>(data.data());
+ // And now, overwrite it with the weak binder
+ memcpy(parcelData, &obj, sizeof(obj));
+ data.setDataSize(sizeof(obj));
+
+ // a previous bug caused other objects to be released an extra time, so we
+ // test with an object that libbinder will actually try to release
+ EXPECT_EQ(OK, data.writeStrongBinder(sp<BBinder>::make()));
+
+ EXPECT_EQ(data.objectsCount(), 2);
+
+ // send it many times, since previous error was memory corruption, make it
+ // more likely that the server crashes
+ for (size_t i = 0; i < 100; i++) {
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_OBJECTS, data, &reply),
+ StatusEq(BAD_VALUE));
+ }
+
+ EXPECT_THAT(server->pingBinder(), StatusEq(NO_ERROR));
+}
+
TEST_F(BinderLibTest, GotSid) {
sp<IBinder> server = addServer();
@@ -1574,7 +1606,7 @@
reply->writeUint64Vector(vector);
return NO_ERROR;
}
- case BINDER_LIB_TEST_REJECT_BUF: {
+ case BINDER_LIB_TEST_REJECT_OBJECTS: {
return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
}
case BINDER_LIB_TEST_CAN_GET_SID: {
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 4950b23..aee15d8 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -16,15 +16,17 @@
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
+#include <binder/Status.h>
#include <cutils/ashmem.h>
#include <gtest/gtest.h>
using android::IPCThreadState;
using android::OK;
using android::Parcel;
+using android::status_t;
using android::String16;
using android::String8;
-using android::status_t;
+using android::binder::Status;
TEST(Parcel, NonNullTerminatedString8) {
String8 kTestString = String8("test-is-good");
@@ -60,6 +62,19 @@
EXPECT_EQ(output.size(), 0);
}
+TEST(Parcel, EnforceNoDataAvail) {
+ const int32_t kTestInt = 42;
+ const String8 kTestString = String8("test-is-good");
+ Parcel p;
+ p.writeInt32(kTestInt);
+ p.writeString8(kTestString);
+ p.setDataPosition(0);
+ EXPECT_EQ(kTestInt, p.readInt32());
+ EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_BAD_PARCELABLE);
+ EXPECT_EQ(kTestString, p.readString8());
+ EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_NONE);
+}
+
// Tests a second operation results in a parcel at the same location as it
// started.
void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 5a96b78..c2639e7 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1517,10 +1517,11 @@
auto keepAlive = sp<BBinder>::make();
auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive);
- if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+ if (!android::base::GetBoolProperty("ro.debuggable", false) ||
+ android::base::GetProperty("ro.build.type", "") == "user") {
ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus)
- << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable builds, "
- "but get "
+ << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable or user "
+ "builds, but get "
<< statusToString(setRpcClientDebugStatus);
GTEST_SKIP();
}
@@ -1674,8 +1675,8 @@
static AssertionResult defaultPostConnect(RpcTransport* serverTransport,
FdTrigger* fdTrigger) {
std::string message(kMessage);
- auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
- message.size(), {});
+ iovec messageIov{message.data(), message.size()};
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {});
if (status != OK) return AssertionFailure() << statusToString(status);
return AssertionSuccess();
}
@@ -1706,9 +1707,9 @@
AssertionResult readMessage(const std::string& expectedMessage = kMessage) {
LOG_ALWAYS_FATAL_IF(mClientTransport == nullptr, "setUpTransport not called or failed");
std::string readMessage(expectedMessage.size(), '\0');
- status_t readStatus =
- mClientTransport->interruptableReadFully(mFdTrigger.get(), readMessage.data(),
- readMessage.size(), {});
+ iovec readMessageIov{readMessage.data(), readMessage.size()};
+ status_t readStatus = mClientTransport->interruptableReadFully(mFdTrigger.get(),
+ &readMessageIov, 1, {});
if (readStatus != OK) {
return AssertionFailure() << statusToString(readStatus);
}
@@ -1902,8 +1903,8 @@
bool shouldContinueWriting = false;
auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
std::string message(RpcTransportTestUtils::kMessage);
- auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
- message.size(), {});
+ iovec messageIov{message.data(), message.size()};
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {});
if (status != OK) return AssertionFailure() << statusToString(status);
{
@@ -1913,7 +1914,8 @@
}
}
- status = serverTransport->interruptableWriteFully(fdTrigger, msg2.data(), msg2.size(), {});
+ iovec msg2Iov{msg2.data(), msg2.size()};
+ status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, {});
if (status != DEAD_OBJECT)
return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully "
"should return DEAD_OBJECT, but it is "
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index acf3f8f..1446802 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -9,7 +9,6 @@
cc_fuzz {
name: "binder_parcel_fuzzer",
- defaults: ["libbinder_ndk_host_user"],
host_supported: true,
fuzz_config: {
@@ -50,6 +49,9 @@
"libbinder",
],
},
+ darwin: {
+ enabled: false,
+ },
},
// This flag enables verbose output in the fuzz target, and is very useful
// for debugging a failure. If you are trying to diagnose how a crash was
@@ -63,7 +65,7 @@
target: {
darwin: {
enabled: false,
- }
+ },
},
srcs: [
"random_fd.cpp",
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 32406e5..13f7195 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -22,6 +22,7 @@
#include <android/os/IServiceManager.h>
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
+#include <binder/Status.h>
using ::android::status_t;
using ::android::base::HexString;
@@ -100,6 +101,7 @@
PARCEL_READ_NO_STATUS(size_t, dataAvail),
PARCEL_READ_NO_STATUS(size_t, dataPosition),
PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+ PARCEL_READ_NO_STATUS(::android::binder::Status, enforceNoDataAvail),
[] (const ::android::Parcel& p, uint8_t pos) {
FUZZ_LOG() << "about to setDataPosition: " << pos;
p.setDataPosition(pos);
@@ -233,6 +235,32 @@
PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
+#define COMMA ,
+ PARCEL_READ_WITH_STATUS(std::array<uint8_t COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<uint8_t COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<char16_t COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<char16_t COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<std::string COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<std::string> COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<android::String16 COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<android::String16> COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<android::sp<android::IBinder> COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<android::sp<android::IBinder> COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<ExampleParcelable> COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<ByteEnum COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<ByteEnum COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<IntEnum COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<IntEnum COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<LongEnum COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<LongEnum COMMA 3>>, readFixedArray),
+ // nested arrays
+ PARCEL_READ_WITH_STATUS(std::array<std::array<uint8_t COMMA 3> COMMA 4>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<uint8_t COMMA 3> COMMA 4>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray),
+#undef COMMA
+
[] (const android::Parcel& p, uint8_t /*len*/) {
FUZZ_LOG() << "about to read flattenable";
ExampleFlattenable f;
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 752fcbb..5aeb5cc 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -156,5 +156,21 @@
PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
+
+ // methods for std::array<T,N>
+#define COMMA ,
+ PARCEL_READ(std::array<bool COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<uint8_t COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<char16_t COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<int32_t COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<int64_t COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<float COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<double COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<std::string COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<SomeParcelable COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<ndk::SpAIBinder COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData),
+#undef COMMA
};
// clang-format on
diff --git a/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder
new file mode 100644
index 0000000..ae081e6
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder
Binary files differ
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
index 570af71..4f63194 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -12,8 +12,9 @@
srcs: ["cputimeinstate.cpp"],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
+ "libbpf_minimal",
"liblog",
"libnetdutils"
],
@@ -31,8 +32,9 @@
srcs: ["testtimeinstate.cpp"],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
+ "libbpf_minimal",
"libtimeinstate",
"libnetdutils",
],
@@ -43,4 +45,5 @@
"-Wextra",
],
require_root: true,
+ test_suites: ["general-tests"],
}
diff --git a/libs/cputimeinstate/TEST_MAPPING b/libs/cputimeinstate/TEST_MAPPING
new file mode 100644
index 0000000..4781520
--- /dev/null
+++ b/libs/cputimeinstate/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libtimeinstate_test"
+ }
+ ]
+}
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 2112b10..1513eca 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -27,6 +27,7 @@
#include <gtest/gtest.h>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <bpf/BpfMap.h>
#include <cputimeinstate.h>
@@ -40,24 +41,31 @@
using std::vector;
-TEST(TimeInStateTest, IsTrackingSupported) {
- isTrackingUidTimesSupported();
- SUCCEED();
-}
+class TimeInStateTest : public testing::Test {
+ protected:
+ TimeInStateTest() {};
-TEST(TimeInStateTest, TotalTimeInState) {
+ void SetUp() {
+ if (!isTrackingUidTimesSupported() ||
+ !android::base::GetBoolProperty("sys.init.perf_lsm_hooks", false)) {
+ GTEST_SKIP();
+ }
+ }
+};
+
+TEST_F(TimeInStateTest, TotalTimeInState) {
auto times = getTotalCpuFreqTimes();
ASSERT_TRUE(times.has_value());
EXPECT_FALSE(times->empty());
}
-TEST(TimeInStateTest, SingleUidTimeInState) {
+TEST_F(TimeInStateTest, SingleUidTimeInState) {
auto times = getUidCpuFreqTimes(0);
ASSERT_TRUE(times.has_value());
EXPECT_FALSE(times->empty());
}
-TEST(TimeInStateTest, SingleUidConcurrentTimes) {
+TEST_F(TimeInStateTest, SingleUidConcurrentTimes) {
auto concurrentTimes = getUidConcurrentTimes(0);
ASSERT_TRUE(concurrentTimes.has_value());
ASSERT_FALSE(concurrentTimes->active.empty());
@@ -117,7 +125,7 @@
EXPECT_EQ(activeSum, policySum);
}
-TEST(TimeInStateTest, SingleUidTimesConsistent) {
+TEST_F(TimeInStateTest, SingleUidTimesConsistent) {
auto times = getUidCpuFreqTimes(0);
ASSERT_TRUE(times.has_value());
@@ -127,7 +135,7 @@
ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes));
}
-TEST(TimeInStateTest, AllUidTimeInState) {
+TEST_F(TimeInStateTest, AllUidTimeInState) {
uint64_t zero = 0;
auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
for (const auto &map : maps) {
@@ -163,7 +171,7 @@
ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC);
}
-TEST(TimeInStateTest, AllUidUpdatedTimeInState) {
+TEST_F(TimeInStateTest, AllUidUpdatedTimeInState) {
uint64_t lastUpdate = 0;
auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
ASSERT_TRUE(map1.has_value());
@@ -197,7 +205,7 @@
}
}
-TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) {
+TEST_F(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) {
auto allUid = getUidsCpuFreqTimes();
auto total = getTotalCpuFreqTimes();
@@ -222,7 +230,7 @@
}
}
-TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
+TEST_F(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
uint64_t zero = 0;
auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
for (const auto &map : maps) {
@@ -246,7 +254,7 @@
}
}
-TEST(TimeInStateTest, AllUidConcurrentTimes) {
+TEST_F(TimeInStateTest, AllUidConcurrentTimes) {
uint64_t zero = 0;
auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
for (const auto &map : maps) {
@@ -264,7 +272,7 @@
}
}
-TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) {
+TEST_F(TimeInStateTest, AllUidUpdatedConcurrentTimes) {
uint64_t lastUpdate = 0;
auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate);
ASSERT_TRUE(map1.has_value());
@@ -299,7 +307,7 @@
}
}
-TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
+TEST_F(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
uint64_t zero = 0;
auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
for (const auto &map : maps) {
@@ -328,7 +336,7 @@
ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
}
-TEST(TimeInStateTest, TotalTimeInStateMonotonic) {
+TEST_F(TimeInStateTest, TotalTimeInStateMonotonic) {
auto before = getTotalCpuFreqTimes();
ASSERT_TRUE(before.has_value());
sleep(1);
@@ -344,7 +352,7 @@
}
}
-TEST(TimeInStateTest, AllUidTimeInStateMonotonic) {
+TEST_F(TimeInStateTest, AllUidTimeInStateMonotonic) {
auto map1 = getUidsCpuFreqTimes();
ASSERT_TRUE(map1.has_value());
sleep(1);
@@ -365,7 +373,7 @@
}
}
-TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) {
+TEST_F(TimeInStateTest, AllUidConcurrentTimesMonotonic) {
auto map1 = getUidsConcurrentTimes();
ASSERT_TRUE(map1.has_value());
ASSERT_FALSE(map1->empty());
@@ -393,7 +401,7 @@
}
}
-TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) {
+TEST_F(TimeInStateTest, AllUidTimeInStateSanityCheck) {
uint64_t zero = 0;
auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
for (const auto &map : maps) {
@@ -414,7 +422,7 @@
}
}
-TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
+TEST_F(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
uint64_t zero = 0;
auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
for (const auto &concurrentMap : maps) {
@@ -441,7 +449,7 @@
}
}
-TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
+TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
uint32_t uid = 0;
{
// Find an unused UID
@@ -463,7 +471,7 @@
ASSERT_FALSE(deleteMapEntry(fd, &key));
}
-TEST(TimeInStateTest, AllUidTimesConsistent) {
+TEST_F(TimeInStateTest, AllUidTimesConsistent) {
auto tisMap = getUidsCpuFreqTimes();
ASSERT_TRUE(tisMap.has_value());
@@ -481,7 +489,7 @@
}
}
-TEST(TimeInStateTest, RemoveUid) {
+TEST_F(TimeInStateTest, RemoveUid) {
uint32_t uid = 0;
{
// Find an unused UID
@@ -547,7 +555,7 @@
ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end());
}
-TEST(TimeInStateTest, GetCpuFreqs) {
+TEST_F(TimeInStateTest, GetCpuFreqs) {
auto freqs = getCpuFreqs();
ASSERT_TRUE(freqs.has_value());
@@ -583,7 +591,7 @@
return nullptr;
}
-TEST(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) {
+TEST_F(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) {
uint64_t startTimeNs = timeNanos();
sem_init(&pingsem, 0, 1);
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index d54de49..7f0cac5 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -343,80 +343,6 @@
return nullptr;
}
-bool GraphicsEnv::checkAngleRules(void* so) {
- auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
- auto model = base::GetProperty("ro.product.model", "UNSET");
-
- auto ANGLEGetFeatureSupportUtilAPIVersion =
- (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
- "ANGLEGetFeatureSupportUtilAPIVersion");
-
- if (!ANGLEGetFeatureSupportUtilAPIVersion) {
- ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function");
- return false;
- }
-
- // Negotiate the interface version by requesting most recent known to the platform
- unsigned int versionToUse = CURRENT_ANGLE_API_VERSION;
- if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) {
- ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, "
- "requested version %u",
- versionToUse);
- return false;
- }
-
- // Add and remove versions below as needed
- bool useAngle = false;
- switch (versionToUse) {
- case 2: {
- ALOGV("Using version %d of ANGLE feature-support library", versionToUse);
- void* rulesHandle = nullptr;
- int rulesVersion = 0;
- void* systemInfoHandle = nullptr;
-
- // Get the symbols for the feature-support-utility library:
-#define GET_SYMBOL(symbol) \
- fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \
- if (!symbol) { \
- ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \
- break; \
- }
- GET_SYMBOL(ANGLEAndroidParseRulesString);
- GET_SYMBOL(ANGLEGetSystemInfo);
- GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
- GET_SYMBOL(ANGLEShouldBeUsedForApplication);
- GET_SYMBOL(ANGLEFreeRulesHandle);
- GET_SYMBOL(ANGLEFreeSystemInfoHandle);
-
- // Parse the rules, obtain the SystemInfo, and evaluate the
- // application against the rules:
- if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) {
- ALOGW("ANGLE feature-support library cannot parse rules file");
- break;
- }
- if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) {
- ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
- break;
- }
- if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
- systemInfoHandle)) {
- ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
- break;
- }
- useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion,
- systemInfoHandle, mAngleAppName.c_str());
- (ANGLEFreeRulesHandle)(rulesHandle);
- (ANGLEFreeSystemInfoHandle)(systemInfoHandle);
- } break;
-
- default:
- ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse);
- }
-
- ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
- return useAngle;
-}
-
bool GraphicsEnv::shouldUseAngle(std::string appName) {
if (appName != mAngleAppName) {
// Make sure we are checking the app we were init'ed for
@@ -444,31 +370,20 @@
const char* ANGLE_PREFER_ANGLE = "angle";
const char* ANGLE_PREFER_NATIVE = "native";
+ mUseAngle = NO;
if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
ALOGV("User set \"Developer Options\" to force the use of ANGLE");
mUseAngle = YES;
} else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
ALOGV("User set \"Developer Options\" to force the use of Native");
- mUseAngle = NO;
} else {
- // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily
- // load ANGLE and call the updatable opt-in/out logic:
- void* featureSo = loadLibrary("feature_support");
- if (featureSo) {
- ALOGV("loaded ANGLE's opt-in/out logic from namespace");
- mUseAngle = checkAngleRules(featureSo) ? YES : NO;
- dlclose(featureSo);
- featureSo = nullptr;
- } else {
- ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE.");
- }
+ ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());
}
}
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
const std::string developerOptIn,
- const std::vector<std::string> eglFeatures, const int rulesFd,
- const long rulesOffset, const long rulesLength) {
+ const std::vector<std::string> eglFeatures) {
if (mUseAngle != UNKNOWN) {
// We've already figured out an answer for this app, so just return.
ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
@@ -485,22 +400,6 @@
ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
mAngleDeveloperOptIn = developerOptIn;
- lseek(rulesFd, rulesOffset, SEEK_SET);
- mRulesBuffer = std::vector<char>(rulesLength + 1);
- ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength);
- if (numBytesRead < 0) {
- ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead);
- numBytesRead = 0;
- } else if (numBytesRead == 0) {
- ALOGW("Empty rules file");
- }
- if (numBytesRead != rulesLength) {
- ALOGW("Did not read all of the necessary bytes from the rules file."
- "expected: %ld, got: %zd",
- rulesLength, numBytesRead);
- }
- mRulesBuffer[numBytesRead] = '\0';
-
// Update the current status of whether we should use ANGLE or not
updateUseAngle();
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 900fc49..56d1139 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,8 +97,7 @@
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
- const std::vector<std::string> eglFeatures, const int rulesFd,
- const long rulesOffset, const long rulesLength);
+ const std::vector<std::string> eglFeatures);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app name for ANGLE debug message.
@@ -129,8 +128,6 @@
// Load requested ANGLE library.
void* loadLibrary(std::string name);
- // Check ANGLE support with the rules.
- bool checkAngleRules(void* so);
// Update whether ANGLE should be used.
void updateUseAngle();
// Link updatable driver namespace with llndk and vndk-sp libs.
@@ -159,8 +156,6 @@
std::string mAngleDeveloperOptIn;
// ANGLE EGL features;
std::vector<std::string> mAngleEglFeatures;
- // ANGLE rules.
- std::vector<char> mRulesBuffer;
// Use ANGLE flag.
UseAngle mUseAngle = UNKNOWN;
// Vulkan debug layers libs.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 3190038..fa853cb 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -78,11 +78,10 @@
],
aidl: {
- export_aidl_headers: true
- }
+ export_aidl_headers: true,
+ },
}
-
cc_library_shared {
name: "libgui",
vendor_available: true,
@@ -192,10 +191,7 @@
"libpdx_headers",
],
- pgo: {
- sampling: true,
- profile_file: "libgui/libgui.profdata",
- },
+ afdo: true,
lto: {
thin: true,
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 56a9683..5b59c59 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -117,16 +117,11 @@
if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
}
-void BLASTBufferItemConsumer::setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) {
- Mutex::Autolock lock(mMutex);
- mBLASTBufferQueue = blastbufferqueue;
-}
-
void BLASTBufferItemConsumer::onSidebandStreamChanged() {
- Mutex::Autolock lock(mMutex);
- if (mBLASTBufferQueue != nullptr) {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (bbq != nullptr) {
sp<NativeHandle> stream = getSidebandStream();
- mBLASTBufferQueue->setSidebandStream(stream);
+ bbq->setSidebandStream(stream);
}
}
@@ -147,7 +142,7 @@
mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
GraphicBuffer::USAGE_HW_COMPOSER |
GraphicBuffer::USAGE_HW_TEXTURE,
- 1, false);
+ 1, false, this);
static int32_t id = 0;
mName = name + "#" + std::to_string(id);
auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
@@ -158,7 +153,6 @@
mBufferItemConsumer->setBufferFreedListener(this);
mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);
mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
- mBufferItemConsumer->setBlastBufferQueue(this);
ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
@@ -177,7 +171,6 @@
}
BLASTBufferQueue::~BLASTBufferQueue() {
- mBufferItemConsumer->setBlastBufferQueue(nullptr);
if (mPendingTransactions.empty()) {
return;
}
@@ -630,7 +623,10 @@
class BBQSurface : public Surface {
private:
+ std::mutex mMutex;
sp<BLASTBufferQueue> mBbq;
+ bool mDestroyed = false;
+
public:
BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,
const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq)
@@ -650,6 +646,10 @@
status_t setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override {
+ std::unique_lock _lock{mMutex};
+ if (mDestroyed) {
+ return DEAD_OBJECT;
+ }
if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
"BBQSurface::setFrameRate")) {
return BAD_VALUE;
@@ -658,8 +658,20 @@
}
status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+ std::unique_lock _lock{mMutex};
+ if (mDestroyed) {
+ return DEAD_OBJECT;
+ }
return mBbq->setFrameTimelineInfo(frameTimelineInfo);
}
+
+ void destroy() override {
+ Surface::destroy();
+
+ std::unique_lock _lock{mMutex};
+ mDestroyed = true;
+ mBbq = nullptr;
+ }
};
// TODO: Can we coalesce this with frame updates? Need to confirm
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2edb4e4..353a91d 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2622,4 +2622,14 @@
return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
}
+sp<IBinder> Surface::getSurfaceControlHandle() const {
+ Mutex::Autolock lock(mMutex);
+ return mSurfaceControlHandle;
+}
+
+void Surface::destroy() {
+ Mutex::Autolock lock(mMutex);
+ mSurfaceControlHandle = nullptr;
+}
+
}; // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index ea9b1c6..bc35792 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -38,11 +38,11 @@
class BLASTBufferItemConsumer : public BufferItemConsumer {
public:
BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
- int bufferCount, bool controlledByApp)
+ int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
: BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+ mBLASTBufferQueue(std::move(bbq)),
mCurrentlyConnected(false),
- mPreviouslyConnected(false),
- mBLASTBufferQueue(nullptr) {}
+ mPreviouslyConnected(false) {}
void onDisconnect() override;
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -59,6 +59,8 @@
void onSidebandStreamChanged() override REQUIRES(mMutex);
private:
+ const wp<BLASTBufferQueue> mBLASTBufferQueue;
+
uint64_t mCurrentFrameNumber = 0;
Mutex mMutex;
@@ -66,7 +68,6 @@
std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mMutex);
bool mCurrentlyConnected GUARDED_BY(mMutex);
bool mPreviouslyConnected GUARDED_BY(mMutex);
- BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mMutex);
};
class BLASTBufferQueue
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 7e4143b..e540351 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -99,7 +99,7 @@
*/
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
- sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; }
+ sp<IBinder> getSurfaceControlHandle() const;
/* convenience function to check that the given surface is non NULL as
* well as its IGraphicBufferProducer */
@@ -333,6 +333,7 @@
virtual int connect(
int api, bool reportBufferRemoval,
const sp<SurfaceListener>& sListener);
+ virtual void destroy();
// When client connects to Surface with reportBufferRemoval set to true, any buffers removed
// from this Surface will be collected and returned here. Once this method returns, these
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index b9ec14a..e5d2684 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -16,6 +16,8 @@
#pragma once
+#include <stdarg.h>
+
#include <cutils/trace.h>
#include <utils/Trace.h>
diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 6882ea3..0128859 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -593,6 +593,10 @@
}
void EGLConsumer::onFreeBufferLocked(int slotIndex) {
+ if (mEglSlots[slotIndex].mEglImage != nullptr &&
+ mEglSlots[slotIndex].mEglImage == mCurrentTextureImage) {
+ mCurrentTextureImage.clear();
+ }
mEglSlots[slotIndex].mEglImage.clear();
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 3c59f11..94023e6 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -424,14 +424,28 @@
return fenceFd;
}
-bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) {
+void SkiaGLRenderEngine::waitFence(base::borrowed_fd fenceFd) {
+ if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) {
+ ATRACE_NAME("SkiaGLRenderEngine::waitFence");
+ sync_wait(fenceFd.get(), -1);
+ }
+}
+
+bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) {
if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
!gl::GLExtensions::getInstance().hasWaitSync()) {
return false;
}
+ // Duplicate the fence for passing to eglCreateSyncKHR.
+ base::unique_fd fenceDup(dup(fenceFd.get()));
+ if (fenceDup.get() < 0) {
+ ALOGE("failed to create duplicate fence fd: %d", fenceDup.get());
+ return false;
+ }
+
// release the fd and transfer the ownership to EGLSync
- EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceDup.release(), EGL_NONE};
EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
if (sync == EGL_NO_SYNC_KHR) {
ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
@@ -726,14 +740,6 @@
return NO_ERROR;
}
- if (bufferFence.get() >= 0) {
- // Duplicate the fence for passing to waitFence.
- base::unique_fd bufferFenceDup(dup(bufferFence.get()));
- if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
- ATRACE_NAME("Waiting before draw");
- sync_wait(bufferFence.get(), -1);
- }
- }
if (buffer == nullptr) {
ALOGE("No output buffer provided. Aborting GPU composition.");
return BAD_VALUE;
@@ -758,6 +764,9 @@
true, mTextureCleanupMgr);
}
+ // wait on the buffer to be ready to use prior to using it
+ waitFence(bufferFence);
+
const ui::Dataspace dstDataspace =
mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
@@ -1014,6 +1023,12 @@
false, mTextureCleanupMgr);
}
+ // if the layer's buffer has a fence, then we must must respect the fence prior to using
+ // the buffer.
+ if (layer->source.buffer.fence != nullptr) {
+ waitFence(layer->source.buffer.fence->get());
+ }
+
// isOpaque means we need to ignore the alpha in the image,
// replacing it with the alpha specified by the LayerSettings. See
// https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index a852bbc..238ad8f 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -99,7 +99,10 @@
inline GrDirectContext* getActiveGrContext() const;
base::unique_fd flush();
- bool waitFence(base::unique_fd fenceFd);
+ // waitFence attempts to wait in the GPU, and if unable to waits on the CPU instead.
+ void waitFence(base::borrowed_fd fenceFd);
+ bool waitGpuFence(base::borrowed_fd fenceFd);
+
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
const ShadowSettings& shadowSettings);
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d4d0ee4..506e308 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -226,10 +226,7 @@
],
min_sdk_version: "29",
- pgo: {
- sampling: true,
- profile_file: "libui/libui.profdata",
- },
+ afdo: true,
}
cc_library_headers {
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
index e6fb2c3..d11631b 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -7,7 +7,7 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "libaudiomanager",
srcs: [
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index 1e8eb1e..178bc29 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -40,6 +40,7 @@
bool chargerAcOnline;
bool chargerUsbOnline;
bool chargerWirelessOnline;
+ bool chargerDockOnline;
int maxChargingCurrent;
int maxChargingVoltage;
int batteryStatus;
diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS
index ac300d0..0ff65bf 100644
--- a/services/gpuservice/OWNERS
+++ b/services/gpuservice/OWNERS
@@ -1,2 +1,6 @@
chrisforbes@google.com
lpy@google.com
+alecmouri@google.com
+lfy@google.com
+paulthomson@google.com
+pbaiget@google.com
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
index 830e53d..24087ac 100644
--- a/services/gpuservice/gpumem/Android.bp
+++ b/services/gpuservice/gpumem/Android.bp
@@ -28,7 +28,7 @@
],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
"libcutils",
"liblog",
diff --git a/services/gpuservice/gpuservice.rc b/services/gpuservice/gpuservice.rc
index 65a5c27..0da8bd3 100644
--- a/services/gpuservice/gpuservice.rc
+++ b/services/gpuservice/gpuservice.rc
@@ -1,4 +1,4 @@
service gpu /system/bin/gpuservice
class core
user gpu_service
- group graphics
+ group graphics readtracefs
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 6d87c45..5b69f96 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -34,7 +34,7 @@
],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
"libcutils",
"libgfxstats",
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
new file mode 100644
index 0000000..e789052
--- /dev/null
+++ b/services/gpuservice/vts/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 653544
+paulthomson@google.com
+pbaiget@google.com
+lfy@google.com
+chrisforbes@google.com
+lpy@google.com
+alecmouri@google.com
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 33b3e1e..71b0f5f 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -287,16 +287,16 @@
// --- NotifyPointerCaptureChangedArgs ---
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime,
- bool enabled)
- : NotifyArgs(id, eventTime), enabled(enabled) {}
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+ int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
+ : NotifyArgs(id, eventTime), request(request) {}
NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
const NotifyPointerCaptureChangedArgs& other)
- : NotifyArgs(other.id, other.eventTime), enabled(other.enabled) {}
+ : NotifyArgs(other.id, other.eventTime), request(other.request) {}
bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const {
- return id == rhs.id && eventTime == rhs.eventTime && enabled == rhs.enabled;
+ return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request;
}
void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 9cc777d..d34482f 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -67,6 +67,9 @@
if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) {
result += "EXTERNAL_STYLUS_PRESENCE | ";
}
+ if (changes & CHANGE_POINTER_CAPTURE) {
+ result += "POINTER_CAPTURE | ";
+ }
if (changes & CHANGE_ENABLED_STATE) {
result += "ENABLED_STATE | ";
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index bc77b8a..aa8cc30 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -113,7 +113,7 @@
void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
- void setPointerCapture(bool enabled) override {}
+ void setPointerCapture(const PointerCaptureRequest&) override {}
void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 881024f..5c3747e 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -119,15 +119,15 @@
// PointerCaptureChanged notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER
// for all entries.
PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime,
- bool hasPointerCapture)
+ const PointerCaptureRequest& request)
: EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
- pointerCaptureEnabled(hasPointerCapture) {}
+ pointerCaptureRequest(request) {}
PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
std::string PointerCaptureChangedEntry::getDescription() const {
return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
- pointerCaptureEnabled ? "true" : "false");
+ pointerCaptureRequest.enable ? "true" : "false");
}
// --- DragEntry ---
@@ -324,8 +324,7 @@
keyEntry(nullptr),
userActivityEventType(0),
seq(0),
- handled(false),
- enabled(false) {}
+ handled(false) {}
CommandEntry::~CommandEntry() {}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ebbd8e9..6f1dfad 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -104,9 +104,9 @@
};
struct PointerCaptureChangedEntry : EventEntry {
- bool pointerCaptureEnabled;
+ const PointerCaptureRequest pointerCaptureRequest;
- PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture);
+ PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
std::string getDescription() const override;
~PointerCaptureChangedEntry() override;
@@ -284,7 +284,7 @@
sp<IBinder> oldToken;
sp<IBinder> newToken;
std::string obscuringPackage;
- bool enabled;
+ PointerCaptureRequest pointerCaptureRequest;
int32_t pid;
nsecs_t consumeTime; // time when the event was consumed by InputConsumer
int32_t displayId;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c2a2794..6e9430a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -523,7 +523,6 @@
mInTouchMode(true),
mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
- mFocusedWindowRequestedPointerCapture(false),
mWindowTokenWithPointerCapture(nullptr),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator),
@@ -1311,36 +1310,51 @@
void InputDispatcher::dispatchPointerCaptureChangedLocked(
nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
DropReason& dropReason) {
+ dropReason = DropReason::NOT_DROPPED;
+
const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
- if (entry->pointerCaptureEnabled && haveWindowWithPointerCapture) {
- LOG_ALWAYS_FATAL("Pointer Capture has already been enabled for the window.");
- }
- if (!entry->pointerCaptureEnabled && !haveWindowWithPointerCapture) {
- // Pointer capture was already forcefully disabled because of focus change.
- dropReason = DropReason::NOT_DROPPED;
- return;
- }
-
- // Set drop reason for early returns
- dropReason = DropReason::NO_POINTER_CAPTURE;
-
sp<IBinder> token;
- if (entry->pointerCaptureEnabled) {
- // Enable Pointer Capture
- if (!mFocusedWindowRequestedPointerCapture) {
+
+ if (entry->pointerCaptureRequest.enable) {
+ // Enable Pointer Capture.
+ if (haveWindowWithPointerCapture &&
+ (entry->pointerCaptureRequest == mCurrentPointerCaptureRequest)) {
+ LOG_ALWAYS_FATAL("This request to enable Pointer Capture has already been dispatched "
+ "to the window.");
+ }
+ if (!mCurrentPointerCaptureRequest.enable) {
// This can happen if a window requests capture and immediately releases capture.
ALOGW("No window requested Pointer Capture.");
+ dropReason = DropReason::NO_POINTER_CAPTURE;
return;
}
+ if (entry->pointerCaptureRequest.seq != mCurrentPointerCaptureRequest.seq) {
+ ALOGI("Skipping dispatch of Pointer Capture being enabled: sequence number mismatch.");
+ return;
+ }
+
token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
mWindowTokenWithPointerCapture = token;
} else {
- // Disable Pointer Capture
+ // Disable Pointer Capture.
+ // We do not check if the sequence number matches for requests to disable Pointer Capture
+ // for two reasons:
+ // 1. Pointer Capture can be disabled by a focus change, which means we can get two entries
+ // to disable capture with the same sequence number: one generated by
+ // disablePointerCaptureForcedLocked() and another as an acknowledgement of Pointer
+ // Capture being disabled in InputReader.
+ // 2. We respect any request to disable Pointer Capture generated by InputReader, since the
+ // actual Pointer Capture state that affects events being generated by input devices is
+ // in InputReader.
+ if (!haveWindowWithPointerCapture) {
+ // Pointer capture was already forcefully disabled because of focus change.
+ dropReason = DropReason::NOT_DROPPED;
+ return;
+ }
token = mWindowTokenWithPointerCapture;
mWindowTokenWithPointerCapture = nullptr;
- if (mFocusedWindowRequestedPointerCapture) {
- mFocusedWindowRequestedPointerCapture = false;
+ if (mCurrentPointerCaptureRequest.enable) {
setPointerCaptureLocked(false);
}
}
@@ -1349,8 +1363,7 @@
if (channel == nullptr) {
// Window has gone away, clean up Pointer Capture state.
mWindowTokenWithPointerCapture = nullptr;
- if (mFocusedWindowRequestedPointerCapture) {
- mFocusedWindowRequestedPointerCapture = false;
+ if (mCurrentPointerCaptureRequest.enable) {
setPointerCaptureLocked(false);
}
return;
@@ -3185,7 +3198,7 @@
static_cast<const PointerCaptureChangedEntry&>(eventEntry);
status = connection->inputPublisher
.publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
- captureEntry.pointerCaptureEnabled);
+ captureEntry.pointerCaptureRequest.enable);
break;
}
@@ -3990,14 +4003,14 @@
void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
- args->enabled ? "true" : "false");
+ args->request.enable ? "true" : "false");
#endif
bool needWake;
{ // acquire lock
std::scoped_lock _l(mLock);
auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
- args->enabled);
+ args->request);
needWake = enqueueInboundEventLocked(std::move(entry));
} // release lock
@@ -4941,8 +4954,8 @@
std::string InputDispatcher::dumpPointerCaptureStateLocked() {
std::string dump;
- dump += StringPrintf(INDENT "FocusedWindowRequestedPointerCapture: %s\n",
- toString(mFocusedWindowRequestedPointerCapture));
+ dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n",
+ toString(mCurrentPointerCaptureRequest.enable));
std::string windowName = "None";
if (mWindowTokenWithPointerCapture) {
@@ -4951,7 +4964,7 @@
windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
: "token has capture without window";
}
- dump += StringPrintf(INDENT "CurrentWindowWithPointerCapture: %s\n", windowName.c_str());
+ dump += StringPrintf(INDENT "Current Window with Pointer Capture: %s\n", windowName.c_str());
return dump;
}
@@ -5419,14 +5432,13 @@
return;
}
- if (enabled == mFocusedWindowRequestedPointerCapture) {
+ if (enabled == mCurrentPointerCaptureRequest.enable) {
ALOGW("Ignoring request to %s Pointer Capture: "
"window has %s requested pointer capture.",
enabled ? "enable" : "disable", enabled ? "already" : "not");
return;
}
- mFocusedWindowRequestedPointerCapture = enabled;
setPointerCaptureLocked(enabled);
} // release lock
@@ -6185,14 +6197,13 @@
}
void InputDispatcher::disablePointerCaptureForcedLocked() {
- if (!mFocusedWindowRequestedPointerCapture && !mWindowTokenWithPointerCapture) {
+ if (!mCurrentPointerCaptureRequest.enable && !mWindowTokenWithPointerCapture) {
return;
}
ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
- if (mFocusedWindowRequestedPointerCapture) {
- mFocusedWindowRequestedPointerCapture = false;
+ if (mCurrentPointerCaptureRequest.enable) {
setPointerCaptureLocked(false);
}
@@ -6209,14 +6220,16 @@
}
auto entry = std::make_unique<PointerCaptureChangedEntry>(mIdGenerator.nextId(), now(),
- false /* hasCapture */);
+ mCurrentPointerCaptureRequest);
mInboundQueue.push_front(std::move(entry));
}
void InputDispatcher::setPointerCaptureLocked(bool enabled) {
+ mCurrentPointerCaptureRequest.enable = enabled;
+ mCurrentPointerCaptureRequest.seq++;
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doSetPointerCaptureLockedInterruptible);
- commandEntry->enabled = enabled;
+ commandEntry->pointerCaptureRequest = mCurrentPointerCaptureRequest;
postCommandLocked(std::move(commandEntry));
}
@@ -6224,7 +6237,7 @@
android::inputdispatcher::CommandEntry* commandEntry) {
mLock.unlock();
- mPolicy->setPointerCapture(commandEntry->enabled);
+ mPolicy->setPointerCapture(commandEntry->pointerCaptureRequest);
mLock.lock();
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 9edf41c..30652c6 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -357,10 +357,12 @@
// Keeps track of the focused window per display and determines focus changes.
FocusResolver mFocusResolver GUARDED_BY(mLock);
- // Whether the focused window on the focused display has requested Pointer Capture.
- // The state of this variable should always be in sync with the state of Pointer Capture in the
- // policy, which is updated through setPointerCaptureLocked(enabled).
- bool mFocusedWindowRequestedPointerCapture GUARDED_BY(mLock);
+
+ // The enabled state of this request is true iff the focused window on the focused display has
+ // requested Pointer Capture. This request also contains the sequence number associated with the
+ // current request. The state of this variable should always be in sync with the state of
+ // Pointer Capture in the policy, and is only updated through setPointerCaptureLocked(request).
+ PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
// The window token that has Pointer Capture.
// This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
@@ -370,7 +372,7 @@
void disablePointerCaptureForcedLocked() REQUIRES(mLock);
// Set the Pointer Capture state in the Policy.
- void setPointerCaptureLocked(bool enabled) REQUIRES(mLock);
+ void setPointerCaptureLocked(bool enable) REQUIRES(mLock);
// Dispatcher state at time of last ANR.
std::string mLastAnrState GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 219f45a..fd591e0 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -156,7 +156,7 @@
*
* InputDispatcher is solely responsible for updating the Pointer Capture state.
*/
- virtual void setPointerCapture(bool enabled) = 0;
+ virtual void setPointerCapture(const PointerCaptureRequest&) = 0;
/* Notifies the policy that the drag window has moved over to another window */
virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
diff --git a/services/inputflinger/docs/pointer_capture.md b/services/inputflinger/docs/pointer_capture.md
index 8da699d..0b44187 100644
--- a/services/inputflinger/docs/pointer_capture.md
+++ b/services/inputflinger/docs/pointer_capture.md
@@ -17,6 +17,8 @@
`InputDispatcher` is responsible for controlling the state of Pointer Capture. Since the feature requires changes to how events are generated, Pointer Capture is configured in `InputReader`.
+We use a sequence number to synchronize different requests to enable Pointer Capture between InputReader and InputDispatcher.
+
### Enabling Pointer Capture
There are four key steps that take place when Pointer Capture is enabled:
@@ -40,5 +42,5 @@
`InputDispatcher` tracks two pieces of state information regarding Pointer Capture:
-- `mFocusedWindowRequestedPointerCapture`: Whether or not the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
+- `mCurrentPointerCaptureRequest`: The sequence number of the current Pointer Capture request. This request is enabled iff the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
- `mWindowTokenWithPointerCapture`: The Binder token of the `InputWindow` that currently has Pointer Capture. This is only updated during the dispatch cycle. If it is not `nullptr`, it signifies that the window was notified that it has Pointer Capture.
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 4b7d26d..fe74214 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -211,11 +211,12 @@
/* Describes a change in the state of Pointer Capture. */
struct NotifyPointerCaptureChangedArgs : public NotifyArgs {
- bool enabled;
+ // The sequence number of the Pointer Capture request, if enabled.
+ PointerCaptureRequest request;
inline NotifyPointerCaptureChangedArgs() {}
- NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, bool enabled);
+ NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other);
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 7fdbbfd..3c8ac1c 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -279,29 +279,30 @@
// True to show the location of touches on the touch screen as spots.
bool showTouches;
- // True if pointer capture is enabled.
- bool pointerCapture;
+ // The latest request to enable or disable Pointer Capture.
+ PointerCaptureRequest pointerCaptureRequest;
// The set of currently disabled input devices.
std::set<int32_t> disabledDevices;
- InputReaderConfiguration() :
- virtualKeyQuietTime(0),
+ InputReaderConfiguration()
+ : virtualKeyQuietTime(0),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
pointerGesturesEnabled(true),
- pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
- pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
- pointerGestureTapInterval(150 * 1000000LL), // 150 ms
- pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
- pointerGestureTapSlop(10.0f), // 10 pixels
+ pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
+ pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
+ pointerGestureTapInterval(150 * 1000000LL), // 150 ms
+ pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
+ pointerGestureTapSlop(10.0f), // 10 pixels
pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
- pointerGestureMultitouchMinDistance(15), // 15 pixels
- pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
+ pointerGestureMultitouchMinDistance(15), // 15 pixels
+ pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
pointerGestureSwipeMaxWidthRatio(0.25f),
pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f),
- showTouches(false), pointerCapture(false) { }
+ showTouches(false),
+ pointerCaptureRequest() {}
static std::string changesToString(uint32_t changes);
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 10c04f6..5120860 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -367,9 +367,15 @@
}
if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
- const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
- mConfig.pointerCapture);
- mQueuedListener->notifyPointerCaptureChanged(&args);
+ if (mCurrentPointerCaptureRequest == mConfig.pointerCaptureRequest) {
+ ALOGV("Skipping notifying pointer capture changes: "
+ "There was no change in the pointer capture state.");
+ } else {
+ mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
+ const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
+ mCurrentPointerCaptureRequest);
+ mQueuedListener->notifyPointerCaptureChanged(&args);
+ }
}
}
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index a00c5af..e44aa0f 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -230,6 +230,8 @@
uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
+ PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
+
// state queries
typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 437902a..2ac41b1 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -154,9 +154,9 @@
mHWheelScale = 1.0f;
}
- if ((!changes && config->pointerCapture) ||
+ if ((!changes && config->pointerCaptureRequest.enable) ||
(changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
- if (config->pointerCapture) {
+ if (config->pointerCaptureRequest.enable) {
if (mParameters.mode == Parameters::MODE_POINTER) {
mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f457315..ae89ca1 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -603,7 +603,7 @@
// Determine device mode.
if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
- mConfig.pointerGesturesEnabled && !mConfig.pointerCapture) {
+ mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.enable) {
mSource = AINPUT_SOURCE_MOUSE;
mDeviceMode = DeviceMode::POINTER;
if (hasStylus()) {
@@ -776,11 +776,12 @@
// preserve the cursor position.
if (mDeviceMode == DeviceMode::POINTER ||
(mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
- (mParameters.deviceType == Parameters::DeviceType::POINTER && mConfig.pointerCapture)) {
+ (mParameters.deviceType == Parameters::DeviceType::POINTER &&
+ mConfig.pointerCaptureRequest.enable)) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
- if (mConfig.pointerCapture) {
+ if (mConfig.pointerCaptureRequest.enable) {
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
}
} else {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index dff0752..7d4c638 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -239,19 +239,22 @@
mConfig.keyRepeatDelay = delay;
}
- void waitForSetPointerCapture(bool enabled) {
+ PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) {
std::unique_lock lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms,
[this, enabled]() REQUIRES(mLock) {
- return mPointerCaptureEnabled &&
- *mPointerCaptureEnabled ==
+ return mPointerCaptureRequest->enable ==
enabled;
})) {
- FAIL() << "Timed out waiting for setPointerCapture(" << enabled << ") to be called.";
+ ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << enabled
+ << ") to be called.";
+ return {};
}
- mPointerCaptureEnabled.reset();
+ auto request = *mPointerCaptureRequest;
+ mPointerCaptureRequest.reset();
+ return request;
}
void assertSetPointerCaptureNotCalled() {
@@ -259,11 +262,11 @@
base::ScopedLockAssertion assumeLocked(mLock);
if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
- FAIL() << "Expected setPointerCapture(enabled) to not be called, but was called. "
+ FAIL() << "Expected setPointerCapture(request) to not be called, but was called. "
"enabled = "
- << *mPointerCaptureEnabled;
+ << std::to_string(mPointerCaptureRequest->enable);
}
- mPointerCaptureEnabled.reset();
+ mPointerCaptureRequest.reset();
}
void assertDropTargetEquals(const sp<IBinder>& targetToken) {
@@ -281,7 +284,8 @@
std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
std::condition_variable mPointerCaptureChangedCondition;
- std::optional<bool> mPointerCaptureEnabled GUARDED_BY(mLock);
+
+ std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock);
// ANR handling
std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
@@ -398,9 +402,9 @@
mOnPointerDownToken = newToken;
}
- void setPointerCapture(bool enabled) override {
+ void setPointerCapture(const PointerCaptureRequest& request) override {
std::scoped_lock lock(mLock);
- mPointerCaptureEnabled = {enabled};
+ mPointerCaptureRequest = {request};
mPointerCaptureChangedCondition.notify_all();
}
@@ -1379,8 +1383,9 @@
return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
}
-static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(bool enabled) {
- return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), enabled);
+static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(
+ const PointerCaptureRequest& request) {
+ return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request);
}
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
@@ -4601,16 +4606,18 @@
mWindow->consumeFocusEvent(true);
}
- void notifyPointerCaptureChanged(bool enabled) {
- const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(enabled);
+ void notifyPointerCaptureChanged(const PointerCaptureRequest& request) {
+ const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(request);
mDispatcher->notifyPointerCaptureChanged(&args);
}
- void requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, bool enabled) {
+ PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window,
+ bool enabled) {
mDispatcher->requestPointerCapture(window->getToken(), enabled);
- mFakePolicy->waitForSetPointerCapture(enabled);
- notifyPointerCaptureChanged(enabled);
+ auto request = mFakePolicy->assertSetPointerCaptureCalled(enabled);
+ notifyPointerCaptureChanged(request);
window->consumeCaptureEvent(enabled);
+ return request;
}
};
@@ -4632,7 +4639,7 @@
}
TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLosesFocus) {
- requestAndVerifyPointerCapture(mWindow, true);
+ auto request = requestAndVerifyPointerCapture(mWindow, true);
setFocusedWindow(mSecondWindow);
@@ -4640,26 +4647,26 @@
mWindow->consumeCaptureEvent(false);
mWindow->consumeFocusEvent(false);
mSecondWindow->consumeFocusEvent(true);
- mFakePolicy->waitForSetPointerCapture(false);
+ mFakePolicy->assertSetPointerCaptureCalled(false);
// Ensure that additional state changes from InputReader are not sent to the window.
- notifyPointerCaptureChanged(false);
- notifyPointerCaptureChanged(true);
- notifyPointerCaptureChanged(false);
+ notifyPointerCaptureChanged({});
+ notifyPointerCaptureChanged(request);
+ notifyPointerCaptureChanged({});
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
mFakePolicy->assertSetPointerCaptureNotCalled();
}
TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerCapture) {
- requestAndVerifyPointerCapture(mWindow, true);
+ auto request = requestAndVerifyPointerCapture(mWindow, true);
// InputReader unexpectedly disables and enables pointer capture.
- notifyPointerCaptureChanged(false);
- notifyPointerCaptureChanged(true);
+ notifyPointerCaptureChanged({});
+ notifyPointerCaptureChanged(request);
// Ensure that Pointer Capture is disabled.
- mFakePolicy->waitForSetPointerCapture(false);
+ mFakePolicy->assertSetPointerCaptureCalled(false);
mWindow->consumeCaptureEvent(false);
mWindow->assertNoEvents();
}
@@ -4669,24 +4676,43 @@
// The first window loses focus.
setFocusedWindow(mSecondWindow);
- mFakePolicy->waitForSetPointerCapture(false);
+ mFakePolicy->assertSetPointerCaptureCalled(false);
mWindow->consumeCaptureEvent(false);
// Request Pointer Capture from the second window before the notification from InputReader
// arrives.
mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
- mFakePolicy->waitForSetPointerCapture(true);
+ auto request = mFakePolicy->assertSetPointerCaptureCalled(true);
// InputReader notifies Pointer Capture was disabled (because of the focus change).
- notifyPointerCaptureChanged(false);
+ notifyPointerCaptureChanged({});
// InputReader notifies Pointer Capture was enabled (because of mSecondWindow's request).
- notifyPointerCaptureChanged(true);
+ notifyPointerCaptureChanged(request);
mSecondWindow->consumeFocusEvent(true);
mSecondWindow->consumeCaptureEvent(true);
}
+TEST_F(InputDispatcherPointerCaptureTests, EnableRequestFollowsSequenceNumbers) {
+ // App repeatedly enables and disables capture.
+ mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+ auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+ mDispatcher->requestPointerCapture(mWindow->getToken(), false);
+ mFakePolicy->assertSetPointerCaptureCalled(false);
+ mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+ auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+
+ // InputReader notifies that PointerCapture has been enabled for the first request. Since the
+ // first request is now stale, this should do nothing.
+ notifyPointerCaptureChanged(firstRequest);
+ mWindow->assertNoEvents();
+
+ // InputReader notifies that the second request was enabled.
+ notifyPointerCaptureChanged(secondRequest);
+ mWindow->consumeCaptureEvent(true);
+}
+
class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
protected:
constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 997cbe8..38dfe40 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -299,8 +299,9 @@
transform = t;
}
- void setPointerCapture(bool enabled) {
- mConfig.pointerCapture = enabled;
+ PointerCaptureRequest setPointerCapture(bool enabled) {
+ mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+ return mConfig.pointerCaptureRequest;
}
void setShowTouches(bool enabled) {
@@ -314,6 +315,8 @@
float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
private:
+ uint32_t mNextPointerCaptureSequenceNumber = 0;
+
DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
int32_t orientation, bool isActive,
const std::string& uniqueId,
@@ -1961,24 +1964,24 @@
TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
NotifyPointerCaptureChangedArgs args;
- mFakePolicy->setPointerCapture(true);
+ auto request = mFakePolicy->setPointerCapture(true);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasCalled(&args);
- ASSERT_TRUE(args.enabled) << "Pointer Capture should be enabled.";
+ ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled.";
+ ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match.";
mFakePolicy->setPointerCapture(false);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasCalled(&args);
- ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+ ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled.";
- // Verify that the Pointer Capture state is re-configured correctly when the configuration value
+ // Verify that the Pointer Capture state is not updated when the configuration value
// does not change.
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
mReader->loopOnce();
- mFakeListener->assertNotifyCaptureWasCalled(&args);
- ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+ mFakeListener->assertNotifyCaptureWasNotCalled();
}
class FakeVibratorInputMapper : public FakeInputMapper {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index fb7de97..6a26c63 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -100,6 +100,11 @@
"to have been called."));
}
+void TestInputListener::assertNotifyCaptureWasNotCalled() {
+ ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyPointerCaptureChangedArgs>(
+ "notifyPointerCaptureChanged() should not be called."));
+}
+
template <class NotifyArgsType>
void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
std::unique_lock<std::mutex> lock(mLock);
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 0ffcaaa..0a1dc4b 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -55,6 +55,7 @@
void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr);
+ void assertNotifyCaptureWasNotCalled();
void assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs = nullptr);
void assertNotifyVibratorStateWasCalled(NotifyVibratorStateArgs* outEventArgs = nullptr);
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index b596708..e6b118b 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -79,10 +79,7 @@
"libpermission",
],
- pgo: {
- sampling: true,
- profile_file: "sensorservice/libsensorservice.profdata",
- },
+ afdo: true,
}
cc_binary {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index eb31e92..40b1fd0 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -101,7 +101,7 @@
// TODO (marissaw): this library is not used by surfaceflinger. This is here so
// the library compiled in a way that is accessible to system partition when running
// IMapper's VTS.
- required: ["libgralloctypes"]
+ required: ["libgralloctypes"],
}
cc_defaults {
@@ -114,10 +114,7 @@
thin: true,
},
whole_program_vtables: true, // Requires ThinLTO
- pgo: {
- sampling: true,
- profile_file: "surfaceflinger/surfaceflinger.profdata",
- },
+ afdo: true,
// TODO(b/131771163): Fix broken fuzzer support with LTO.
sanitize: {
fuzzer: false,
@@ -247,7 +244,7 @@
"libSurfaceFlingerProp",
],
- logtags: ["EventLog/EventLogTags.logtags"],
+ logtags: ["EventLog/EventLogTags.logtags"],
}
subdirs = [
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 75dd51c..d8cead5 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -820,11 +820,7 @@
}
bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
- sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get());
- if (handle == nullptr) {
- return false;
- }
- sp<Layer> relative = handle->owner.promote();
+ sp<Layer> relative = fromHandle(relativeToHandle).promote();
if (relative == nullptr) {
return false;
}
@@ -1610,8 +1606,7 @@
bool Layer::reparent(const sp<IBinder>& newParentHandle) {
sp<Layer> newParent;
if (newParentHandle != nullptr) {
- auto handle = static_cast<Handle*>(newParentHandle.get());
- newParent = handle->owner.promote();
+ newParent = fromHandle(newParentHandle).promote();
if (newParent == nullptr) {
ALOGE("Unable to promote Layer handle");
return false;
@@ -1986,24 +1981,10 @@
mDrawingParent = mCurrentParent;
}
-static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) {
- if (weakBinderHandle == nullptr) {
- return nullptr;
- }
- sp<IBinder> binderHandle = weakBinderHandle.promote();
- if (binderHandle == nullptr) {
- return nullptr;
- }
- sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get());
- if (handle == nullptr) {
- return nullptr;
- }
- return handle->owner;
-}
void Layer::setInputInfo(const InputWindowInfo& info) {
mDrawingState.inputInfo = info;
- mDrawingState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
+ mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
mDrawingState.modified = true;
mFlinger->mInputInfoChanged = true;
setTransactionFlags(eTransactionNeeded);
@@ -2562,6 +2543,23 @@
mFlinger->mNumClones++;
}
+const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle");
+
+wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) {
+ if (handleBinder == nullptr) {
+ return nullptr;
+ }
+
+ BBinder* b = handleBinder->localBinder();
+ if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) {
+ return nullptr;
+ }
+
+ // We can safely cast this binder since its local and we verified its interface descriptor.
+ sp<Handle> handle = static_cast<Handle*>(handleBinder.get());
+ return handle->owner;
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 59f5b0d..8905548 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -289,16 +289,17 @@
class LayerCleaner {
sp<SurfaceFlinger> mFlinger;
sp<Layer> mLayer;
+ BBinder* mHandle;
protected:
~LayerCleaner() {
// destroy client resources
- mFlinger->onHandleDestroyed(mLayer);
+ mFlinger->onHandleDestroyed(mHandle, mLayer);
}
public:
- LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
- : mFlinger(flinger), mLayer(layer) {}
+ LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle)
+ : mFlinger(flinger), mLayer(layer), mHandle(handle) {}
};
/*
@@ -312,11 +313,15 @@
class Handle : public BBinder, public LayerCleaner {
public:
Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
- : LayerCleaner(flinger, layer), owner(layer) {}
+ : LayerCleaner(flinger, layer, this), owner(layer) {}
+ const String16& getInterfaceDescriptor() const override { return kDescriptor; }
+ static const String16 kDescriptor;
wp<Layer> owner;
};
+ static wp<Layer> fromHandle(const sp<IBinder>& handle);
+
explicit Layer(const LayerCreationArgs& args);
virtual ~Layer();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 0334d70..71cd52d 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -133,42 +133,37 @@
return true;
}
-float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
- const RefreshRate& refreshRate,
- bool isSeamlessSwitch) const {
- if (!isVoteAllowed(layer, refreshRate)) {
- return 0;
- }
-
- // Slightly prefer seamless switches.
- constexpr float kSeamedSwitchPenalty = 0.95f;
- const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
-
- // If the layer wants Max, give higher score to the higher refresh rate
- if (layer.vote == LayerVoteType::Max) {
- const auto ratio =
- refreshRate.fps.getValue() / mAppRequestRefreshRates.back()->fps.getValue();
- // use ratio^2 to get a lower score the more we get further from peak
- return ratio * ratio;
- }
+float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(
+ const LayerRequirement& layer, const RefreshRate& refreshRate) const {
+ constexpr float kScoreForFractionalPairs = .8f;
const auto displayPeriod = refreshRate.getVsyncPeriod();
const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
if (layer.vote == LayerVoteType::ExplicitDefault) {
// Find the actual rate the layer will render, assuming
- // that layerPeriod is the minimal time to render a frame
+ // that layerPeriod is the minimal period to render a frame.
+ // For example if layerPeriod is 20ms and displayPeriod is 16ms,
+ // then the actualLayerPeriod will be 32ms, because it is the
+ // smallest multiple of the display period which is >= layerPeriod.
auto actualLayerPeriod = displayPeriod;
int multiplier = 1;
while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
multiplier++;
actualLayerPeriod = displayPeriod * multiplier;
}
+
+ // Because of the threshold we used above it's possible that score is slightly
+ // above 1.
return std::min(1.0f,
static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
}
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
+ if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) {
+ return kScoreForFractionalPairs;
+ }
+
// Calculate how many display vsyncs we need to present a single frame for this
// layer
const auto [displayFramesQuotient, displayFramesRemainder] =
@@ -176,7 +171,7 @@
static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
if (displayFramesRemainder == 0) {
// Layer desired refresh rate matches the display rate.
- return 1.0f * seamlessness;
+ return 1.0f;
}
if (displayFramesQuotient == 0) {
@@ -194,7 +189,29 @@
iter++;
}
- return (1.0f / iter) * seamlessness;
+ return (1.0f / iter);
+ }
+
+ return 0;
+}
+
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
+ const RefreshRate& refreshRate,
+ bool isSeamlessSwitch) const {
+ if (!isVoteAllowed(layer, refreshRate)) {
+ return 0;
+ }
+
+ // Slightly prefer seamless switches.
+ constexpr float kSeamedSwitchPenalty = 0.95f;
+ const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+ // If the layer wants Max, give higher score to the higher refresh rate
+ if (layer.vote == LayerVoteType::Max) {
+ const auto ratio = refreshRate.getFps().getValue() /
+ mAppRequestRefreshRates.back()->getFps().getValue();
+ // use ratio^2 to get a lower score the more we get further from peak
+ return ratio * ratio;
}
if (layer.vote == LayerVoteType::ExplicitExact) {
@@ -209,7 +226,18 @@
return divider == 1;
}
- return 0;
+ // If the layer frame rate is a divider of the refresh rate it should score
+ // the highest score.
+ if (getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+ return 1.0f * seamlessness;
+ }
+
+ // The layer frame rate is not a divider of the refresh rate,
+ // there is a small penalty attached to the score to favor the frame rates
+ // the exactly matches the display refresh rate or a multiple.
+ constexpr float kNonExactMatchingPenalty = 0.95f;
+ return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
+ kNonExactMatchingPenalty;
}
struct RefreshRateScore {
@@ -421,7 +449,7 @@
const auto layerScore =
calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
- ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+ ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
scores[i].refreshRate->getName().c_str(), layerScore);
scores[i].score += weight * layerScore;
}
@@ -582,7 +610,7 @@
template <typename Iter>
const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
- constexpr auto EPSILON = 0.001f;
+ constexpr auto kEpsilon = 0.0001f;
const RefreshRate* bestRefreshRate = begin->refreshRate;
float max = begin->score;
for (auto i = begin; i != end; ++i) {
@@ -591,7 +619,7 @@
ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100));
- if (score > max * (1 + EPSILON)) {
+ if (score > max * (1 + kEpsilon)) {
max = score;
bestRefreshRate = refreshRate;
}
@@ -911,7 +939,10 @@
int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
// This calculation needs to be in sync with the java code
// in DisplayManagerService.getDisplayInfoForFrameRateOverride
- constexpr float kThreshold = 0.1f;
+
+ // The threshold must be smaller than 0.001 in order to differentiate
+ // between the fractional pairs (e.g. 59.94 and 60).
+ constexpr float kThreshold = 0.0009f;
const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
const auto numPeriodsRounded = std::round(numPeriods);
if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
@@ -921,6 +952,17 @@
return static_cast<int>(numPeriodsRounded);
}
+bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
+ if (smaller.getValue() > bigger.getValue()) {
+ return isFractionalPairOrMultiple(bigger, smaller);
+ }
+
+ const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
+ constexpr float kCoef = 1000.f / 1001.f;
+ return bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier / kCoef)) ||
+ bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier * kCoef));
+}
+
void RefreshRateConfigs::dump(std::string& result) const {
std::lock_guard lock(mLock);
base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index dfd1395..913e5ee 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -342,6 +342,10 @@
// layer refresh rate.
static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
+ // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
+ // for an integer t.
+ static bool isFractionalPairOrMultiple(Fps, Fps);
+
using UidToFrameRateOverride = std::map<uid_t, Fps>;
// Returns the frame rate override for each uid.
//
@@ -405,6 +409,9 @@
float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
bool isSeamlessSwitch) const REQUIRES(mLock);
+ float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&,
+ const RefreshRate&) const REQUIRES(mLock);
+
// The list of refresh rates, indexed by display modes ID. This may change after this
// object is initialized.
AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e0b3640..64ad178 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -386,6 +386,13 @@
}
const auto modeId = *mFeatures.modeId;
+ // If the modeId is not the current mode, this means that a
+ // mode change is in progress. In that case we shouldn't dispatch an event
+ // as it will be dispatched when the current mode changes.
+ if (mRefreshRateConfigs.getCurrentRefreshRate().getModeId() != modeId) {
+ return;
+ }
+
const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromModeId(modeId).getVsyncPeriod();
// If there is no change from cached mode, there is no need to dispatch an event
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index abc49bf..893dc2a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1960,7 +1960,10 @@
// 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());
+ {
+ Mutex::Autolock lock(mStateLock);
+ setActiveModeInternal();
+ }
}
if (framePending) {
@@ -2407,6 +2410,7 @@
mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
if (isDisplayConnected && !display->isPoweredOn()) {
+ getRenderEngine().cleanupPostRender();
return;
}
@@ -3622,7 +3626,7 @@
sp<Layer> layer = nullptr;
if (s.surface) {
- layer = fromHandleLocked(s.surface).promote();
+ layer = fromHandle(s.surface).promote();
} else if (s.hasBufferChanges()) {
ALOGW("Transaction with buffer, but no Layer?");
continue;
@@ -3789,7 +3793,7 @@
setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
postTime, permissions, listenerCallbacksWithSurfaces);
if ((flags & eAnimation) && state.state.surface) {
- if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
+ if (const auto layer = fromHandle(state.state.surface).promote(); layer) {
mScheduler->recordLayerHistory(layer.get(),
isAutoTimestamp ? 0 : desiredPresentTime,
LayerHistory::LayerUpdateType::AnimationTX);
@@ -3944,13 +3948,11 @@
if (what & layer_state_t::eLayerCreated) {
layer = handleLayerCreatedLocked(s.surface);
if (layer) {
- // put the created layer into mLayersByLocalBinderToken.
- mLayersByLocalBinderToken.emplace(s.surface->localBinder(), layer);
flags |= eTransactionNeeded | eTraversalNeeded;
mLayersAdded = true;
}
} else {
- layer = fromHandleLocked(s.surface).promote();
+ layer = fromHandle(s.surface).promote();
}
} else {
// The client may provide us a null handle. Treat it as if the layer was removed.
@@ -4278,7 +4280,7 @@
{
Mutex::Autolock _l(mStateLock);
- mirrorFrom = fromHandleLocked(mirrorFromHandle).promote();
+ mirrorFrom = fromHandle(mirrorFromHandle).promote();
if (!mirrorFrom) {
return NAME_NOT_FOUND;
}
@@ -4476,7 +4478,7 @@
setTransactionFlags(eTransactionNeeded);
}
-void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) {
+void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) {
Mutex::Autolock lock(mStateLock);
// If a layer has a parent, we allow it to out-live it's handle
// with the idea that the parent holds a reference and will eventually
@@ -4487,17 +4489,7 @@
mCurrentState.layersSortedByZ.remove(layer);
}
markLayerPendingRemovalLocked(layer);
-
- auto it = mLayersByLocalBinderToken.begin();
- while (it != mLayersByLocalBinderToken.end()) {
- if (it->second == layer) {
- mBufferCountTracker.remove(it->first->localBinder());
- it = mLayersByLocalBinderToken.erase(it);
- } else {
- it++;
- }
- }
-
+ mBufferCountTracker.remove(handle);
layer.clear();
}
@@ -6101,7 +6093,7 @@
{
Mutex::Autolock lock(mStateLock);
- parent = fromHandleLocked(args.layerHandle).promote();
+ parent = fromHandle(args.layerHandle).promote();
if (parent == nullptr || parent->isRemovedFromCurrentState()) {
ALOGE("captureLayers called with an invalid or removed parent");
return NAME_NOT_FOUND;
@@ -6132,7 +6124,7 @@
reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
for (const auto& handle : args.excludeHandles) {
- sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
+ sp<Layer> excludeLayer = fromHandle(handle).promote();
if (excludeLayer != nullptr) {
excludeLayers.emplace(excludeLayer);
} else {
@@ -6628,24 +6620,8 @@
}
}
-wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
- Mutex::Autolock _l(mStateLock);
- return fromHandleLocked(handle);
-}
-
-wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const {
- BBinder* b = nullptr;
- if (handle) {
- b = handle->localBinder();
- }
- if (b == nullptr) {
- return nullptr;
- }
- auto it = mLayersByLocalBinderToken.find(b);
- if (it != mLayersByLocalBinderToken.end()) {
- return it->second;
- }
- return nullptr;
+wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const {
+ return Layer::fromHandle(handle);
}
void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
@@ -6950,7 +6926,7 @@
sp<Layer> parent;
bool allowAddRoot = state->addToRoot;
if (state->initialParent != nullptr) {
- parent = fromHandleLocked(state->initialParent.promote()).promote();
+ parent = fromHandle(state->initialParent.promote()).promote();
if (parent == nullptr) {
ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
allowAddRoot = false;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index fa19b7f..4e8e614 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -327,8 +327,7 @@
// Returns nullptr if the handle does not point to an existing layer.
// Otherwise, returns a weak reference so that callers off the main-thread
// won't accidentally hold onto the last strong reference.
- wp<Layer> fromHandle(const sp<IBinder>& handle);
- wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
+ wp<Layer> fromHandle(const sp<IBinder>& handle) const;
// If set, disables reusing client composition buffers. This can be set by
// debug.sf.disable_client_composition_cache
@@ -899,7 +898,7 @@
// called when all clients have released all their references to
// this layer meaning it is entirely safe to destroy all
// resources associated to this layer.
- void onHandleDestroyed(sp<Layer>& layer);
+ void onHandleDestroyed(BBinder* handle, sp<Layer>& layer);
void markLayerPendingRemovalLocked(const sp<Layer>& layer);
// add a layer to SurfaceFlinger
@@ -1291,8 +1290,6 @@
std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
} mVirtualDisplayIdGenerators;
- std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
-
// don't use a lock for these, we don't care
int mDebugRegion = 0;
bool mDebugDisableHWC = false;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 23ab7c8..837b2e8 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -183,12 +183,9 @@
return NO_ERROR;
}
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) const {
- const sp<const IBinder>& handle(weakHandle.promote());
- const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
- const sp<const Layer> layer(layerHandle->owner.promote());
- // layer could be a nullptr at this point
- return layer;
+const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const {
+ sp<IBinder> handle = weakHandle.promote();
+ return Layer::fromHandle(handle).promote();
}
int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
@@ -203,12 +200,11 @@
return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
}
-int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<const IBinder>& handle) const {
+int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const {
if (handle == nullptr) {
return -1;
}
- const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
- const sp<const Layer> layer(layerHandle->owner.promote());
+ const sp<const Layer> layer = Layer::fromHandle(handle).promote();
return layer == nullptr ? -1 : getLayerId(layer);
}
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 673f9e7..c955596 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -133,10 +133,10 @@
void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
status_t writeProtoFileLocked();
- const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle) const;
+ const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const;
int32_t getLayerId(const sp<const Layer>& layer) const;
int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
- int32_t getLayerIdFromHandle(const sp<const IBinder>& weakHandle) const;
+ int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const;
Increment* createTraceIncrementLocked();
void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 3423bd5..112fabd 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -99,9 +99,15 @@
static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4);
static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5);
static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
+ static inline const DisplayModeId HWC_CONFIG_ID_24 = DisplayModeId(7);
+ static inline const DisplayModeId HWC_CONFIG_ID_24_FRAC = DisplayModeId(8);
+ static inline const DisplayModeId HWC_CONFIG_ID_30_FRAC = DisplayModeId(9);
+ static inline const DisplayModeId HWC_CONFIG_ID_60_FRAC = DisplayModeId(10);
// Test configs
DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
+ DisplayModePtr mConfig60Frac =
+ createDisplayMode(HWC_CONFIG_ID_60_FRAC, 0, Fps(59.94f).getPeriodNsecs());
DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
DisplayModePtr mConfig90DifferentGroup =
createDisplayMode(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
@@ -117,9 +123,15 @@
DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
DisplayModePtr mConfig30DifferentGroup =
createDisplayMode(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
+ DisplayModePtr mConfig30Frac =
+ createDisplayMode(HWC_CONFIG_ID_30_FRAC, 0, Fps(29.97f).getPeriodNsecs());
+ DisplayModePtr mConfig25 = createDisplayMode(HWC_CONFIG_ID_25, 0, Fps(25.0f).getPeriodNsecs());
DisplayModePtr mConfig25DifferentGroup =
createDisplayMode(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
+ DisplayModePtr mConfig24 = createDisplayMode(HWC_CONFIG_ID_24, 0, Fps(24.0f).getPeriodNsecs());
+ DisplayModePtr mConfig24Frac =
+ createDisplayMode(HWC_CONFIG_ID_24_FRAC, 0, Fps(23.976f).getPeriodNsecs());
// Test device configurations
// The positions of the configs in the arrays below MUST match their IDs. For example,
@@ -146,6 +158,11 @@
mConfig50};
DisplayModes m60_120Device = {mConfig60, mConfig120};
+ // This is a typical TV configuration.
+ DisplayModes m24_25_30_50_60WithFracDevice = {mConfig24, mConfig24Frac, mConfig25,
+ mConfig30, mConfig30Frac, mConfig50,
+ mConfig60, mConfig60Frac};
+
// Expected RefreshRate objects
RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, Fps(60),
RefreshRate::ConstructorTag(0)};
@@ -166,7 +183,6 @@
RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
RefreshRate::ConstructorTag(0)};
-private:
DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
ui::Size resolution = ui::Size());
};
@@ -1237,7 +1253,109 @@
const auto& refreshRate =
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
EXPECT_TRUE(refreshRate.getFps().equalsWithMargin(Fps(test.second)))
- << "Expecting " << test.first << "fps => " << test.second << "Hz";
+ << "Expecting " << test.first << "fps => " << test.second << "Hz"
+ << " but it was " << refreshRate.getFps();
+ }
+}
+
+TEST_F(RefreshRateConfigsTest,
+ getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ // Test that 23.976 will choose 24 if 23.976 is not supported
+ {
+ android::DisplayModes modes = {mConfig24, mConfig25, mConfig30,
+ mConfig30Frac, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.desiredRefreshRate = Fps(23.976f);
+ lr.name = "ExplicitExactOrMultiple 23.976 fps";
+ EXPECT_EQ(HWC_CONFIG_ID_24,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+ }
+
+ // Test that 24 will choose 23.976 if 24 is not supported
+ {
+ android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+ mConfig30Frac, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+ lr.desiredRefreshRate = Fps(24.f);
+ lr.name = "ExplicitExactOrMultiple 24 fps";
+ EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+ }
+
+ // Test that 29.97 will prefer 59.94 over 60 and 30
+ {
+ android::DisplayModes modes = {mConfig24, mConfig24Frac, mConfig25,
+ mConfig30, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+ lr.desiredRefreshRate = Fps(29.97f);
+ lr.name = "ExplicitExactOrMultiple 29.97f fps";
+ EXPECT_EQ(HWC_CONFIG_ID_60_FRAC,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ // Test that voting for supported refresh rate will select this refresh rate
+ {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ for (auto desiredRefreshRate : {23.976f, 24.f, 25.f, 29.97f, 30.f, 50.f, 59.94f, 60.f}) {
+ lr.vote = LayerVoteType::ExplicitExact;
+ lr.desiredRefreshRate = Fps(desiredRefreshRate);
+ std::stringstream ss;
+ ss << "ExplicitExact " << desiredRefreshRate << " fps";
+ lr.name = ss.str();
+
+ auto selecteRefreshRate =
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+
+ EXPECT_TRUE(selecteRefreshRate.getFps().equalsWithMargin(lr.desiredRefreshRate))
+ << "Expecting " << lr.desiredRefreshRate << " but it was "
+ << selecteRefreshRate.getFps();
+ }
+ }
+
+ // Test that 23.976 will choose 24 if 23.976 is not supported
+ {
+ android::DisplayModes modes = {mConfig24, mConfig25, mConfig30,
+ mConfig30Frac, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+ lr.vote = LayerVoteType::ExplicitExact;
+ lr.desiredRefreshRate = Fps(23.976f);
+ lr.name = "ExplicitExact 23.976 fps";
+ EXPECT_EQ(HWC_CONFIG_ID_24,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+ }
+
+ // Test that 24 will choose 23.976 if 24 is not supported
+ {
+ android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+ mConfig30Frac, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+ lr.desiredRefreshRate = Fps(24.f);
+ lr.name = "ExplicitExact 24 fps";
+ EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
}
}
@@ -2035,6 +2153,100 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
}
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
+ RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+ /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 0.5f},
+ LayerRequirement{.weight = 0.5f}};
+ auto& explicitDefaultLayer = layers[0];
+ auto& explicitExactOrMultipleLayer = layers[1];
+
+ explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+ explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+
+ explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
+ explicitDefaultLayer.name = "ExplicitDefault";
+ explicitDefaultLayer.desiredRefreshRate = Fps(59.94f);
+
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) {
+ constexpr int kMinRefreshRate = 10;
+ constexpr int kMaxRefreshRate = 240;
+
+ DisplayModes displayModes;
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ constexpr int32_t kGroup = 0;
+ const auto refreshRate = Fps(static_cast<float>(fps));
+ displayModes.push_back(
+ createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs()));
+ }
+
+ const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(displayModes,
+ /*currentConfigId=*/displayModes[0]->getId());
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
+ layers[0].desiredRefreshRate = fps;
+ layers[0].vote = vote;
+ EXPECT_EQ(fps.getIntValue(),
+ refreshRateConfigs->getBestRefreshRate(layers, globalSignals)
+ .getFps()
+ .getIntValue())
+ << "Failed for " << RefreshRateConfigs::layerVoteTypeString(vote);
+ };
+
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ const auto refreshRate = Fps(static_cast<float>(fps));
+ testRefreshRate(refreshRate, LayerVoteType::Heuristic);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
+ }
+}
+
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
+ const DisplayModes displayModes = {
+ createDisplayMode(DisplayModeId(0), 0, Fps(43.0f).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(1), 0, Fps(53.0f).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(2), 0, Fps(55.0f).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(3), 0, Fps(60.0f).getPeriodNsecs()),
+ };
+
+ const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(displayModes,
+ /*currentConfigId=*/displayModes[0]->getId());
+
+ const auto layers = std::vector<LayerRequirement>{
+ LayerRequirement{
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = Fps(43.0f),
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.41f,
+ },
+ LayerRequirement{
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = Fps(53.0f),
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.41f,
+ },
+ };
+
+ EXPECT_EQ(53,
+ refreshRateConfigs->getBestRefreshRate(layers, globalSignals).getFps().getIntValue());
+}
+
TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
EXPECT_TRUE(mExpected60Config < mExpected90Config);
EXPECT_FALSE(mExpected60Config < mExpected60Config);
@@ -2123,7 +2335,38 @@
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
- EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f)));
+
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(25.f)));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(23.976f)));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(30.f), Fps(29.97f)));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f)));
+}
+
+TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(23.976f), Fps(24.f)));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(24.f), Fps(23.976f)));
+
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(30.f)));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(30.f), Fps(29.97f)));
+
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(59.94f), Fps(60.f)));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(60.f), Fps(59.94f)));
+
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(60.f)));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(60.f), Fps(29.97f)));
+
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(59.94f), Fps(30.f)));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(30.f), Fps(59.94f)));
+
+ const std::vector<float> refreshRates = {23.976f, 24.f, 25.f, 29.97f, 30.f, 50.f, 59.94f, 60.f};
+ for (auto refreshRate : refreshRates) {
+ EXPECT_FALSE(
+ RefreshRateConfigs::isFractionalPairOrMultiple(Fps(refreshRate), Fps(refreshRate)));
+ }
+
+ EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(24.f), Fps(25.f)));
+ EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(23.978f), Fps(25.f)));
+ EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(59.94f)));
}
TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index d7fdab5..cf774fd 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -979,6 +979,8 @@
void QueryPresentationProperties(
VkPhysicalDevice physicalDevice,
VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
+ ATRACE_CALL();
+
// Request the android-specific presentation properties via GPDP2
VkPhysicalDeviceProperties2 properties = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
@@ -994,7 +996,17 @@
presentation_properties->pNext = nullptr;
presentation_properties->sharedImage = VK_FALSE;
- GetPhysicalDeviceProperties2(physicalDevice, &properties);
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceProperties2) {
+ // >= 1.1 driver, supports core GPDP2 entrypoint.
+ driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
+ } else if (driver.GetPhysicalDeviceProperties2KHR) {
+ // Old driver, but may support presentation properties
+ // if we have the GPDP2 extension. Otherwise, no presentation
+ // properties supported.
+ driver.GetPhysicalDeviceProperties2KHR(physicalDevice, &properties);
+ }
}
VkResult EnumerateDeviceExtensionProperties(
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 8c54a0e..6191063 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1205,18 +1205,26 @@
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
- // in place for that to work yet. Note we only lie to the lower layer-- we
- // don't want to give the app back a swapchain with extra images (which they
- // can't actually use!).
- err = native_window_set_buffer_count(window, std::max(2u, num_images));
+ // Lower layer insists that we have at least min_undequeued_buffers + 1
+ // buffers. This is wasteful and we'd like to relax it in the shared case,
+ // but not all the pieces are in place for that to work yet. Note we only
+ // lie to the lower layer--we don't want to give the app back a swapchain
+ // with extra images (which they can't actually use!).
+ uint32_t min_buffer_count = min_undequeued_buffers + 1;
+ err = native_window_set_buffer_count(
+ window, std::max(min_buffer_count, num_images));
if (err != android::OK) {
ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
strerror(-err), err);
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;