Merge "SF: add composer optional features"
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 8da1352..34ccb21 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
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/dexopt.cpp b/cmds/installd/dexopt.cpp
index 2bcf2d4..b6f42ad 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"
@@ -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.
  *
@@ -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/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/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/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/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9670d7b..8dbdc1d 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>
@@ -224,6 +225,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 +497,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 +837,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 +951,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 +992,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 +1101,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 +1112,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 +1129,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 +1192,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/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index e5560f8..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) {
@@ -571,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'.
  */
@@ -714,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>);
+    }
 }
 
 /**
@@ -724,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>);
+    }
 }
 
 /**
@@ -735,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>);
+    }
 }
 
 /**
@@ -747,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
@@ -1132,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/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 32406e5..077d915 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -233,6 +233,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/ui/Android.bp b/libs/ui/Android.bp
index a7b5900..936e653 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -138,6 +138,7 @@
         "HdrCapabilities.cpp",
         "PixelFormat.cpp",
         "PublicFormat.cpp",
+        "StaticAsserts.cpp",
         "StaticDisplayInfo.cpp",
     ],
 
diff --git a/libs/ui/StaticAsserts.cpp b/libs/ui/StaticAsserts.cpp
new file mode 100644
index 0000000..85da64f
--- /dev/null
+++ b/libs/ui/StaticAsserts.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/PixelFormat.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+
+// Ideally, PIXEL_FORMAT_R_8 would simply be defined to match the aidl PixelFormat, but
+// PixelFormat.h (where PIXEL_FORMAT_R_8 is defined) is pulled in by builds for
+// which there is no aidl build (e.g. Windows).
+static_assert(android::PIXEL_FORMAT_R_8 ==static_cast<int32_t>(
+                                  aidl::android::hardware::graphics::common::PixelFormat::R_8));
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/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index e6bcec8..94ad6c3 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -539,6 +539,7 @@
             break;
         case Composition::CURSOR:
         case Composition::DEVICE:
+        case Composition::DISPLAY_DECORATION:
             writeBufferStateToHWC(hwcLayer, outputIndependentState, skipLayer);
             break;
         case Composition::INVALID:
@@ -684,6 +685,7 @@
 
         case Composition::DEVICE:
         case Composition::SOLID_COLOR:
+        case Composition::DISPLAY_DECORATION:
             result = (to == Composition::CLIENT);
             break;
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 074673e..2d53583 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -114,6 +114,10 @@
                 plan.addLayerType(
                         aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR);
                 continue;
+            case 'A':
+                plan.addLayerType(aidl::android::hardware::graphics::composer3::Composition::
+                                          DISPLAY_DECORATION);
+                continue;
             default:
                 return std::nullopt;
         }
@@ -143,6 +147,10 @@
             case aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR:
                 result.append("S");
                 break;
+            case aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION:
+                // A for "Alpha", since the decoration is an alpha layer.
+                result.append("A");
+                break;
         }
     }
     return result;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 77e704f..7236868 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -111,6 +111,8 @@
             return "Cursor";
         case aidl::android::hardware::graphics::composer3::Composition::SIDEBAND:
             return "Sideband";
+        case aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION:
+            return "DisplayDecoration";
         default:
             return "Unknown";
     }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 5f8e434..ab16027 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -30,6 +30,7 @@
 #include <hidl/HidlTransportUtils.h>
 #include <log/log.h>
 #include <utils/Trace.h>
+#include "Hal.h"
 
 #include <algorithm>
 #include <cinttypes>
@@ -627,6 +628,11 @@
 
 static IComposerClient::Composition to_hidl_type(
         aidl::android::hardware::graphics::composer3::Composition type) {
+    LOG_ALWAYS_FATAL_IF(static_cast<int32_t>(type) >
+                                static_cast<int32_t>(IComposerClient::Composition::SIDEBAND),
+                        "Trying to use %s, which is not supported by HidlComposer!",
+                        android::to_string(type).c_str());
+
     return static_cast<IComposerClient::Composition>(type);
 }
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index eb4befd..e6717d7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1222,12 +1222,14 @@
         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);