Merge "SensorRegistrationInfo: Set sampling rate and max reporting latency to 64-bit variables."
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 3ea1d56..89ce1e5 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -6,141 +6,86 @@
     chmod 0222 /sys/kernel/debug/tracing/trace_marker
     chmod 0222 /sys/kernel/tracing/trace_marker
 
-# Allow the shell group to enable (some) kernel tracing.
-    chown root shell /sys/kernel/debug/tracing/trace_clock
-    chown root shell /sys/kernel/tracing/trace_clock
-    chown root shell /sys/kernel/debug/tracing/buffer_size_kb
-    chown root shell /sys/kernel/tracing/buffer_size_kb
-    chown root shell /sys/kernel/debug/tracing/options/overwrite
-    chown root shell /sys/kernel/tracing/options/overwrite
-    chown root shell /sys/kernel/debug/tracing/options/print-tgid
-    chown root shell /sys/kernel/tracing/options/print-tgid
-    chown root shell /sys/kernel/debug/tracing/saved_cmdlines_size
-    chown root shell /sys/kernel/tracing/saved_cmdlines_size
-    chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable
-    chown root shell /sys/kernel/tracing/events/sched/sched_switch/enable
-    chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
-    chown root shell /sys/kernel/tracing/events/sched/sched_wakeup/enable
-    chown root shell /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable
-    chown root shell /sys/kernel/tracing/events/sched/sched_blocked_reason/enable
-    chown root shell /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable
-    chown root shell /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable
-    chown root shell /sys/kernel/debug/tracing/events/cgroup/enable
-    chown root shell /sys/kernel/tracing/events/cgroup/enable
-    chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
-    chown root shell /sys/kernel/tracing/events/power/cpu_frequency/enable
-    chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable
-    chown root shell /sys/kernel/tracing/events/power/cpu_idle/enable
-    chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
-    chown root shell /sys/kernel/tracing/events/power/clock_set_rate/enable
-    chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
-    chown root shell /sys/kernel/tracing/events/power/cpu_frequency_limits/enable
-    chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
-    chown root shell /sys/kernel/tracing/events/cpufreq_interactive/enable
-    chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
-    chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
-    chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
-    chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
-    chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
-    chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
-    chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
-    chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
-    chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
-    chown root shell /sys/kernel/tracing/events/binder/binder_transaction/enable
-    chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
-    chown root shell /sys/kernel/tracing/events/binder/binder_transaction_received/enable
-    chown root shell /sys/kernel/debug/tracing/events/binder/binder_lock/enable
-    chown root shell /sys/kernel/tracing/events/binder/binder_lock/enable
-    chown root shell /sys/kernel/debug/tracing/events/binder/binder_locked/enable
-    chown root shell /sys/kernel/tracing/events/binder/binder_locked/enable
-    chown root shell /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
-    chown root shell /sys/kernel/tracing/events/binder/binder_unlock/enable
-    chown root shell /sys/kernel/debug/tracing/events/lowmemorykiller/enable
-    chown root shell /sys/kernel/tracing/events/lowmemorykiller/enable
+# Grant unix world read/write permissions to kernel tracepoints.
+# Access control to these files is now entirely in selinux policy.
+    chmod 0666 /sys/kernel/debug/tracing/trace_clock
+    chmod 0666 /sys/kernel/tracing/trace_clock
+    chmod 0666 /sys/kernel/debug/tracing/buffer_size_kb
+    chmod 0666 /sys/kernel/tracing/buffer_size_kb
+    chmod 0666 /sys/kernel/debug/tracing/options/overwrite
+    chmod 0666 /sys/kernel/tracing/options/overwrite
+    chmod 0666 /sys/kernel/debug/tracing/options/print-tgid
+    chmod 0666 /sys/kernel/tracing/options/print-tgid
+    chmod 0666 /sys/kernel/debug/tracing/saved_cmdlines_size
+    chmod 0666 /sys/kernel/tracing/saved_cmdlines_size
+    chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_switch/enable
+    chmod 0666 /sys/kernel/tracing/events/sched/sched_switch/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
+    chmod 0666 /sys/kernel/tracing/events/sched/sched_wakeup/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable
+    chmod 0666 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable
+    chmod 0666 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/cgroup/enable
+    chmod 0666 /sys/kernel/tracing/events/cgroup/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
+    chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
+    chmod 0666 /sys/kernel/tracing/events/power/cpu_idle/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
+    chmod 0666 /sys/kernel/tracing/events/power/clock_set_rate/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
+    chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
+    chmod 0666 /sys/kernel/tracing/events/cpufreq_interactive/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
+    chmod 0666 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
+    chmod 0666 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
+    chmod 0666 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
+    chmod 0666 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
+    chmod 0666 /sys/kernel/debug/tracing/tracing_on
+    chmod 0666 /sys/kernel/tracing/tracing_on
+    chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
+    chmod 0666 /sys/kernel/tracing/events/binder/binder_transaction/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
+    chmod 0666 /sys/kernel/tracing/events/binder/binder_transaction_received/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_lock/enable
+    chmod 0666 /sys/kernel/tracing/events/binder/binder_lock/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_locked/enable
+    chmod 0666 /sys/kernel/tracing/events/binder/binder_locked/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
+    chmod 0666 /sys/kernel/tracing/events/binder/binder_unlock/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/i2c_read/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_write/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/i2c_write/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_result/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/i2c_result/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_reply/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/i2c_reply/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/smbus_read/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/smbus_read/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/smbus_write/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/smbus_write/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/smbus_result/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/smbus_result/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/i2c/smbus_reply/enable
+    chmod 0666 /sys/kernel/tracing/events/i2c/smbus_reply/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/lowmemorykiller/enable
+    chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/enable
 
-    chown root shell /sys/kernel/debug/tracing/tracing_on
-    chown root shell /sys/kernel/tracing/tracing_on
-
-    chmod 0664 /sys/kernel/debug/tracing/trace_clock
-    chmod 0664 /sys/kernel/tracing/trace_clock
-    chmod 0664 /sys/kernel/debug/tracing/buffer_size_kb
-    chmod 0664 /sys/kernel/tracing/buffer_size_kb
-    chmod 0664 /sys/kernel/debug/tracing/options/overwrite
-    chmod 0664 /sys/kernel/tracing/options/overwrite
-    chmod 0664 /sys/kernel/debug/tracing/options/print-tgid
-    chmod 0664 /sys/kernel/tracing/options/print-tgid
-    chmod 0664 /sys/kernel/debug/tracing/saved_cmdlines_size
-    chmod 0664 /sys/kernel/tracing/saved_cmdlines_size
-    chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable
-    chmod 0664 /sys/kernel/tracing/events/sched/sched_switch/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
-    chmod 0664 /sys/kernel/tracing/events/sched/sched_wakeup/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable
-    chmod 0664 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable
-    chmod 0664 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/cgroup/enable
-    chmod 0664 /sys/kernel/tracing/events/cgroup/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
-    chmod 0664 /sys/kernel/tracing/events/power/cpu_frequency/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
-    chmod 0664 /sys/kernel/tracing/events/power/cpu_idle/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
-    chmod 0664 /sys/kernel/tracing/events/power/clock_set_rate/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
-    chmod 0664 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
-    chmod 0664 /sys/kernel/tracing/events/cpufreq_interactive/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
-    chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
-    chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
-    chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
-    chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
-    chmod 0664 /sys/kernel/debug/tracing/tracing_on
-    chmod 0664 /sys/kernel/tracing/tracing_on
-    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
-    chmod 0664 /sys/kernel/tracing/events/binder/binder_transaction/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
-    chmod 0664 /sys/kernel/tracing/events/binder/binder_transaction_received/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_lock/enable
-    chmod 0664 /sys/kernel/tracing/events/binder/binder_lock/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_locked/enable
-    chmod 0664 /sys/kernel/tracing/events/binder/binder_locked/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
-    chmod 0664 /sys/kernel/tracing/events/binder/binder_unlock/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/i2c_read/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_write/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/i2c_write/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_result/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/i2c_result/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_reply/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/i2c_reply/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_read/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/smbus_read/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_write/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/smbus_write/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_result/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/smbus_result/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_reply/enable
-    chmod 0664 /sys/kernel/tracing/events/i2c/smbus_reply/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/lowmemorykiller/enable
-    chmod 0664 /sys/kernel/tracing/events/lowmemorykiller/enable
-
-    # Tracing disabled by default
+# Tracing disabled by default
     write /sys/kernel/debug/tracing/tracing_on 0
     write /sys/kernel/tracing/tracing_on 0
 
-# Allow only the shell group to read and truncate the kernel trace.
-    chown root shell /sys/kernel/debug/tracing/trace
-    chown root shell /sys/kernel/tracing/trace
-    chmod 0660 /sys/kernel/debug/tracing/trace
-    chmod 0660 /sys/kernel/tracing/trace
+# Read and truncate the kernel trace.
+    chmod 0666 /sys/kernel/debug/tracing/trace
+    chmod 0666 /sys/kernel/tracing/trace
 
 on property:persist.debug.atrace.boottrace=1
     start boottrace
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index 5fd28e2..9d632cc 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -1,47 +1,43 @@
 ## Permissions to allow additional system-wide tracing to the kernel trace buffer.
 ## The default list of permissions is set in frameworks/native/cmds/atrace/atrace.rc
 
-# Allow the shell group to enable kernel tracepoints:
+# Grant unix world read/write permissions to enable kernel tracepoints.
+# Access control to these files is now entirely in selinux policy.
 
 on post-fs
-    chown root shell /sys/kernel/debug/tracing/events/sync/enable
-    chown root shell /sys/kernel/debug/tracing/events/workqueue/enable
-    chown root shell /sys/kernel/debug/tracing/events/regulator/enable
-    chown root shell /sys/kernel/debug/tracing/events/pagecache/enable
+    chmod 0666 /sys/kernel/tracing/events/sync/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/sync/enable
+    chmod 0666 /sys/kernel/tracing/events/workqueue/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/workqueue/enable
+    chmod 0666 /sys/kernel/tracing/events/regulator/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/regulator/enable
+    chmod 0666 /sys/kernel/tracing/events/pagecache/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/pagecache/enable
 
     # irq
-    chown root shell /sys/kernel/debug/tracing/events/irq/enable
-    chown root shell /sys/kernel/debug/tracing/events/ipi/enable
+    chmod 0666 /sys/kernel/tracing/events/irq/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/irq/enable
+    chmod 0666 /sys/kernel/tracing/events/ipi/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ipi/enable
 
     # disk
-    chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable
-    chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable
-    chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable
-    chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable
-    chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable
-    chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable
-    chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable
-    chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable
-    chown root shell /sys/kernel/debug/tracing/events/block/block_rq_issue/enable
-    chown root shell /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
-
-    chmod 0664 /sys/kernel/debug/tracing/events/sync/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/workqueue/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/regulator/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/pagecache/enable
-
-    # irq
-    chmod 0664 /sys/kernel/debug/tracing/events/irq/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/ipi/enable
-
-    # disk
-    chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/block/block_rq_issue/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
+    chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_enter/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable
+    chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable
+    chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_write_begin/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable
+    chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_write_end/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable
+    chmod 0666 /sys/kernel/tracing/events/ext4/ext4_da_write_begin/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable
+    chmod 0666 /sys/kernel/tracing/events/ext4/ext4_da_write_end/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable
+    chmod 0666 /sys/kernel/tracing/events/ext4/ext4_sync_file_enter/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable
+    chmod 0666 /sys/kernel/tracing/events/ext4/ext4_sync_file_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable
+    chmod 0666 /sys/kernel/tracing/events/block/block_rq_issue/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/block/block_rq_issue/enable
+    chmod 0666 /sys/kernel/tracing/events/block/block_rq_complete/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index ce3a6aa..35cff5f 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -83,7 +83,9 @@
     ],
     srcs: [
         "DumpstateInternal.cpp",
+        "DumpstateSectionReporter.cpp",
         "DumpstateService.cpp",
+        "main.cpp",
         "utils.cpp",
         "dumpstate.cpp",
     ],
diff --git a/cmds/dumpstate/DumpstateSectionReporter.cpp b/cmds/dumpstate/DumpstateSectionReporter.cpp
new file mode 100644
index 0000000..f814bde
--- /dev/null
+++ b/cmds/dumpstate/DumpstateSectionReporter.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateSectionReporter.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+DumpstateSectionReporter::DumpstateSectionReporter(const std::string& title,
+                                                   sp<android::os::IDumpstateListener> listener,
+                                                   bool sendReport)
+    : title_(title), listener_(listener), sendReport_(sendReport), status_(OK), size_(-1) {
+    started_ = std::chrono::steady_clock::now();
+}
+
+DumpstateSectionReporter::~DumpstateSectionReporter() {
+    if ((listener_ != nullptr) && (sendReport_)) {
+        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
+            std::chrono::steady_clock::now() - started_);
+        listener_->onSectionComplete(title_, status_, size_, (int32_t)elapsed.count());
+    }
+}
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpstate/DumpstateSectionReporter.h b/cmds/dumpstate/DumpstateSectionReporter.h
new file mode 100644
index 0000000..e971de8
--- /dev/null
+++ b/cmds/dumpstate/DumpstateSectionReporter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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_OS_DUMPSTATESECTIONREPORTER_H_
+#define ANDROID_OS_DUMPSTATESECTIONREPORTER_H_
+
+#include <android/os/IDumpstateListener.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+
+/*
+ * Helper class used to report per section details to a listener.
+ *
+ * Typical usage:
+ *
+ *    DumpstateSectionReporter sectionReporter(title, listener, sendReport);
+ *    sectionReporter.setSize(5000);
+ *
+ */
+class DumpstateSectionReporter {
+  public:
+    DumpstateSectionReporter(const std::string& title, sp<android::os::IDumpstateListener> listener,
+                             bool sendReport);
+
+    ~DumpstateSectionReporter();
+
+    void setStatus(status_t status) {
+        status_ = status;
+    }
+
+    void setSize(int size) {
+        size_ = size;
+    }
+
+  private:
+    std::string title_;
+    android::sp<android::os::IDumpstateListener> listener_;
+    bool sendReport_;
+    status_t status_;
+    int size_;
+    std::chrono::time_point<std::chrono::steady_clock> started_;
+};
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
+
+#endif  // ANDROID_OS_DUMPSTATESECTIONREPORTER_H_
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index efe0466..49a78e7 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -52,6 +52,7 @@
 
 binder::Status DumpstateService::setListener(const std::string& name,
                                              const sp<IDumpstateListener>& listener,
+                                             bool getSectionDetails,
                                              sp<IDumpstateToken>* returned_token) {
     *returned_token = nullptr;
     if (name.empty()) {
@@ -70,6 +71,7 @@
 
     ds_.listener_name_ = name;
     ds_.listener_ = listener;
+    ds_.report_section_ = getSectionDetails;
     *returned_token = new DumpstateToken();
 
     return binder::Status::ok();
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 4352d3d..7bca24a 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -38,6 +38,7 @@
 
     status_t dump(int fd, const Vector<String16>& args) override;
     binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
+                               bool getSectionDetails,
                                sp<IDumpstateToken>* returned_token) override;
 
   private:
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 4becccf..9b11b96 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -30,6 +30,9 @@
      *
      * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already
      * set (the listener behaves like a Highlander: There Can be Only One).
+     * Set {@code getSectionDetails} to true in order to receive callbacks with per section
+     * progress details
      */
-    IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener);
+    IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener,
+                                boolean getSectionDetails);
 }
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 32717f4..030d69d 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -24,4 +24,16 @@
 interface IDumpstateListener {
     void onProgressUpdated(int progress);
     void onMaxProgressUpdated(int maxProgress);
+
+    /**
+    * Called after every section is complete.
+    * @param  name          section name
+    * @param  status        values from status_t
+    *                       {@code OK} section completed successfully
+    *                       {@code TIMEOUT} dump timed out
+    *                       {@code != OK} error
+    * @param  size          size in bytes, may be invalid if status != OK
+    * @param  durationMs    duration in ms
+    */
+    void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs);
 }
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2e3e3ca..93f8d43 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -43,8 +43,10 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
+#include <hidl/ServiceManagement.h>
 #include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -131,8 +133,6 @@
 };
 static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles);
 
-static const std::string kLsHalDebugPath = "/bugreports/dumpstate_lshal.txt";
-
 static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
 static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
 static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
@@ -831,40 +831,52 @@
     }
 }
 
+static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000;
+
+static void DoKernelLogcat() {
+    unsigned long timeout_ms = logcat_timeout("kernel");
+    if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
+        timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
+    }
+    RunCommand(
+        "KERNEL LOG",
+        {"logcat", "-b", "kernel", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+}
+
 static void DoLogcat() {
     unsigned long timeout_ms;
     // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
     // calculate timeout
     timeout_ms = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
-    if (timeout_ms < 20000) {
-        timeout_ms = 20000;
+    if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
+        timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
     }
     RunCommand("SYSTEM LOG",
                {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
                CommandOptions::WithTimeoutInMs(timeout_ms).Build());
     timeout_ms = logcat_timeout("events");
-    if (timeout_ms < 20000) {
-        timeout_ms = 20000;
+    if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
+        timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
     }
-    RunCommand("EVENT LOG",
-               {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid",
-                        "-d", "*:v"},
-               CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+    RunCommand(
+        "EVENT LOG",
+        {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build());
     timeout_ms = logcat_timeout("radio");
-    if (timeout_ms < 20000) {
-        timeout_ms = 20000;
+    if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
+        timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
     }
-    RunCommand("RADIO LOG",
-               {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid",
-                        "-d", "*:v"},
-               CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+    RunCommand(
+        "RADIO LOG",
+        {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build());
 
     RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
 
     /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
-    RunCommand("LAST LOGCAT",
-                {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid",
-                        "-d", "*:v"});
+    RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable",
+                               "-v", "uid", "-d", "*:v"});
 }
 
 static void DumpIpTablesAsRoot() {
@@ -1091,6 +1103,57 @@
     }
 }
 
+static void DumpHals() {
+    using android::sp;
+    using android::hidl::manager::V1_0::IServiceManager;
+    using android::hardware::defaultServiceManager;
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+        MYLOGE("Could not retrieve hwservicemanager to dump hals.\n");
+        return;
+    }
+
+    auto ret = sm->list([&](const auto& interfaces) {
+        for (const std::string& interface : interfaces) {
+            std::string cleanName = interface;
+            std::replace_if(cleanName.begin(),
+                            cleanName.end(),
+                            [](char c) {
+                                return !isalnum(c) &&
+                                    std::string("@-_:.").find(c) == std::string::npos;
+                            }, '_');
+            const std::string path = kDumpstateBoardPath + "lshal_debug_" + cleanName;
+
+            {
+                auto fd = android::base::unique_fd(
+                    TEMP_FAILURE_RETRY(open(path.c_str(),
+                    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+                if (fd < 0) {
+                    MYLOGE("Could not open %s to dump additional hal information.\n", path.c_str());
+                    continue;
+                }
+                RunCommandToFd(fd,
+                        "",
+                        {"lshal", "debug", interface},
+                        CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
+
+                bool empty = 0 == lseek(fd, 0, SEEK_END);
+                if (!empty) {
+                    ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path);
+                }
+            }
+
+            unlink(path.c_str());
+        }
+    });
+
+    if (!ret.isOk()) {
+        MYLOGE("Could not list hals from hwservicemanager.\n");
+    }
+}
+
 static void dumpstate() {
     DurationReporter duration_reporter("DUMPSTATE");
 
@@ -1119,18 +1182,10 @@
     RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT);
 
     if (ds.IsZipping()) {
-        RunCommand(
-                "HARDWARE HALS",
-                {"lshal", std::string("--debug=") + kLsHalDebugPath},
-                CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
-
-        ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath);
-
-        unlink(kLsHalDebugPath.c_str());
+        RunCommand("HARDWARE HALS", {"lshal"}, CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
+        DumpHals();
     } else {
-        RunCommand(
-                "HARDWARE HALS", {"lshal", "--debug"},
-                CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+        RunCommand("HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
     }
 
     RunCommand("PRINTENV", {"printenv"});
@@ -1142,7 +1197,12 @@
         RunCommand("LSMOD", {"lsmod"});
     }
 
-    do_dmesg();
+    if (__android_logger_property_get_bool(
+            "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) {
+        DoKernelLogcat();
+    } else {
+        do_dmesg();
+    }
 
     RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
     for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
@@ -1191,19 +1251,6 @@
 
     RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
 
-    printf("------ BACKLIGHTS ------\n");
-    printf("LCD brightness=");
-    DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
-    printf("Button brightness=");
-    DumpFile("", "/sys/class/leds/button-backlight/brightness");
-    printf("Keyboard brightness=");
-    DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
-    printf("ALS mode=");
-    DumpFile("", "/sys/class/leds/lcd-backlight/als");
-    printf("LCD driver registers:\n");
-    DumpFile("", "/sys/class/leds/lcd-backlight/registers");
-    printf("\n");
-
     /* Binder state is expensive to look at as it uses a lot of memory. */
     DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
     DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
@@ -1556,7 +1603,8 @@
     // clang-format on
 }
 
-int main(int argc, char *argv[]) {
+/** Main entry point for dumpstate. */
+int run_main(int argc, char* argv[]) {
     int do_add_date = 0;
     int do_zip_file = 0;
     int do_vibrate = 1;
@@ -1569,6 +1617,8 @@
     bool show_header_only = false;
     bool do_start_service = false;
     bool telephony_only = false;
+    int dup_stdout_fd;
+    int dup_stderr_fd;
 
     /* set as high priority, and protect from OOM killer */
     setpriority(PRIO_PROCESS, 0, -20);
@@ -1840,11 +1890,13 @@
     }
 
     if (is_redirecting) {
+        TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
         redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
         if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
                    ds.log_path_.c_str(), strerror(errno));
         }
+        TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout)));
         /* TODO: rather than generating a text file now and zipping it later,
            it would be more efficient to redirect stdout to the zip entry
            directly, but the libziparchive doesn't support that option yet. */
@@ -1918,7 +1970,7 @@
 
     /* close output if needed */
     if (is_redirecting) {
-        fclose(stdout);
+        TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
     }
 
     /* rename or zip the (now complete) .tmp file to its final location */
@@ -2049,7 +2101,7 @@
     MYLOGI("done (id %d)\n", ds.id_);
 
     if (is_redirecting) {
-        fclose(stderr);
+        TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));
     }
 
     if (use_control_socket && ds.control_socket_fd_ != -1) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 8db23a9..843c545 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -341,9 +341,10 @@
     // Pointer to the zip structure.
     std::unique_ptr<ZipWriter> zip_writer_;
 
-    // Binder object listing to progress.
+    // Binder object listening to progress.
     android::sp<android::os::IDumpstateListener> listener_;
     std::string listener_name_;
+    bool report_section_;
 
     // Notification title and description
     std::string notification_title;
@@ -433,6 +434,9 @@
 /** Gets command-line arguments. */
 void format_args(int argc, const char *argv[], std::string *args);
 
+/** Main entry point for dumpstate. */
+int run_main(int argc, char* argv[]);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
new file mode 100644
index 0000000..78aad11
--- /dev/null
+++ b/cmds/dumpstate/main.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 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 "dumpstate.h"
+
+int main(int argc, char* argv[]) {
+    return run_main(argc, argv);
+}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index a2e9453..838b385 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -58,6 +58,8 @@
   public:
     MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
     MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
+    MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status,
+                                                   int32_t size, int32_t durationMs));
 
   protected:
     MOCK_METHOD0(onAsBinder, IBinder*());
@@ -601,27 +603,43 @@
 TEST_F(DumpstateServiceTest, SetListenerNoName) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
     sp<IDumpstateToken> token;
-    EXPECT_TRUE(dss.setListener("", listener, &token).isOk());
+    EXPECT_TRUE(dss.setListener("", listener, /* getSectionDetails = */ false, &token).isOk());
     ASSERT_THAT(token, IsNull());
 }
 
 TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
     sp<IDumpstateToken> token;
-    EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk());
+    EXPECT_TRUE(
+        dss.setListener("whatever", nullptr, /* getSectionDetails = */ false, &token).isOk());
     ASSERT_THAT(token, IsNull());
 }
 
 TEST_F(DumpstateServiceTest, SetListenerTwice) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
     sp<IDumpstateToken> token;
-    EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk());
+    EXPECT_TRUE(
+        dss.setListener("whatever", listener, /* getSectionDetails = */ false, &token).isOk());
     ASSERT_THAT(token, NotNull());
     EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+    EXPECT_FALSE(Dumpstate::GetInstance().report_section_);
 
     token.clear();
-    EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk());
+    EXPECT_TRUE(
+        dss.setListener("whatsoever", listener, /* getSectionDetails = */ false, &token).isOk());
     ASSERT_THAT(token, IsNull());
     EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+    EXPECT_FALSE(Dumpstate::GetInstance().report_section_);
+}
+
+TEST_F(DumpstateServiceTest, SetListenerWithSectionDetails) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    sp<IDumpstateToken> token;
+    Dumpstate::GetInstance().listener_ = nullptr;
+    EXPECT_TRUE(
+        dss.setListener("whatever", listener, /* getSectionDetails = */ true, &token).isOk());
+    ASSERT_THAT(token, NotNull());
+    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+    EXPECT_TRUE(Dumpstate::GetInstance().report_section_);
 }
 
 class ProgressTest : public DumpstateBaseTest {
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index ac48041..fcd6742 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -84,6 +84,7 @@
         "/system/bin/drmserver",
         "/system/bin/mediadrmserver",
         "/system/bin/mediaextractor", // media.extractor
+        "/system/bin/mediametrics", // media.metrics
         "/system/bin/mediaserver",
         "/system/bin/sdcard",
         "/system/bin/surfaceflinger",
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 0862a40..ae0cc01 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -43,9 +43,11 @@
 #include "dumpsys.h"
 
 using namespace android;
-using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::base::WriteFully;
+using ::android::base::StringAppendF;
+using ::android::base::StringPrintf;
+using ::android::base::unique_fd;
+using ::android::base::WriteFully;
+using ::android::base::WriteStringToFd;
 
 static int sort_func(const String16* lhs, const String16* rhs)
 {
@@ -96,6 +98,19 @@
     return false;
 }
 
+String16 ConvertBitmaskToPriorityType(int bitmask) {
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) {
+        return String16(PriorityDumper::PRIORITY_ARG_CRITICAL);
+    }
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) {
+        return String16(PriorityDumper::PRIORITY_ARG_HIGH);
+    }
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+        return String16(PriorityDumper::PRIORITY_ARG_NORMAL);
+    }
+    return String16("");
+}
+
 int Dumpsys::main(int argc, char* const argv[]) {
     Vector<String16> services;
     Vector<String16> args;
@@ -104,9 +119,9 @@
     Vector<String16> protoServices;
     bool showListOnly = false;
     bool skipServices = false;
-    bool filterByProto = false;
+    bool asProto = false;
     int timeoutArgMs = 10000;
-    int dumpPriorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
+    int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
     static struct option longOptions[] = {{"priority", required_argument, 0, 0},
                                           {"proto", no_argument, 0, 0},
                                           {"skip", no_argument, 0, 0},
@@ -131,13 +146,13 @@
             if (!strcmp(longOptions[optionIndex].name, "skip")) {
                 skipServices = true;
             } else if (!strcmp(longOptions[optionIndex].name, "proto")) {
-                filterByProto = true;
+                asProto = true;
             } else if (!strcmp(longOptions[optionIndex].name, "help")) {
                 usage();
                 return 0;
             } else if (!strcmp(longOptions[optionIndex].name, "priority")) {
                 priorityType = String16(String8(optarg));
-                if (!ConvertPriorityTypeToBitmask(priorityType, dumpPriorityFlags)) {
+                if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) {
                     fprintf(stderr, "\n");
                     usage();
                     return -1;
@@ -198,28 +213,11 @@
     }
 
     if (services.empty() || showListOnly) {
-        // gets all services
-        services = sm_->listServices(dumpPriorityFlags);
-        services.sort(sort_func);
-        if (filterByProto) {
-            protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
-            protoServices.sort(sort_func);
-            Vector<String16> intersection;
-            std::set_intersection(services.begin(), services.end(), protoServices.begin(),
-                                  protoServices.end(), std::back_inserter(intersection));
-            services = std::move(intersection);
-            args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
-        }
-        if (dumpPriorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
-            args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
-            args.insertAt(priorityType, 1);
-        } else {
-            args.add(String16("-a"));
-        }
+        services = listServices(priorityFlags, asProto);
+        setServiceArgs(args, asProto, priorityFlags);
     }
 
     const size_t N = services.size();
-
     if (N > 1) {
         // first print a list of the current services
         aout << "Currently running services:" << endl;
@@ -239,129 +237,204 @@
     }
 
     for (size_t i = 0; i < N; i++) {
-        const String16& service_name = std::move(services[i]);
-        if (IsSkipped(skippedServices, service_name)) continue;
+        const String16& serviceName = services[i];
+        if (IsSkipped(skippedServices, serviceName)) continue;
 
-        sp<IBinder> service = sm_->checkService(service_name);
-        if (service != nullptr) {
-            int sfd[2];
-
-            if (pipe(sfd) != 0) {
-                aerr << "Failed to create pipe to dump service info for " << service_name
-                     << ": " << strerror(errno) << endl;
-                continue;
+        if (startDumpThread(serviceName, args) == OK) {
+            bool addSeparator = (N > 1);
+            if (addSeparator) {
+                writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
             }
+            std::chrono::duration<double> elapsedDuration;
+            size_t bytesWritten = 0;
+            status_t status =
+                writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
+                          asProto, elapsedDuration, bytesWritten);
 
-            unique_fd local_end(sfd[0]);
-            unique_fd remote_end(sfd[1]);
-            sfd[0] = sfd[1] = -1;
-
-            if (N > 1) {
-                aout << "------------------------------------------------------------"
-                        "-------------------" << endl;
-                if (dumpPriorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
-                    aout << "DUMP OF SERVICE " << service_name << ":" << endl;
-                } else {
-                    aout << "DUMP OF SERVICE " << priorityType << " " << service_name << ":" << endl;
-                }
-            }
-
-            // dump blocks until completion, so spawn a thread..
-            std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
-                int err = service->dump(remote_end.get(), args);
-
-                // It'd be nice to be able to close the remote end of the socketpair before the dump
-                // call returns, to terminate our reads if the other end closes their copy of the
-                // file descriptor, but then hangs for some reason. There doesn't seem to be a good
-                // way to do this, though.
-                remote_end.reset();
-
-                if (err != 0) {
-                    aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
-                         << endl;
-                }
-            });
-
-            auto timeout = std::chrono::milliseconds(timeoutArgMs);
-            auto start = std::chrono::steady_clock::now();
-            auto end = start + timeout;
-
-            struct pollfd pfd = {
-                .fd = local_end.get(),
-                .events = POLLIN
-            };
-
-            bool timed_out = false;
-            bool error = false;
-            while (true) {
-                // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
-                auto time_left_ms = [end]() {
-                    auto now = std::chrono::steady_clock::now();
-                    auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
-                    return std::max(diff.count(), 0ll);
-                };
-
-                int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
-                if (rc < 0) {
-                    aerr << "Error in poll while dumping service " << service_name << " : "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                } else if (rc == 0) {
-                    timed_out = true;
-                    break;
-                }
-
-                char buf[4096];
-                rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
-                if (rc < 0) {
-                    aerr << "Failed to read while dumping service " << service_name << ": "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                } else if (rc == 0) {
-                    // EOF.
-                    break;
-                }
-
-                if (!WriteFully(STDOUT_FILENO, buf, rc)) {
-                    aerr << "Failed to write while dumping service " << service_name << ": "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                }
-            }
-
-            if (timed_out) {
+            if (status == TIMED_OUT) {
                 aout << endl
-                     << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArgMs
+                     << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
                      << "ms) EXPIRED ***" << endl
                      << endl;
             }
 
-            if (timed_out || error) {
-                dump_thread.detach();
-            } else {
-                dump_thread.join();
+            if (addSeparator) {
+                writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
             }
-
-            if (N > 1) {
-              std::chrono::duration<double> elapsed_seconds =
-                  std::chrono::steady_clock::now() - start;
-              aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
-                   << "was the duration of dumpsys " << service_name;
-
-              using std::chrono::system_clock;
-              const auto finish = system_clock::to_time_t(system_clock::now());
-              std::tm finish_tm;
-              localtime_r(&finish, &finish_tm);
-              aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S")
-                   << endl;
-            }
-        } else {
-            aerr << "Can't find service: " << service_name << endl;
+            bool dumpComplete = (status == OK);
+            stopDumpThread(dumpComplete);
         }
     }
 
     return 0;
 }
+
+Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const {
+    Vector<String16> services = sm_->listServices(priorityFilterFlags);
+    services.sort(sort_func);
+    if (filterByProto) {
+        Vector<String16> protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
+        protoServices.sort(sort_func);
+        Vector<String16> intersection;
+        std::set_intersection(services.begin(), services.end(), protoServices.begin(),
+                              protoServices.end(), std::back_inserter(intersection));
+        services = std::move(intersection);
+    }
+    return services;
+}
+
+void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const {
+    if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) ||
+        (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) {
+        args.add(String16("-a"));
+    }
+    if (asProto) {
+        args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
+    }
+    if (priorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
+        String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+        args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
+        args.insertAt(priorityType, 1);
+    }
+}
+
+status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<String16>& args) {
+    sp<IBinder> service = sm_->checkService(serviceName);
+    if (service == nullptr) {
+        aerr << "Can't find service: " << serviceName << endl;
+        return NAME_NOT_FOUND;
+    }
+
+    int sfd[2];
+    if (pipe(sfd) != 0) {
+        aerr << "Failed to create pipe to dump service info for " << serviceName << ": "
+             << strerror(errno) << endl;
+        return -errno;
+    }
+
+    redirectFd_ = unique_fd(sfd[0]);
+    unique_fd remote_end(sfd[1]);
+    sfd[0] = sfd[1] = -1;
+
+    // dump blocks until completion, so spawn a thread..
+    activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
+        int err = service->dump(remote_end.get(), args);
+
+        // It'd be nice to be able to close the remote end of the socketpair before the dump
+        // call returns, to terminate our reads if the other end closes their copy of the
+        // file descriptor, but then hangs for some reason. There doesn't seem to be a good
+        // way to do this, though.
+        remote_end.reset();
+
+        if (err != 0) {
+            aerr << "Error dumping service info: (" << strerror(err) << ") "
+                 << serviceName << endl;
+        }
+    });
+    return OK;
+}
+
+void Dumpsys::stopDumpThread(bool dumpComplete) {
+    if (dumpComplete) {
+        activeThread_.join();
+    } else {
+        activeThread_.detach();
+    }
+    /* close read end of the dump output redirection pipe */
+    redirectFd_.reset();
+}
+
+void Dumpsys::writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const {
+    std::string msg(
+        "----------------------------------------"
+        "---------------------------------------\n");
+    if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL ||
+        priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+        StringAppendF(&msg, "DUMP OF SERVICE %s:\n", String8(serviceName).c_str());
+    } else {
+        String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+        StringAppendF(&msg, "DUMP OF SERVICE %s %s:\n", String8(priorityType).c_str(),
+                      String8(serviceName).c_str());
+    }
+    WriteStringToFd(msg, fd);
+}
+
+status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
+                            bool asProto, std::chrono::duration<double>& elapsedDuration,
+                            size_t& bytesWritten) const {
+    status_t status = OK;
+    size_t totalBytes = 0;
+    auto start = std::chrono::steady_clock::now();
+    auto end = start + timeout;
+
+    int serviceDumpFd = redirectFd_.get();
+    if (serviceDumpFd == -1) {
+        return INVALID_OPERATION;
+    }
+
+    struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN};
+
+    while (true) {
+        // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
+        auto time_left_ms = [end]() {
+            auto now = std::chrono::steady_clock::now();
+            auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
+            return std::max(diff.count(), 0ll);
+        };
+
+        int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
+        if (rc < 0) {
+            aerr << "Error in poll while dumping service " << serviceName << " : "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        } else if (rc == 0) {
+            status = TIMED_OUT;
+            break;
+        }
+
+        char buf[4096];
+        rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
+        if (rc < 0) {
+            aerr << "Failed to read while dumping service " << serviceName << ": "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        } else if (rc == 0) {
+            // EOF.
+            break;
+        }
+
+        if (!WriteFully(fd, buf, rc)) {
+            aerr << "Failed to write while dumping service " << serviceName << ": "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        }
+        totalBytes += rc;
+    }
+
+    if ((status == TIMED_OUT) && (!asProto)) {
+        std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n",
+                                       String8(serviceName).string(), timeout.count());
+        WriteStringToFd(msg, fd);
+    }
+
+    elapsedDuration = std::chrono::steady_clock::now() - start;
+    bytesWritten = totalBytes;
+    return status;
+}
+
+void Dumpsys::writeDumpFooter(int fd, const String16& serviceName,
+                              const std::chrono::duration<double>& elapsedDuration) const {
+    using std::chrono::system_clock;
+    const auto finish = system_clock::to_time_t(system_clock::now());
+    std::tm finish_tm;
+    localtime_r(&finish, &finish_tm);
+    std::stringstream oss;
+    oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S");
+    std::string msg =
+        StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n",
+                     elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str());
+    WriteStringToFd(msg, fd);
+}
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 2534dde..1d78aa4 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -17,6 +17,9 @@
 #ifndef FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
 #define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
 
+#include <thread>
+
+#include <android-base/unique_fd.h>
 #include <binder/IServiceManager.h>
 
 namespace android {
@@ -25,10 +28,97 @@
   public:
     Dumpsys(android::IServiceManager* sm) : sm_(sm) {
     }
+    /**
+     * Main entry point into dumpsys.
+     */
     int main(int argc, char* const argv[]);
 
+    /**
+     * Returns a list of services.
+     * @param priorityFlags filter services by specified priorities
+     * @param supportsProto filter services that support proto dumps
+     * @return list of services
+     */
+    Vector<String16> listServices(int priorityFlags, bool supportsProto) const;
+
+    /**
+     * Modifies @{code args} to add additional arguments  to indicate if the service
+     * must dump as proto or dump to a certian priority bucket.
+     * @param args initial list of arguments to pass to service dump method.
+     * @param asProto dump service as proto by passing an additional --proto arg
+     * @param priorityFlags indicates priority of dump by passing additional priority args
+     * to the service
+     */
+    void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const;
+
+    /**
+     * Starts a thread to connect to a service and get its dump output. The thread redirects
+     * the output to a pipe. Thread must be stopped by a subsequent callto {@code
+     * stopDumpThread}.
+     * @param serviceName
+     * @param args list of arguments to pass to service dump method.
+     * @return {@code OK} thread is started successfully.
+     *         {@code NAME_NOT_FOUND} service could not be found.
+     *         {@code != OK} error
+     */
+    status_t startDumpThread(const String16& serviceName, const Vector<String16>& args);
+
+    /**
+     * Writes a section header to a file descriptor.
+     * @param fd file descriptor to write data
+     * @param serviceName
+     * @param priorityFlags dump priority specified
+     */
+    void writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const;
+
+    /**
+     * Redirects service dump to a file descriptor. This requires
+     * {@code startDumpThread} to be called successfully otherwise the function will
+     * return {@code INVALID_OPERATION}.
+     * @param fd file descriptor to write data
+     * @param serviceName
+     * @param timeout timeout to terminate the dump if not completed
+     * @param asProto used to supresses additional output to the fd such as timeout
+     * error messages
+     * @param elapsedDuration returns elapsed time in seconds
+     * @param bytesWritten returns number of bytes written
+     * @return {@code OK} if successful
+     *         {@code TIMED_OUT} dump timed out
+     *         {@code INVALID_OPERATION} invalid state
+     *         {@code != OK} error
+     */
+    status_t writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
+                       bool asProto, std::chrono::duration<double>& elapsedDuration,
+                       size_t& bytesWritten) const;
+
+    /**
+     * Writes a section footer to a file descriptor with duration info.
+     * @param fd file descriptor to write data
+     * @param serviceName
+     * @param elapsedDuration duration of dump
+     */
+    void writeDumpFooter(int fd, const String16& serviceName,
+                         const std::chrono::duration<double>& elapsedDuration) const;
+
+    /**
+     * Terminates dump thread.
+     * @param dumpComplete If {@code true}, indicates the dump was successfully completed and
+     * tries to join the thread. Otherwise thread is detached.
+     */
+    void stopDumpThread(bool dumpComplete);
+
+    /**
+     * Returns file descriptor of the pipe used to dump service data. This assumes
+     * {@code startDumpThread} was called successfully.
+     */
+    int getDumpFd() const {
+        return redirectFd_.get();
+    }
+
   private:
     android::IServiceManager* sm_;
+    std::thread activeThread_;
+    mutable android::base::unique_fd redirectFd_;
 };
 }
 
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index bdb0a9a..b13f59d 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -188,6 +188,22 @@
         EXPECT_THAT(status, Eq(0));
     }
 
+    void CallSingleService(const String16& serviceName, Vector<String16>& args, int priorityFlags,
+                           bool supportsProto, std::chrono::duration<double>& elapsedDuration,
+                           size_t& bytesWritten) {
+        CaptureStdout();
+        CaptureStderr();
+        dump_.setServiceArgs(args, supportsProto, priorityFlags);
+        status_t status = dump_.startDumpThread(serviceName, args);
+        EXPECT_THAT(status, Eq(0));
+        status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false,
+                                 elapsedDuration, bytesWritten);
+        EXPECT_THAT(status, Eq(0));
+        dump_.stopDumpThread(/* dumpCompleted = */ true);
+        stdout_ = GetCapturedStdout();
+        stderr_ = GetCapturedStderr();
+    }
+
     void AssertRunningServices(const std::vector<std::string>& services) {
         std::string expected;
         if (services.size() > 1) {
@@ -209,6 +225,7 @@
 
     void AssertDumped(const std::string& service, const std::string& dump) {
         EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
+        EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: "));
     }
 
     void AssertDumpedWithPriority(const std::string& service, const std::string& dump,
@@ -216,6 +233,7 @@
         std::string priority = String8(priorityType).c_str();
         EXPECT_THAT(stdout_,
                     HasSubstr("DUMP OF SERVICE " + priority + " " + service + ":\n" + dump));
+        EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: "));
     }
 
     void AssertNotDumped(const std::string& dump) {
@@ -425,8 +443,8 @@
     CallMain({"--priority", "NORMAL"});
 
     AssertRunningServices({"runningnormal1", "runningnormal2"});
-    AssertDumpedWithPriority("runningnormal1", "dump1", PriorityDumper::PRIORITY_ARG_NORMAL);
-    AssertDumpedWithPriority("runningnormal2", "dump2", PriorityDumper::PRIORITY_ARG_NORMAL);
+    AssertDumped("runningnormal1", "dump1");
+    AssertDumped("runningnormal2", "dump2");
 }
 
 // Tests 'dumpsys --proto'
@@ -461,3 +479,29 @@
     AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH);
     AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH);
 }
+
+TEST_F(DumpsysTest, GetBytesWritten) {
+    const char* serviceName = "service2";
+    const char* dumpContents = "dump1";
+    ExpectDump(serviceName, dumpContents);
+
+    String16 service(serviceName);
+    Vector<String16> args;
+    std::chrono::duration<double> elapsedDuration;
+    size_t bytesWritten;
+
+    CallSingleService(service, args, IServiceManager::DUMP_FLAG_PRIORITY_ALL,
+                      /* as_proto = */ false, elapsedDuration, bytesWritten);
+
+    AssertOutput(dumpContents);
+    EXPECT_THAT(bytesWritten, Eq(strlen(dumpContents)));
+}
+
+TEST_F(DumpsysTest, WriteDumpWithoutThreadStart) {
+    std::chrono::duration<double> elapsedDuration;
+    size_t bytesWritten;
+    status_t status =
+        dump_.writeDump(STDOUT_FILENO, String16("service"), std::chrono::milliseconds(500),
+                        /* as_proto = */ false, elapsedDuration, bytesWritten);
+    EXPECT_THAT(status, Eq(INVALID_OPERATION));
+}
\ No newline at end of file
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 715bf8c..f787887 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -37,6 +37,7 @@
 #include <unistd.h>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -71,6 +72,7 @@
 
 static constexpr const char* kCpPath = "/system/bin/cp";
 static constexpr const char* kXattrDefault = "user.default";
+static constexpr const char* kPropHasReserved = "vold.has_reserved";
 
 static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
 
@@ -82,6 +84,8 @@
 static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
 static constexpr const char* IDMAP_SUFFIX = "@idmap";
 
+static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
+
 // NOTE: keep in sync with Installer
 static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
 static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
@@ -302,6 +306,9 @@
  */
 static int prepare_app_quota(const std::unique_ptr<std::string>& uuid, const std::string& device,
         uid_t uid) {
+    // Skip when reserved blocks are protecting us against abusive apps
+    if (android::base::GetBoolProperty(kPropHasReserved, false)) return 0;
+    // Skip when device no quotas present
     if (device.empty()) return 0;
 
     struct dqblk dq;
@@ -1891,7 +1898,7 @@
         int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
         const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
         const std::unique_ptr<std::string>& classLoaderContext,
-        const std::unique_ptr<std::string>& seInfo, bool downgrade) {
+        const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     if (packageName && *packageName != "*") {
@@ -1909,7 +1916,7 @@
     const char* se_info = seInfo ? seInfo->c_str() : nullptr;
     int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,
             oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info,
-            downgrade);
+            downgrade, targetSdkVersion);
     return res ? error(res, "Failed to dexopt") : ok();
 }
 
@@ -2351,6 +2358,17 @@
     return res ? ok() : error();
 }
 
+binder::Status InstalldNativeService::installApkVerity(const std::string& /*filePath*/,
+        const ::android::base::unique_fd& /*verityInput*/) {
+    ENFORCE_UID(AID_SYSTEM);
+    if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
+        return ok();
+    }
+    // TODO: Append verity to filePath then issue ioctl to enable
+    // it and hide the tree.  See b/30972906.
+    return error("not implemented yet");
+}
+
 binder::Status InstalldNativeService::reconcileSecondaryDexFile(
         const std::string& dexPath, const std::string& packageName, int32_t uid,
         const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
@@ -2417,14 +2435,18 @@
                 mQuotaReverseMounts[target] = source;
 
                 // ext4 only enables DQUOT_USAGE_ENABLED by default, so we
-                // need to kick it again to enable DQUOT_LIMITS_ENABLED.
-                if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0
-                        && errno != EBUSY) {
-                    PLOG(ERROR) << "Failed to enable USRQUOTA on " << source;
-                }
-                if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0
-                        && errno != EBUSY) {
-                    PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source;
+                // need to kick it again to enable DQUOT_LIMITS_ENABLED. We
+                // only need hard limits enabled when we're not being protected
+                // by reserved blocks.
+                if (!android::base::GetBoolProperty(kPropHasReserved, false)) {
+                    if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1,
+                            nullptr) != 0 && errno != EBUSY) {
+                        PLOG(ERROR) << "Failed to enable USRQUOTA on " << source;
+                    }
+                    if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1,
+                            nullptr) != 0 && errno != EBUSY) {
+                        PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source;
+                    }
                 }
             }
         }
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 57dd834..cef62cd 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -84,7 +84,8 @@
             int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
             const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
             const std::unique_ptr<std::string>& classLoaderContext,
-            const std::unique_ptr<std::string>& seInfo, bool downgrade);
+            const std::unique_ptr<std::string>& seInfo, bool downgrade,
+            int32_t targetSdkVersion);
 
     binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
 
@@ -117,6 +118,8 @@
             const std::string& outputPath);
     binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
             const std::unique_ptr<std::string>& outputPath);
+    binder::Status installApkVerity(const std::string& filePath,
+            const ::android::base::unique_fd& verityInput);
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
         const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
         const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 394c028..c819e98 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -51,7 +51,7 @@
             @nullable @utf8InCpp String outputPath, int dexFlags,
             @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
             @nullable @utf8InCpp String sharedLibraries,
-            @nullable @utf8InCpp String seInfo, boolean downgrade);
+            @nullable @utf8InCpp String seInfo, boolean downgrade, int targetSdkVersion);
 
     void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
 
@@ -81,6 +81,7 @@
             @utf8InCpp String outputPath);
     void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
             @nullable @utf8InCpp String outputPath);
+    void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput);
 
     boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
         int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index fd4eef3..80e18d3 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -66,6 +66,7 @@
 static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo";
 static constexpr bool kMinidebugInfoSystemPropertyDefault = false;
 static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
+static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
 
 // Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
 struct FreeDelete {
@@ -216,7 +217,7 @@
         const char* input_file_name, const char* output_file_name, int swap_fd,
         const char* instruction_set, const char* compiler_filter,
         bool debuggable, bool post_bootcomplete, bool background_job_compile, int profile_fd,
-        const char* class_loader_context) {
+        const char* class_loader_context, int target_sdk_version) {
     static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
 
     if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
@@ -327,6 +328,7 @@
     bool have_dex2oat_image_fd = false;
     char dex2oat_image_fd[arraysize("--app-image-fd=") + MAX_INT_LEN];
     size_t class_loader_context_size = arraysize("--class-loader-context=") + PKG_PATH_MAX;
+    char target_sdk_version_arg[arraysize("-Xtarget-sdk-version:") + MAX_INT_LEN];
     char class_loader_context_arg[class_loader_context_size];
     if (class_loader_context != nullptr) {
         snprintf(class_loader_context_arg, class_loader_context_size, "--class-loader-context=%s",
@@ -357,6 +359,7 @@
     if (have_dex2oat_Xmx_flag) {
         sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
     }
+    sprintf(target_sdk_version_arg, "-Xtarget-sdk-version:%d", target_sdk_version);
 
     // Compute compiler filter.
 
@@ -414,6 +417,12 @@
 
     ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
 
+    // Disable cdex if update input vdex is true since this combination of options is not
+    // supported.
+    // Disable cdex for non-background compiles since we don't want to regress app install until
+    // there are enough benefits to justify the tradeoff.
+    const bool disable_cdex = !background_job_compile || (input_vdex_fd == output_vdex_fd);
+
     const char* argv[9  // program name, mandatory arguments and the final NULL
                      + (have_dex2oat_isa_variant ? 1 : 0)
                      + (have_dex2oat_isa_features ? 1 : 0)
@@ -432,7 +441,9 @@
                      + (class_loader_context != nullptr ? 1 : 0)
                      + (has_base_dir ? 1 : 0)
                      + (have_dex2oat_large_app_threshold ? 1 : 0)
-                     + (generate_minidebug_info ? 1 : 0)];
+                     + (disable_cdex ? 1 : 0)
+                     + (generate_minidebug_info ? 1 : 0)
+                     + (target_sdk_version != 0 ? 2 : 0)];
     int i = 0;
     argv[i++] = dex2oat_bin;
     argv[i++] = zip_fd_arg;
@@ -499,6 +510,13 @@
     if (generate_minidebug_info) {
         argv[i++] = kMinidebugDex2oatFlag;
     }
+    if (disable_cdex) {
+        argv[i++] = kDisableCompactDexFlag;
+    }
+    if (target_sdk_version != 0) {
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = target_sdk_version_arg;
+    }
 
     // Do not add after dex2oat_flags, they should override others for debugging.
     argv[i] = NULL;
@@ -963,7 +981,7 @@
   if (EndsWith(oat_path, ".dex")) {
     std::string new_path = oat_path;
     new_path.replace(new_path.length() - strlen(".dex"), strlen(".dex"), new_ext);
-    CHECK(EndsWith(new_path, new_ext.c_str()));
+    CHECK(EndsWith(new_path, new_ext));
     return new_path;
   }
 
@@ -1761,7 +1779,7 @@
 int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
         const char* volume_uuid, const char* class_loader_context, const char* se_info,
-        bool downgrade) {
+        bool downgrade, int target_sdk_version) {
     CHECK(pkgname != nullptr);
     CHECK(pkgname[0] != 0);
     if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
@@ -1878,7 +1896,8 @@
                     boot_complete,
                     background_job_compile,
                     reference_profile_fd.get(),
-                    class_loader_context);
+                    class_loader_context,
+                    target_sdk_version);
         _exit(68);   /* only get here on exec failure */
     } else {
         int res = wait_child(pid);
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 496f594..b1506c3 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -82,7 +82,7 @@
 int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
         const char* volume_uuid, const char* class_loader_context, const char* se_info,
-        bool downgrade);
+        bool downgrade, int target_sdk_version);
 
 bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir,
         const char *apk_path, const char *instruction_set);
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 0c2d341..0e36c33 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -178,6 +178,7 @@
         const char* shared_libraries;
         const char* se_info;
         bool downgrade;
+        int target_sdk_version;
     };
 
     bool ReadSystemProperties() {
@@ -358,6 +359,8 @@
                 return ReadArgumentsV2(argc, argv, true);
             case 3:
                 return ReadArgumentsV3(argc, argv);
+            case 4:
+                return ReadArgumentsV4(argc, argv);
 
             default:
                 LOG(ERROR) << "Unsupported version " << version;
@@ -440,6 +443,10 @@
         // filter, which is not the case during ota.
         package_parameters_.downgrade = false;
 
+        // Set target_sdk_version to 0, ie the platform SDK version. This is
+        // conservative and may force some classes to verify at runtime.
+        package_parameters_.target_sdk_version = 0;
+
         if (param_index != 11) {
             LOG(ERROR) << "Not enough parameters";
             return false;
@@ -523,6 +530,10 @@
             }
         }
 
+        // Set target_sdk_version to 0, ie the platform SDK version. This is
+        // conservative and may force some classes to verify at runtime.
+        package_parameters_.target_sdk_version = 0;
+
         if (param_index != 12) {
             LOG(ERROR) << "Not enough parameters";
             return false;
@@ -531,6 +542,93 @@
         return true;
     }
 
+    bool ReadArgumentsV4(int argc ATTRIBUTE_UNUSED, char** argv) {
+        size_t dexopt_index = 3;
+
+        // Check for "dexopt".
+        if (argv[dexopt_index] == nullptr) {
+            LOG(ERROR) << "Missing parameters";
+            return false;
+        }
+        if (std::string("dexopt").compare(argv[dexopt_index]) != 0) {
+            LOG(ERROR) << "Expected \"dexopt\"";
+            return false;
+        }
+
+        size_t param_index = 0;
+        for (;; ++param_index) {
+            const char* param = argv[dexopt_index + 1 + param_index];
+            if (param == nullptr) {
+                break;
+            }
+
+            switch (param_index) {
+                case 0:
+                    package_parameters_.apk_path = param;
+                    break;
+
+                case 1:
+                    package_parameters_.uid = atoi(param);
+                    break;
+
+                case 2:
+                    package_parameters_.pkgName = param;
+                    break;
+
+                case 3:
+                    package_parameters_.instruction_set = param;
+                    break;
+
+                case 4:
+                    package_parameters_.dexopt_needed = atoi(param);
+                    break;
+
+                case 5:
+                    package_parameters_.oat_dir = param;
+                    break;
+
+                case 6:
+                    package_parameters_.dexopt_flags = atoi(param);
+                    break;
+
+                case 7:
+                    package_parameters_.compiler_filter = param;
+                    break;
+
+                case 8:
+                    package_parameters_.volume_uuid = ParseNull(param);
+                    break;
+
+                case 9:
+                    package_parameters_.shared_libraries = ParseNull(param);
+                    break;
+
+                case 10:
+                    package_parameters_.se_info = ParseNull(param);
+                    break;
+
+                case 11:
+                    package_parameters_.downgrade = ParseBool(param);
+                    break;
+
+                case 12:
+                    package_parameters_.target_sdk_version = atoi(param);
+                    break;
+
+                default:
+                    LOG(ERROR) << "Too many arguments, got " << param;
+                    return false;
+            }
+        }
+
+        if (param_index != 13) {
+            LOG(ERROR) << "Not enough parameters";
+            return false;
+        }
+
+        return true;
+    }
+
     static int ReplaceMask(int input, int old_mask, int new_mask) {
         return (input & old_mask) != 0 ? new_mask : 0;
     }
@@ -634,6 +732,10 @@
         // filter, which is not the case during ota.
         package_parameters_.downgrade = false;
 
+        // Set target_sdk_version to 0, ie the platform SDK version. This is
+        // conservative and may force some classes to verify at runtime.
+        package_parameters_.target_sdk_version = 0;
+
         return true;
     }
 
@@ -882,7 +984,7 @@
         //        backs to do weird things.)
         const char* apk_path = package_parameters_.apk_path;
         CHECK(apk_path != nullptr);
-        if (StartsWith(apk_path, android_root_.c_str())) {
+        if (StartsWith(apk_path, android_root_)) {
             const char* last_slash = strrchr(apk_path, '/');
             if (last_slash != nullptr) {
                 std::string path(apk_path, last_slash - apk_path + 1);
@@ -921,7 +1023,8 @@
                       package_parameters_.volume_uuid,
                       package_parameters_.shared_libraries,
                       package_parameters_.se_info,
-                      package_parameters_.downgrade);
+                      package_parameters_.downgrade,
+                      package_parameters_.target_sdk_version);
     }
 
     int RunPreopt() {
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 2030997..c402c3c 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -59,6 +59,12 @@
 // The file descriptor denoted by status-fd will be closed. The rest of the parameters will
 // be passed on to otapreopt in the chroot.
 static int otapreopt_chroot(const int argc, char **arg) {
+    // Validate arguments
+    // We need the command, status channel and target slot, at a minimum.
+    if(argc < 3) {
+        PLOG(ERROR) << "Not enough arguments.";
+        exit(208);
+    }
     // Close all file descriptors. They are coming from the caller, we do not want to pass them
     // on across our fork/exec into a different domain.
     // 1) Default descriptors.
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index eaf0aa1..ff29506 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -258,6 +258,7 @@
         std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
         std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
         bool downgrade = false;
+        int32_t target_sdk_version = 0;  // default
 
         binder::Status result = service_->dexopt(path,
                                                  uid,
@@ -270,7 +271,8 @@
                                                  volume_uuid_,
                                                  class_loader_context_ptr,
                                                  se_info_ptr,
-                                                 downgrade);
+                                                 downgrade,
+                                                 target_sdk_version);
         ASSERT_EQ(should_binder_call_succeed, result.isOk());
         int expected_access = should_dex_be_compiled ? 0 : -1;
         std::string odex = GetSecondaryDexArtifact(path, "odex");
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp
index 622f866..f371320 100644
--- a/cmds/lshal/DebugCommand.cpp
+++ b/cmds/lshal/DebugCommand.cpp
@@ -18,6 +18,8 @@
 
 #include "Lshal.h"
 
+#include <hidl-util/FQName.h>
+
 namespace android {
 namespace lshal {
 
@@ -46,7 +48,15 @@
     if (status != OK) {
         return status;
     }
+
     auto pair = splitFirst(mInterfaceName, '/');
+
+    FQName fqName(pair.first);
+    if (!fqName.isValid() || fqName.isIdentifier() || !fqName.isFullyQualified()) {
+        mLshal.err() << "Invalid fully-qualified name '" << pair.first << "'\n\n";
+        return USAGE;
+    }
+
     return mLshal.emitDebugInfo(
             pair.first, pair.second.empty() ? "default" : pair.second, mOptions,
             mLshal.out().buf(),
diff --git a/data/etc/android.hardware.telephony.mbms.xml b/data/etc/android.hardware.telephony.mbms.xml
new file mode 100644
index 0000000..271ea58
--- /dev/null
+++ b/data/etc/android.hardware.telephony.mbms.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Feature for devices that support MBMS. -->
+<permissions>
+    <feature name="android.hardware.telephony.mbms" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index b6abc1b..6d739a1 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -46,7 +46,7 @@
     <feature name="android.software.home_screen" />
     <feature name="android.software.input_methods" />
     <feature name="android.software.picture_in_picture" notLowRam="true" />
-    <feature name="android.software.activities_on_secondary_displays" />
+    <feature name="android.software.activities_on_secondary_displays" notLowRam="true" />
     <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
@@ -57,10 +57,8 @@
     <!-- Feature to specify if the device support managed users. -->
     <feature name="android.software.managed_users" notLowRam="true"/>
 
-    <!-- Feature to specify if the device supports a VR mode.
-         feature name="android.software.vr.mode" -->
-    <!-- Devices with all optimizations required to be a "VR Ready" device that
-         pass all CTS tests for this feature must include feature
+    <!-- Devices with all optimizations required to support VR Mode and
+         pass all CDD requirements for this feature may include
          android.hardware.vr.high_performance -->
     <!-- Devices that support VR headtracking features and pass all CDD
          requirements may include
diff --git a/headers/media_plugin/media/openmax/OMX_IVCommon.h b/headers/media_plugin/media/openmax/OMX_IVCommon.h
index f9b6f4b..758d7cf 100644
--- a/headers/media_plugin/media/openmax/OMX_IVCommon.h
+++ b/headers/media_plugin/media/openmax/OMX_IVCommon.h
@@ -165,6 +165,12 @@
      *  format for it. */
     OMX_COLOR_FormatYUV420Flexible = 0x7F420888,
 
+    // 10-bit or 12-bit YUV format, LSB-justified (0's on higher bits)
+    OMX_COLOR_FormatYUV420Planar16 = 0x7F42016B,
+
+    // 32-bit RGBA format, 10-bit per channel, alpha channel in highest 2-bit
+    OMX_COLOR_Format32BitRGBA1010102 = 0x7F00AAAA,
+
     OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100,
     OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00,
     OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03,
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
new file mode 100644
index 0000000..2904718
--- /dev/null
+++ b/libs/binder/ActivityManager.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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 <mutex>
+#include <binder/ActivityManager.h>
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/SystemClock.h>
+
+namespace android {
+
+ActivityManager::ActivityManager()
+{
+}
+
+sp<IActivityManager> ActivityManager::getService()
+{
+    std::lock_guard<Mutex> scoped_lock(mLock);
+    int64_t startTime = 0;
+    sp<IActivityManager> service = mService;
+    while (service == NULL || !IInterface::asBinder(service)->isBinderAlive()) {
+        sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity"));
+        if (binder == NULL) {
+            // Wait for the activity service to come back...
+            if (startTime == 0) {
+                startTime = uptimeMillis();
+                ALOGI("Waiting for activity service");
+            } else if ((uptimeMillis() - startTime) > 10000) {
+                ALOGW("Waiting too long for activity service, giving up");
+                service = NULL;
+                break;
+            }
+            sleep(1);
+        } else {
+            service = interface_cast<IActivityManager>(binder);
+            mService = service;
+        }
+    }
+    return service;
+}
+
+int ActivityManager::openContentUri(const String16& stringUri)
+{
+    sp<IActivityManager> service = getService();
+    return service != NULL ? service->openContentUri(stringUri) : -1;
+}
+
+void ActivityManager::registerUidObserver(const sp<IUidObserver>& observer,
+                                          const int32_t event,
+                                          const int32_t cutpoint,
+                                          const String16& callingPackage)
+{
+    sp<IActivityManager> service = getService();
+    if (service != NULL) {
+        service->registerUidObserver(observer, event, cutpoint, callingPackage);
+    }
+}
+
+void ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer)
+{
+    sp<IActivityManager> service = getService();
+    if (service != NULL) {
+        service->unregisterUidObserver(observer);
+    }
+}
+
+}; // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index c130087..239c04d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -38,6 +38,7 @@
     },
 
     srcs: [
+        "ActivityManager.cpp",
         "AppOpsManager.cpp",
         "Binder.cpp",
         "BpBinder.cpp",
@@ -56,6 +57,7 @@
         "IResultReceiver.cpp",
         "IServiceManager.cpp",
         "IShellCallback.cpp",
+        "IUidObserver.cpp",
         "MemoryBase.cpp",
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
@@ -92,6 +94,7 @@
         "liblog",
         "libcutils",
         "libutils",
+        "libutilscallstack",
     ],
 
     header_libs: [
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 50a8b28..b7a5fd9 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -56,6 +56,28 @@
         }
         return fd;
     }
+
+    virtual void registerUidObserver(const sp<IUidObserver>& observer,
+                                     const int32_t event,
+                                     const int32_t cutpoint,
+                                     const String16& callingPackage)
+    {
+         Parcel data, reply;
+         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+         data.writeStrongBinder(IInterface::asBinder(observer));
+         data.writeInt32(event);
+         data.writeInt32(cutpoint);
+         data.writeString16(callingPackage);
+         remote()->transact(REGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+    }
+
+    virtual void unregisterUidObserver(const sp<IUidObserver>& observer)
+    {
+         Parcel data, reply;
+         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+         data.writeStrongBinder(IInterface::asBinder(observer));
+         remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+    }
 };
 
 // ------------------------------------------------------------------------------------
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
new file mode 100644
index 0000000..697e948
--- /dev/null
+++ b/libs/binder/IUidObserver.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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 <binder/IUidObserver.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class BpUidObserver : public BpInterface<IUidObserver>
+{
+public:
+    explicit BpUidObserver(const sp<IBinder>& impl)
+        : BpInterface<IUidObserver>(impl)
+    {
+    }
+
+    virtual void onUidGone(uid_t uid, bool disabled)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
+        data.writeInt32((int32_t) uid);
+        data.writeInt32(disabled ? 1 : 0);
+        remote()->transact(ON_UID_GONE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    virtual void onUidActive(uid_t uid)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
+        data.writeInt32((int32_t) uid);
+        remote()->transact(ON_UID_ACTIVE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    virtual void onUidIdle(uid_t uid, bool disabled)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
+        data.writeInt32((int32_t) uid);
+        data.writeInt32(disabled ? 1 : 0);
+        remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+// ----------------------------------------------------------------------
+
+IMPLEMENT_META_INTERFACE(UidObserver, "android.app.IUidObserver");
+
+// ----------------------------------------------------------------------
+
+status_t BnUidObserver::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case ON_UID_GONE_TRANSACTION: {
+            CHECK_INTERFACE(IUidObserver, data, reply);
+            uid_t uid = data.readInt32();
+            bool disabled = data.readInt32() == 1;
+            onUidGone(uid, disabled);
+            return NO_ERROR;
+        } break;
+
+        case ON_UID_ACTIVE_TRANSACTION: {
+            CHECK_INTERFACE(IUidObserver, data, reply);
+            uid_t uid = data.readInt32();
+            onUidActive(uid);
+            return NO_ERROR;
+        } break;
+
+        case ON_UID_IDLE_TRANSACTION: {
+            CHECK_INTERFACE(IUidObserver, data, reply);
+            uid_t uid = data.readInt32();
+            bool disabled = data.readInt32() == 1;
+            onUidIdle(uid, disabled);
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}; // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 44039f8..597fca9 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -86,6 +86,12 @@
         }
         LOG_ALWAYS_FATAL("ProcessState was already initialized.");
     }
+
+    if (access(driver, R_OK) == -1) {
+        ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
+        driver = "/dev/binder";
+    }
+
     gProcess = new ProcessState(driver);
     return gProcess;
 }
@@ -420,7 +426,7 @@
         mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
         if (mVMStart == MAP_FAILED) {
             // *sigh*
-            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
+            ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
             close(mDriverFD);
             mDriverFD = -1;
             mDriverName.clear();
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
new file mode 100644
index 0000000..408c428
--- /dev/null
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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_ACTIVITY_MANAGER_H
+#define ANDROID_ACTIVITY_MANAGER_H
+
+#include <binder/IActivityManager.h>
+
+#include <utils/threads.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class ActivityManager
+{
+public:
+
+    enum {
+        // Flag for registerUidObserver: report uid gone
+        UID_OBSERVER_GONE = 1<<1,
+        // Flag for registerUidObserver: report uid has become idle
+        UID_OBSERVER_IDLE = 1<<2,
+        // Flag for registerUidObserver: report uid has become active
+        UID_OBSERVER_ACTIVE = 1<<3
+    };
+
+    enum {
+        // Not a real process state
+        PROCESS_STATE_UNKNOWN = -1
+    };
+
+    ActivityManager();
+
+    int openContentUri(const String16& stringUri);
+    void registerUidObserver(const sp<IUidObserver>& observer,
+                             const int32_t event,
+                             const int32_t cutpoint,
+                             const String16& callingPackage);
+    void unregisterUidObserver(const sp<IUidObserver>& observer);
+
+private:
+    Mutex mLock;
+    sp<IActivityManager> mService;
+    sp<IActivityManager> getService();
+};
+
+
+}; // namespace android
+// ---------------------------------------------------------------------------
+#endif // ANDROID_ACTIVITY_MANAGER_H
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index 5ad2180..bac2a99 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -18,6 +18,7 @@
 #define ANDROID_IACTIVITY_MANAGER_H
 
 #include <binder/IInterface.h>
+#include <binder/IUidObserver.h>
 
 namespace android {
 
@@ -28,10 +29,17 @@
 public:
     DECLARE_META_INTERFACE(ActivityManager)
 
-    virtual int openContentUri(const String16& /* stringUri */) = 0;
+    virtual int openContentUri(const String16& stringUri) = 0;
+    virtual void registerUidObserver(const sp<IUidObserver>& observer,
+                                     const int32_t event,
+                                     const int32_t cutpoint,
+                                     const String16& callingPackage) = 0;
+    virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
 
     enum {
-        OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+        OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        REGISTER_UID_OBSERVER_TRANSACTION,
+        UNREGISTER_UID_OBSERVER_TRANSACTION
     };
 };
 
@@ -39,4 +47,4 @@
 
 }; // namespace android
 
-#endif // ANDROID_IACTIVITY_MANAGER_H
\ No newline at end of file
+#endif // ANDROID_IACTIVITY_MANAGER_H
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
new file mode 100644
index 0000000..fd4d8a6
--- /dev/null
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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_IUID_OBSERVER_H
+#define ANDROID_IUID_OBSERVER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IUidObserver : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(UidObserver)
+
+    virtual void onUidGone(uid_t uid, bool disabled) = 0;
+    virtual void onUidActive(uid_t uid) = 0;
+    virtual void onUidIdle(uid_t uid, bool disabled) = 0;
+
+    enum {
+        ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        ON_UID_ACTIVE_TRANSACTION,
+        ON_UID_IDLE_TRANSACTION
+    };
+};
+
+// ----------------------------------------------------------------------
+
+class BnUidObserver : public BnInterface<IUidObserver>
+{
+public:
+    virtual status_t  onTransact(uint32_t code,
+                                 const Parcel& data,
+                                 Parcel* reply,
+                                 uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IUID_OBSERVER_H
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 9f99538..4da30e9 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -22,7 +22,6 @@
     cflags: ["-Wall", "-Werror"],
 
     shared_libs: [
-        "libnativeloader",
         "liblog",
     ],
 
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index f46e9f6..961f101 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -20,13 +20,23 @@
 
 #include <mutex>
 
+#include <android/dlext.h>
 #include <log/log.h>
-#include <nativeloader/dlext_namespaces.h>
-#include <nativeloader/native_loader.h>
 
 // TODO(b/37049319) Get this from a header once one exists
 extern "C" {
   android_namespace_t* android_get_exported_namespace(const char*);
+  android_namespace_t* android_create_namespace(const char* name,
+                                                const char* ld_library_path,
+                                                const char* default_library_path,
+                                                uint64_t type,
+                                                const char* permitted_when_isolated_path,
+                                                android_namespace_t* parent);
+
+  enum {
+     ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+     ANDROID_NAMESPACE_TYPE_SHARED = 2,
+  };
 }
 
 namespace android {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index cf72d55..ad04d03 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -19,7 +19,7 @@
 
 cc_library_shared {
     name: "libgui",
-    vendor_available: true,
+    vendor_available: false,
     vndk: {
         enabled: true,
     },
@@ -62,6 +62,9 @@
         // Allow documentation warnings
         "-Wno-documentation",
 
+        // Allow implicit instantiation for templated class function
+        "-Wno-undefined-func-template",
+
         "-DDEBUG_ONLY_CODE=0",
     ],
 
@@ -79,6 +82,7 @@
 
     srcs: [
         "BitTube.cpp",
+        "BufferHubProducer.cpp",
         "BufferItem.cpp",
         "BufferItemConsumer.cpp",
         "BufferQueue.cpp",
@@ -92,6 +96,7 @@
         "FrameTimestamps.cpp",
         "GLConsumer.cpp",
         "GuiConfig.cpp",
+        "HdrMetadata.cpp",
         "IDisplayEventConnection.cpp",
         "IConsumerListener.cpp",
         "IGraphicBufferConsumer.cpp",
@@ -130,7 +135,15 @@
         "android.hardware.configstore-utils",
     ],
 
+    // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
+    static_libs: [
+        "libbufferhub",
+        "libbufferhubqueue",
+        "libpdx_default_transport",
+    ],
+
     header_libs: [
+        "libdvr_headers",
         "libnativebase_headers",
         "libgui_headers",
     ],
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
new file mode 100644
index 0000000..af1f833
--- /dev/null
+++ b/libs/gui/BufferHubProducer.cpp
@@ -0,0 +1,710 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#endif
+
+// The following headers are included without checking every warning.
+// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
+// in these headers and their dependencies.
+#include <dvr/dvr_api.h>
+#include <gui/BufferHubProducer.h>
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#include <inttypes.h>
+#include <log/log.h>
+#include <system/window.h>
+
+namespace android {
+
+/* static */
+sp<BufferHubProducer> BufferHubProducer::Create(const std::shared_ptr<dvr::ProducerQueue>& queue) {
+    if (queue->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
+        ALOGE("BufferHubProducer::Create producer's metadata size is different "
+              "than the size of DvrNativeBufferMetadata");
+        return nullptr;
+    }
+
+    sp<BufferHubProducer> producer = new BufferHubProducer;
+    producer->queue_ = queue;
+    return producer;
+}
+
+/* static */
+sp<BufferHubProducer> BufferHubProducer::Create(dvr::ProducerQueueParcelable parcelable) {
+    if (!parcelable.IsValid()) {
+        ALOGE("BufferHubProducer::Create: Invalid producer parcelable.");
+        return nullptr;
+    }
+
+    sp<BufferHubProducer> producer = new BufferHubProducer;
+    producer->queue_ = dvr::ProducerQueue::Import(parcelable.TakeChannelHandle());
+    return producer;
+}
+
+status_t BufferHubProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+    ALOGV("requestBuffer: slot=%d", slot);
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (connected_api_ == kNoConnectedApi) {
+        ALOGE("requestBuffer: BufferHubProducer has no connected producer");
+        return NO_INIT;
+    }
+
+    if (slot < 0 || slot >= max_buffer_count_) {
+        ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
+        return BAD_VALUE;
+    } else if (!buffers_[slot].mBufferState.isDequeued()) {
+        ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)", slot,
+              buffers_[slot].mBufferState.string());
+        return BAD_VALUE;
+    } else if (buffers_[slot].mGraphicBuffer != nullptr) {
+        ALOGE("requestBuffer: slot %d is not empty.", slot);
+        return BAD_VALUE;
+    } else if (buffers_[slot].mBufferProducer == nullptr) {
+        ALOGE("requestBuffer: slot %d is not dequeued.", slot);
+        return BAD_VALUE;
+    }
+
+    const auto& buffer_producer = buffers_[slot].mBufferProducer;
+    sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer();
+
+    buffers_[slot].mGraphicBuffer = graphic_buffer;
+    buffers_[slot].mRequestBufferCalled = true;
+
+    *buf = graphic_buffer;
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::setMaxDequeuedBufferCount(int max_dequeued_buffers) {
+    ALOGV("setMaxDequeuedBufferCount: max_dequeued_buffers=%d", max_dequeued_buffers);
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (max_dequeued_buffers <= 0 ||
+        max_dequeued_buffers >
+                int(dvr::BufferHubQueue::kMaxQueueCapacity - kDefaultUndequeuedBuffers)) {
+        ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]", max_dequeued_buffers,
+              dvr::BufferHubQueue::kMaxQueueCapacity);
+        return BAD_VALUE;
+    }
+
+    // The new dequeued_buffers count should not be violated by the number
+    // of currently dequeued buffers.
+    int dequeued_count = 0;
+    for (const auto& buf : buffers_) {
+        if (buf.mBufferState.isDequeued()) {
+            dequeued_count++;
+        }
+    }
+    if (dequeued_count > max_dequeued_buffers) {
+        ALOGE("setMaxDequeuedBufferCount: the requested dequeued_buffers"
+              "count (%d) exceeds the current dequeued buffer count (%d)",
+              max_dequeued_buffers, dequeued_count);
+        return BAD_VALUE;
+    }
+
+    max_dequeued_buffer_count_ = max_dequeued_buffers;
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::setAsyncMode(bool async) {
+    if (async) {
+        // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer
+        // automatically and behaves differently from IGraphicBufferConsumer. Thus,
+        // android::BufferQueue's async mode (a.k.a. allocating an additional buffer
+        // to prevent dequeueBuffer from being blocking) technically does not apply
+        // here.
+        //
+        // In Daydream, non-blocking producer side dequeue is guaranteed by careful
+        // buffer consumer implementations. In another word, BufferHubQueue based
+        // dequeueBuffer should never block whether setAsyncMode(true) is set or
+        // not.
+        //
+        // See: IGraphicBufferProducer::setAsyncMode and
+        // BufferQueueProducer::setAsyncMode for more about original implementation.
+        ALOGW("BufferHubProducer::setAsyncMode: BufferHubQueue should always be "
+              "asynchronous. This call makes no effact.");
+        return NO_ERROR;
+    }
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
+                                          uint32_t height, PixelFormat format, uint64_t usage,
+                                          uint64_t* /*outBufferAge*/,
+                                          FrameEventHistoryDelta* /* out_timestamps */) {
+    ALOGV("dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage);
+
+    status_t ret;
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (connected_api_ == kNoConnectedApi) {
+        ALOGE("dequeueBuffer: BufferQueue has no connected producer");
+        return NO_INIT;
+    }
+
+    const uint32_t kLayerCount = 1;
+    if (int32_t(queue_->capacity()) < max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) {
+        // Lazy allocation. When the capacity of |queue_| has not reached
+        // |max_dequeued_buffer_count_|, allocate new buffer.
+        // TODO(jwcai) To save memory, the really reasonable thing to do is to go
+        // over existing slots and find first existing one to dequeue.
+        ret = AllocateBuffer(width, height, kLayerCount, format, usage);
+        if (ret < 0) return ret;
+    }
+
+    size_t slot = 0;
+    std::shared_ptr<dvr::BufferProducer> buffer_producer;
+
+    for (size_t retry = 0; retry < dvr::BufferHubQueue::kMaxQueueCapacity; retry++) {
+        LocalHandle fence;
+        auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
+        if (!buffer_status) return NO_MEMORY;
+
+        buffer_producer = buffer_status.take();
+        if (!buffer_producer) return NO_MEMORY;
+
+        if (width == buffer_producer->width() && height == buffer_producer->height() &&
+            uint32_t(format) == buffer_producer->format()) {
+            // The producer queue returns a buffer producer matches the request.
+            break;
+        }
+
+        // Needs reallocation.
+        // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
+        ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
+              "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
+              "re-allocattion.",
+              width, height, format, slot, buffer_producer->width(), buffer_producer->height(),
+              buffer_producer->format());
+        // Mark the slot as reallocating, so that later we can set
+        // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
+        buffers_[slot].mIsReallocating = true;
+
+        // Remove the old buffer once the allocation before allocating its
+        // replacement.
+        RemoveBuffer(slot);
+
+        // Allocate a new producer buffer with new buffer configs. Note that if
+        // there are already multiple buffers in the queue, the next one returned
+        // from |queue_->Dequeue| may not be the new buffer we just reallocated.
+        // Retry up to BufferHubQueue::kMaxQueueCapacity times.
+        ret = AllocateBuffer(width, height, kLayerCount, format, usage);
+        if (ret < 0) return ret;
+    }
+
+    // With the BufferHub backed solution. Buffer slot returned from
+    // |queue_->Dequeue| is guaranteed to avaiable for producer's use.
+    // It's either in free state (if the buffer has never been used before) or
+    // in queued state (if the buffer has been dequeued and queued back to
+    // BufferHubQueue).
+    LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() &&
+                         !buffers_[slot].mBufferState.isQueued()),
+                        "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot,
+                        buffers_[slot].mBufferState.string());
+
+    buffers_[slot].mBufferState.freeQueued();
+    buffers_[slot].mBufferState.dequeue();
+    ALOGV("dequeueBuffer: slot=%zu", slot);
+
+    // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
+    // just need to exopose that through |BufferHubQueue| once we need fence.
+    *out_fence = Fence::NO_FENCE;
+    *out_slot = int(slot);
+    ret = NO_ERROR;
+
+    if (buffers_[slot].mIsReallocating) {
+        ret |= BUFFER_NEEDS_REALLOCATION;
+        buffers_[slot].mIsReallocating = false;
+    }
+
+    return ret;
+}
+
+status_t BufferHubProducer::detachBuffer(int /* slot */) {
+    ALOGE("BufferHubProducer::detachBuffer not implemented.");
+    return INVALID_OPERATION;
+}
+
+status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* /* out_buffer */,
+                                             sp<Fence>* /* out_fence */) {
+    ALOGE("BufferHubProducer::detachNextBuffer not implemented.");
+    return INVALID_OPERATION;
+}
+
+status_t BufferHubProducer::attachBuffer(int* /* out_slot */,
+                                         const sp<GraphicBuffer>& /* buffer */) {
+    // With this BufferHub backed implementation, we assume (for now) all buffers
+    // are allocated and owned by the BufferHub. Thus the attempt of transfering
+    // ownership of a buffer to the buffer queue is intentionally unsupported.
+    LOG_ALWAYS_FATAL("BufferHubProducer::attachBuffer not supported.");
+    return INVALID_OPERATION;
+}
+
+status_t BufferHubProducer::queueBuffer(int slot, const QueueBufferInput& input,
+                                        QueueBufferOutput* output) {
+    ALOGV("queueBuffer: slot %d", slot);
+
+    if (output == nullptr) {
+        return BAD_VALUE;
+    }
+
+    int64_t timestamp;
+    bool is_auto_timestamp;
+    android_dataspace dataspace;
+    Rect crop(Rect::EMPTY_RECT);
+    int scaling_mode;
+    uint32_t transform;
+    sp<Fence> fence;
+
+    input.deflate(&timestamp, &is_auto_timestamp, &dataspace, &crop, &scaling_mode, &transform,
+                  &fence);
+
+    // Check input scaling mode is valid.
+    switch (scaling_mode) {
+        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+        case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
+            break;
+        default:
+            ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode);
+            return BAD_VALUE;
+    }
+
+    // Check input fence is valid.
+    if (fence == nullptr) {
+        ALOGE("queueBuffer: fence is NULL");
+        return BAD_VALUE;
+    }
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (connected_api_ == kNoConnectedApi) {
+        ALOGE("queueBuffer: BufferQueue has no connected producer");
+        return NO_INIT;
+    }
+
+    if (slot < 0 || slot >= max_buffer_count_) {
+        ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
+        return BAD_VALUE;
+    } else if (!buffers_[slot].mBufferState.isDequeued()) {
+        ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)", slot,
+              buffers_[slot].mBufferState.string());
+        return BAD_VALUE;
+    } else if ((!buffers_[slot].mRequestBufferCalled || buffers_[slot].mGraphicBuffer == nullptr)) {
+        ALOGE("queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, "
+              "mGraphicBuffer=%p)",
+              slot, buffers_[slot].mRequestBufferCalled, buffers_[slot].mGraphicBuffer.get());
+        return BAD_VALUE;
+    }
+
+    // Post the buffer producer with timestamp in the metadata.
+    const auto& buffer_producer = buffers_[slot].mBufferProducer;
+
+    // Check input crop is not out of boundary of current buffer.
+    Rect buffer_rect(buffer_producer->width(), buffer_producer->height());
+    Rect cropped_rect(Rect::EMPTY_RECT);
+    crop.intersect(buffer_rect, &cropped_rect);
+    if (cropped_rect != crop) {
+        ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot);
+        return BAD_VALUE;
+    }
+
+    LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
+
+    DvrNativeBufferMetadata meta_data;
+    meta_data.timestamp = timestamp;
+    meta_data.is_auto_timestamp = int32_t(is_auto_timestamp);
+    meta_data.dataspace = int32_t(dataspace);
+    meta_data.crop_left = crop.left;
+    meta_data.crop_top = crop.top;
+    meta_data.crop_right = crop.right;
+    meta_data.crop_bottom = crop.bottom;
+    meta_data.scaling_mode = int32_t(scaling_mode);
+    meta_data.transform = int32_t(transform);
+
+    buffer_producer->PostAsync(&meta_data, fence_fd);
+    buffers_[slot].mBufferState.queue();
+
+    output->width = buffer_producer->width();
+    output->height = buffer_producer->height();
+    output->transformHint = 0; // default value, we don't use it yet.
+
+    // |numPendingBuffers| counts of the number of buffers that has been enqueued
+    // by the producer but not yet acquired by the consumer. Due to the nature
+    // of BufferHubQueue design, this is hard to trace from the producer's client
+    // side, but it's safe to assume it's zero.
+    output->numPendingBuffers = 0;
+
+    // Note that we are not setting nextFrameNumber here as it seems to be only
+    // used by surface flinger. See more at b/22802885, ag/791760.
+    output->nextFrameNumber = 0;
+
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+    ALOGV(__FUNCTION__);
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (connected_api_ == kNoConnectedApi) {
+        ALOGE("cancelBuffer: BufferQueue has no connected producer");
+        return NO_INIT;
+    }
+
+    if (slot < 0 || slot >= max_buffer_count_) {
+        ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
+        return BAD_VALUE;
+    } else if (!buffers_[slot].mBufferState.isDequeued()) {
+        ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)", slot,
+              buffers_[slot].mBufferState.string());
+        return BAD_VALUE;
+    } else if (fence == nullptr) {
+        ALOGE("cancelBuffer: fence is NULL");
+        return BAD_VALUE;
+    }
+
+    auto buffer_producer = buffers_[slot].mBufferProducer;
+    queue_->Enqueue(buffer_producer, size_t(slot), 0ULL);
+    buffers_[slot].mBufferState.cancel();
+    buffers_[slot].mFence = fence;
+    ALOGV("cancelBuffer: slot %d", slot);
+
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::query(int what, int* out_value) {
+    ALOGV(__FUNCTION__);
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (out_value == nullptr) {
+        ALOGE("query: out_value was NULL");
+        return BAD_VALUE;
+    }
+
+    int value = 0;
+    switch (what) {
+        case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+            // TODO(b/36187402) This should be the maximum number of buffers that this
+            // producer queue's consumer can acquire. Set to be at least one. Need to
+            // find a way to set from the consumer side.
+            value = kDefaultUndequeuedBuffers;
+            break;
+        case NATIVE_WINDOW_BUFFER_AGE:
+            value = 0;
+            break;
+        case NATIVE_WINDOW_WIDTH:
+            value = int32_t(queue_->default_width());
+            break;
+        case NATIVE_WINDOW_HEIGHT:
+            value = int32_t(queue_->default_height());
+            break;
+        case NATIVE_WINDOW_FORMAT:
+            value = int32_t(queue_->default_format());
+            break;
+        case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+            // BufferHubQueue is always operating in async mode, thus semantically
+            // consumer can never be running behind. See BufferQueueCore.cpp core
+            // for more information about the original meaning of this flag.
+            value = 0;
+            break;
+        case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+            // TODO(jwcai) This is currently not implement as we don't need
+            // IGraphicBufferConsumer parity.
+            value = 0;
+            break;
+        case NATIVE_WINDOW_DEFAULT_DATASPACE:
+            // TODO(jwcai) Return the default value android::BufferQueue is using as
+            // there is no way dvr::ConsumerQueue can set it.
+            value = 0; // HAL_DATASPACE_UNKNOWN
+            break;
+        case NATIVE_WINDOW_STICKY_TRANSFORM:
+            // TODO(jwcai) Return the default value android::BufferQueue is using as
+            // there is no way dvr::ConsumerQueue can set it.
+            value = 0;
+            break;
+        case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
+            // In Daydream's implementation, the consumer end (i.e. VR Compostior)
+            // knows how to handle protected buffers.
+            value = 1;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    ALOGV("query: key=%d, v=%d", what, value);
+    *out_value = value;
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::connect(const sp<IProducerListener>& /* listener */, int api,
+                                    bool /* producer_controlled_by_app */,
+                                    QueueBufferOutput* output) {
+    // Consumer interaction are actually handled by buffer hub, and we need
+    // to maintain consumer operations here. We only need to perform basic input
+    // parameter checks here.
+    ALOGV(__FUNCTION__);
+
+    if (output == nullptr) {
+        return BAD_VALUE;
+    }
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (connected_api_ != kNoConnectedApi) {
+        return BAD_VALUE;
+    }
+
+    if (!queue_->is_connected()) {
+        ALOGE("BufferHubProducer::connect: This BufferHubProducer is not "
+              "connected to bufferhud. Has it been taken out as a parcelable?");
+        return BAD_VALUE;
+    }
+
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+        case NATIVE_WINDOW_API_CPU:
+        case NATIVE_WINDOW_API_MEDIA:
+        case NATIVE_WINDOW_API_CAMERA:
+            connected_api_ = api;
+
+            output->width = queue_->default_width();
+            output->height = queue_->default_height();
+
+            // default values, we don't use them yet.
+            output->transformHint = 0;
+            output->numPendingBuffers = 0;
+            output->nextFrameNumber = 0;
+            output->bufferReplaced = false;
+
+            break;
+        default:
+            ALOGE("BufferHubProducer::connect: unknow API %d", api);
+            return BAD_VALUE;
+    }
+
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::disconnect(int api, DisconnectMode /*mode*/) {
+    // Consumer interaction are actually handled by buffer hub, and we need
+    // to maintain consumer operations here.  We only need to perform basic input
+    // parameter checks here.
+    ALOGV(__FUNCTION__);
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (kNoConnectedApi == connected_api_) {
+        return NO_INIT;
+    } else if (api != connected_api_) {
+        return BAD_VALUE;
+    }
+
+    FreeAllBuffers();
+    connected_api_ = kNoConnectedApi;
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::setSidebandStream(const sp<NativeHandle>& stream) {
+    if (stream != nullptr) {
+        // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
+        // metadata.
+        ALOGE("SidebandStream is not currently supported.");
+        return INVALID_OPERATION;
+    }
+    return NO_ERROR;
+}
+
+void BufferHubProducer::allocateBuffers(uint32_t /* width */, uint32_t /* height */,
+                                        PixelFormat /* format */, uint64_t /* usage */) {
+    // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
+    // of buffers permitted by the current BufferQueue configuration (aka
+    // |max_buffer_count_|).
+    ALOGE("BufferHubProducer::allocateBuffers not implemented.");
+}
+
+status_t BufferHubProducer::allowAllocation(bool /* allow */) {
+    ALOGE("BufferHubProducer::allowAllocation not implemented.");
+    return INVALID_OPERATION;
+}
+
+status_t BufferHubProducer::setGenerationNumber(uint32_t generation_number) {
+    ALOGV(__FUNCTION__);
+
+    std::unique_lock<std::mutex> lock(mutex_);
+    generation_number_ = generation_number;
+    return NO_ERROR;
+}
+
+String8 BufferHubProducer::getConsumerName() const {
+    // BufferHub based implementation could have one to many producer/consumer
+    // relationship, thus |getConsumerName| from the producer side does not
+    // make any sense.
+    ALOGE("BufferHubProducer::getConsumerName not supported.");
+    return String8("BufferHubQueue::DummyConsumer");
+}
+
+status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) {
+    if (shared_buffer_mode) {
+        ALOGE("BufferHubProducer::setSharedBufferMode(true) is not supported.");
+        // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow.
+        return INVALID_OPERATION;
+    }
+    // Setting to default should just work as a no-op.
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::setAutoRefresh(bool auto_refresh) {
+    if (auto_refresh) {
+        ALOGE("BufferHubProducer::setAutoRefresh(true) is not supported.");
+        return INVALID_OPERATION;
+    }
+    // Setting to default should just work as a no-op.
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::setDequeueTimeout(nsecs_t timeout) {
+    ALOGV(__FUNCTION__);
+
+    std::unique_lock<std::mutex> lock(mutex_);
+    dequeue_timeout_ms_ = int(timeout / (1000 * 1000));
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::getLastQueuedBuffer(sp<GraphicBuffer>* /* out_buffer */,
+                                                sp<Fence>* /* out_fence */,
+                                                float /*out_transform_matrix*/[16]) {
+    ALOGE("BufferHubProducer::getLastQueuedBuffer not implemented.");
+    return INVALID_OPERATION;
+}
+
+void BufferHubProducer::getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {
+    ALOGE("BufferHubProducer::getFrameTimestamps not implemented.");
+}
+
+status_t BufferHubProducer::getUniqueId(uint64_t* out_id) const {
+    ALOGV(__FUNCTION__);
+
+    *out_id = unique_id_;
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::getConsumerUsage(uint64_t* out_usage) const {
+    ALOGV(__FUNCTION__);
+
+    // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS
+    *out_usage = 0;
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::TakeAsParcelable(dvr::ProducerQueueParcelable* out_parcelable) {
+    if (!out_parcelable || out_parcelable->IsValid()) return BAD_VALUE;
+
+    if (connected_api_ != kNoConnectedApi) {
+        ALOGE("BufferHubProducer::TakeAsParcelable: BufferHubProducer has "
+              "connected client. Must disconnect first.");
+        return BAD_VALUE;
+    }
+
+    if (!queue_->is_connected()) {
+        ALOGE("BufferHubProducer::TakeAsParcelable: This BufferHubProducer "
+              "is not connected to bufferhud. Has it been taken out as a "
+              "parcelable?");
+        return BAD_VALUE;
+    }
+
+    auto status = queue_->TakeAsParcelable();
+    if (!status) {
+        ALOGE("BufferHubProducer::TakeAsParcelable: Failed to take out "
+              "ProducuerQueueParcelable from the producer queue, error: %s.",
+              status.GetErrorMessage().c_str());
+        return BAD_VALUE;
+    }
+
+    *out_parcelable = status.take();
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
+                                           PixelFormat format, uint64_t usage) {
+    auto status = queue_->AllocateBuffer(width, height, layer_count, uint32_t(format), usage);
+    if (!status) {
+        ALOGE("BufferHubProducer::AllocateBuffer: Failed to allocate buffer: %s",
+              status.GetErrorMessage().c_str());
+        return NO_MEMORY;
+    }
+
+    size_t slot = status.get();
+    auto buffer_producer = queue_->GetBuffer(slot);
+
+    LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr, "Failed to get buffer producer at slot: %zu",
+                        slot);
+
+    buffers_[slot].mBufferProducer = buffer_producer;
+
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::RemoveBuffer(size_t slot) {
+    auto status = queue_->RemoveBuffer(slot);
+    if (!status) {
+        ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer: %s",
+              status.GetErrorMessage().c_str());
+        return INVALID_OPERATION;
+    }
+
+    // Reset in memory objects related the the buffer.
+    buffers_[slot].mBufferProducer = nullptr;
+    buffers_[slot].mGraphicBuffer = nullptr;
+    buffers_[slot].mBufferState.detachProducer();
+    return NO_ERROR;
+}
+
+status_t BufferHubProducer::FreeAllBuffers() {
+    for (size_t slot = 0; slot < dvr::BufferHubQueue::kMaxQueueCapacity; slot++) {
+        // Reset in memory objects related the the buffer.
+        buffers_[slot].mGraphicBuffer = nullptr;
+        buffers_[slot].mBufferState.reset();
+        buffers_[slot].mRequestBufferCalled = false;
+        buffers_[slot].mBufferProducer = nullptr;
+        buffers_[slot].mFence = Fence::NO_FENCE;
+    }
+
+    auto status = queue_->FreeAllBuffers();
+    if (!status) {
+        ALOGE("BufferHubProducer::FreeAllBuffers: Failed to free all buffers on "
+              "the queue: %s",
+              status.GetErrorMessage().c_str());
+    }
+
+    if (queue_->capacity() != 0 || queue_->count() != 0) {
+        LOG_ALWAYS_FATAL("BufferHubProducer::FreeAllBuffers: Not all buffers are freed.");
+    }
+
+    return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 69b5962..f7409dc 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -98,6 +98,7 @@
         size = FlattenableUtils::align<4>(size);
     }
     size += mSurfaceDamage.getFlattenedSize();
+    size += mHdrMetadata.getFlattenedSize();
     size = FlattenableUtils::align<8>(size);
     return size + getPodSize();
 }
@@ -151,6 +152,10 @@
     if (err) return err;
     FlattenableUtils::advance(buffer, size, mSurfaceDamage.getFlattenedSize());
 
+    err = mHdrMetadata.flatten(buffer, size);
+    if (err) return err;
+    FlattenableUtils::advance(buffer, size, mHdrMetadata.getFlattenedSize());
+
     // Check we still have enough space
     if (size < getPodSize()) {
         return NO_MEMORY;
@@ -212,6 +217,10 @@
     if (err) return err;
     FlattenableUtils::advance(buffer, size, mSurfaceDamage.getFlattenedSize());
 
+    err = mHdrMetadata.unflatten(buffer, size);
+    if (err) return err;
+    FlattenableUtils::advance(buffer, size, mHdrMetadata.getFlattenedSize());
+
     // Check we still have enough space
     if (size < getPodSize()) {
         return NO_MEMORY;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index c5cab2d..add857c 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -765,6 +765,7 @@
             &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
             &getFrameTimestamps);
     const Region& surfaceDamage = input.getSurfaceDamage();
+    const HdrMetadata& hdrMetadata = input.getHdrMetadata();
 
     if (acquireFence == NULL) {
         BQ_LOGE("queueBuffer: fence is NULL");
@@ -825,9 +826,9 @@
         }
 
         BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
-                " crop=[%d,%d,%d,%d] transform=%#x scale=%s",
-                slot, mCore->mFrameCounter + 1, requestedPresentTimestamp,
-                dataSpace, crop.left, crop.top, crop.right, crop.bottom,
+                " validHdrMetadataTypes=0x%x crop=[%d,%d,%d,%d] transform=%#x scale=%s",
+                slot, mCore->mFrameCounter + 1, requestedPresentTimestamp, dataSpace,
+                hdrMetadata.validTypes, crop.left, crop.top, crop.right, crop.bottom,
                 transform,
                 BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));
 
@@ -866,6 +867,7 @@
         item.mTimestamp = requestedPresentTimestamp;
         item.mIsAutoTimestamp = isAutoTimestamp;
         item.mDataSpace = dataSpace;
+        item.mHdrMetadata = hdrMetadata;
         item.mFrameNumber = currentFrameNumber;
         item.mSlot = slot;
         item.mFence = acquireFence;
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
new file mode 100644
index 0000000..299bdfa
--- /dev/null
+++ b/libs/gui/HdrMetadata.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/HdrMetadata.h>
+
+namespace android {
+
+size_t HdrMetadata::getFlattenedSize() const {
+    size_t size = sizeof(validTypes);
+    if (validTypes & SMPTE2086) {
+        size += sizeof(smpte2086);
+    }
+    if (validTypes & CTA861_3) {
+        size += sizeof(cta8613);
+    }
+    return size;
+}
+
+status_t HdrMetadata::flatten(void* buffer, size_t size) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, validTypes);
+    if (validTypes & SMPTE2086) {
+        FlattenableUtils::write(buffer, size, smpte2086);
+    }
+    if (validTypes & CTA861_3) {
+        FlattenableUtils::write(buffer, size, cta8613);
+    }
+
+    return NO_ERROR;
+}
+
+status_t HdrMetadata::unflatten(void const* buffer, size_t size) {
+    if (size < sizeof(validTypes)) {
+        return NO_MEMORY;
+    }
+    FlattenableUtils::read(buffer, size, validTypes);
+    if (validTypes & SMPTE2086) {
+        if (size < sizeof(smpte2086)) {
+            return NO_MEMORY;
+        }
+        FlattenableUtils::read(buffer, size, smpte2086);
+    }
+    if (validTypes & CTA861_3) {
+        if (size < sizeof(cta8613)) {
+            return NO_MEMORY;
+        }
+        FlattenableUtils::read(buffer, size, cta8613);
+    }
+
+    return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 71e22ce..7e49024 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -951,7 +951,8 @@
 size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
     return minFlattenedSize() +
             fence->getFlattenedSize() +
-            surfaceDamage.getFlattenedSize();
+            surfaceDamage.getFlattenedSize() +
+            hdrMetadata.getFlattenedSize();
 }
 
 size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
@@ -978,7 +979,12 @@
     if (result != NO_ERROR) {
         return result;
     }
-    return surfaceDamage.flatten(buffer, size);
+    result = surfaceDamage.flatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+    return hdrMetadata.flatten(buffer, size);
 }
 
 status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
@@ -1002,7 +1008,12 @@
     if (result != NO_ERROR) {
         return result;
     }
-    return surfaceDamage.unflatten(buffer, size);
+    result = surfaceDamage.unflatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+    return hdrMetadata.unflatten(buffer, size);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 80216bc..a4aec6e 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -667,6 +667,9 @@
             mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
             fence, mStickyTransform, mEnableFrameTimestamps);
 
+    // we should send HDR metadata as needed if this becomes a bottleneck
+    input.setHdrMetadata(mHdrMetadata);
+
     if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
         input.setSurfaceDamage(Region::INVALID_REGION);
     } else {
@@ -944,6 +947,12 @@
     case NATIVE_WINDOW_SET_BUFFERS_DATASPACE:
         res = dispatchSetBuffersDataSpace(args);
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA:
+        res = dispatchSetBuffersSmpte2086Metadata(args);
+        break;
+    case NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA:
+        res = dispatchSetBuffersCta8613Metadata(args);
+        break;
     case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
         res = dispatchSetSurfaceDamage(args);
         break;
@@ -1088,6 +1097,18 @@
     return setBuffersDataSpace(dataspace);
 }
 
+int Surface::dispatchSetBuffersSmpte2086Metadata(va_list args) {
+    const android_smpte2086_metadata* metadata =
+        va_arg(args, const android_smpte2086_metadata*);
+    return setBuffersSmpte2086Metadata(metadata);
+}
+
+int Surface::dispatchSetBuffersCta8613Metadata(va_list args) {
+    const android_cta861_3_metadata* metadata =
+        va_arg(args, const android_cta861_3_metadata*);
+    return setBuffersCta8613Metadata(metadata);
+}
+
 int Surface::dispatchSetSurfaceDamage(va_list args) {
     android_native_rect_t* rects = va_arg(args, android_native_rect_t*);
     size_t numRects = va_arg(args, size_t);
@@ -1512,6 +1533,30 @@
     return NO_ERROR;
 }
 
+int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata) {
+    ALOGV("Surface::setBuffersSmpte2086Metadata");
+    Mutex::Autolock lock(mMutex);
+    if (metadata) {
+        mHdrMetadata.smpte2086 = *metadata;
+        mHdrMetadata.validTypes |= HdrMetadata::SMPTE2086;
+    } else {
+        mHdrMetadata.validTypes &= ~HdrMetadata::SMPTE2086;
+    }
+    return NO_ERROR;
+}
+
+int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata) {
+    ALOGV("Surface::setBuffersCta8613Metadata");
+    Mutex::Autolock lock(mMutex);
+    if (metadata) {
+        mHdrMetadata.cta8613 = *metadata;
+        mHdrMetadata.validTypes |= HdrMetadata::CTA861_3;
+    } else {
+        mHdrMetadata.validTypes &= ~HdrMetadata::CTA861_3;
+    }
+    return NO_ERROR;
+}
+
 android_dataspace_t Surface::getBuffersDataSpace() {
     ALOGV("Surface::getBuffersDataSpace");
     Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h
new file mode 100644
index 0000000..2ee011b
--- /dev/null
+++ b/libs/gui/include/gui/BufferHubProducer.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2018 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_GUI_BUFFERHUBPRODUCER_H_
+#define ANDROID_GUI_BUFFERHUBPRODUCER_H_
+
+#include <gui/BufferSlot.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/buffer_hub_queue_parcelable.h>
+
+namespace android {
+
+class BufferHubProducer : public BnGraphicBufferProducer {
+public:
+    static constexpr int kNoConnectedApi = -1;
+
+    // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer
+    // side logic doesn't limit the number of buffer it can acquire
+    // simultaneously. We need a way for consumer logic to configure and enforce
+    // that.
+    static constexpr int kDefaultUndequeuedBuffers = 1;
+
+    // Creates a BufferHubProducer instance by importing an existing prodcuer
+    // queue.
+    static sp<BufferHubProducer> Create(const std::shared_ptr<dvr::ProducerQueue>& producer);
+
+    // Creates a BufferHubProducer instance by importing an existing prodcuer
+    // parcelable. Note that this call takes the ownership of the parcelable
+    // object and is guaranteed to succeed if parcelable object is valid.
+    static sp<BufferHubProducer> Create(dvr::ProducerQueueParcelable parcelable);
+
+    // See |IGraphicBufferProducer::requestBuffer|
+    status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
+
+    // For the BufferHub based implementation. All buffers in the queue are
+    // allowed to be dequeued from the consumer side. It call always returns
+    // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting
+    // |max_dequeued_buffers| here can be considered the same as setting queue
+    // capacity.
+    //
+    // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info
+    status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override;
+
+    // See |IGraphicBufferProducer::setAsyncMode|
+    status_t setAsyncMode(bool async) override;
+
+    // See |IGraphicBufferProducer::dequeueBuffer|
+    status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height,
+                           PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+                           FrameEventHistoryDelta* outTimestamps) override;
+
+    // See |IGraphicBufferProducer::detachBuffer|
+    status_t detachBuffer(int slot) override;
+
+    // See |IGraphicBufferProducer::detachNextBuffer|
+    status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) override;
+
+    // See |IGraphicBufferProducer::attachBuffer|
+    status_t attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) override;
+
+    // See |IGraphicBufferProducer::queueBuffer|
+    status_t queueBuffer(int slot, const QueueBufferInput& input,
+                         QueueBufferOutput* output) override;
+
+    // See |IGraphicBufferProducer::cancelBuffer|
+    status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
+
+    // See |IGraphicBufferProducer::query|
+    status_t query(int what, int* out_value) override;
+
+    // See |IGraphicBufferProducer::connect|
+    status_t connect(const sp<IProducerListener>& listener, int api,
+                     bool producer_controlled_by_app, QueueBufferOutput* output) override;
+
+    // See |IGraphicBufferProducer::disconnect|
+    status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) override;
+
+    // See |IGraphicBufferProducer::setSidebandStream|
+    status_t setSidebandStream(const sp<NativeHandle>& stream) override;
+
+    // See |IGraphicBufferProducer::allocateBuffers|
+    void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
+                         uint64_t usage) override;
+
+    // See |IGraphicBufferProducer::allowAllocation|
+    status_t allowAllocation(bool allow) override;
+
+    // See |IGraphicBufferProducer::setGenerationNumber|
+    status_t setGenerationNumber(uint32_t generation_number) override;
+
+    // See |IGraphicBufferProducer::getConsumerName|
+    String8 getConsumerName() const override;
+
+    // See |IGraphicBufferProducer::setSharedBufferMode|
+    status_t setSharedBufferMode(bool shared_buffer_mode) override;
+
+    // See |IGraphicBufferProducer::setAutoRefresh|
+    status_t setAutoRefresh(bool auto_refresh) override;
+
+    // See |IGraphicBufferProducer::setDequeueTimeout|
+    status_t setDequeueTimeout(nsecs_t timeout) override;
+
+    // See |IGraphicBufferProducer::getLastQueuedBuffer|
+    status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence,
+                                 float out_transform_matrix[16]) override;
+
+    // See |IGraphicBufferProducer::getFrameTimestamps|
+    void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override;
+
+    // See |IGraphicBufferProducer::getUniqueId|
+    status_t getUniqueId(uint64_t* out_id) const override;
+
+    // See |IGraphicBufferProducer::getConsumerUsage|
+    status_t getConsumerUsage(uint64_t* out_usage) const override;
+
+    // Takes out the current producer as a binder parcelable object. Note that the
+    // producer must be disconnected to be exportable. After successful export,
+    // the producer queue can no longer be connected again. Returns NO_ERROR when
+    // takeout is successful and out_parcelable will hold the new parcelable
+    // object. Also note that out_parcelable cannot be NULL and must points to an
+    // invalid parcelable.
+    status_t TakeAsParcelable(dvr::ProducerQueueParcelable* out_parcelable);
+
+private:
+    using LocalHandle = pdx::LocalHandle;
+
+    // Private constructor to force use of |Create|.
+    BufferHubProducer() {}
+
+    static uint64_t genUniqueId() {
+        static std::atomic<uint32_t> counter{0};
+        static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+        return id | counter++;
+    }
+
+    // Allocate new buffer through BufferHub and add it into |queue_| for
+    // bookkeeping.
+    status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
+                            PixelFormat format, uint64_t usage);
+
+    // Remove a buffer via BufferHubRPC.
+    status_t RemoveBuffer(size_t slot);
+
+    // Free all buffers which are owned by the prodcuer. Note that if graphic
+    // buffers are acquired by the consumer, we can't .
+    status_t FreeAllBuffers();
+
+    // Concreate implementation backed by BufferHubBuffer.
+    std::shared_ptr<dvr::ProducerQueue> queue_;
+
+    // Mutex for thread safety.
+    std::mutex mutex_;
+
+    // Connect client API, should be one of the NATIVE_WINDOW_API_* flags.
+    int connected_api_{kNoConnectedApi};
+
+    // |max_buffer_count_| sets the capacity of the underlying buffer queue.
+    int32_t max_buffer_count_{dvr::BufferHubQueue::kMaxQueueCapacity};
+
+    // |max_dequeued_buffer_count_| set the maximum number of buffers that can
+    // be dequeued at the same momment.
+    int32_t max_dequeued_buffer_count_{1};
+
+    // Sets how long dequeueBuffer or attachBuffer will block if a buffer or
+    // slot is not yet available. The timeout is stored in milliseconds.
+    int dequeue_timeout_ms_{dvr::BufferHubQueue::kNoTimeOut};
+
+    // |generation_number_| stores the current generation number of the attached
+    // producer. Any attempt to attach a buffer with a different generation
+    // number will fail.
+    // TOOD(b/38137191) Currently not used as we don't support
+    // IGraphicBufferProducer::detachBuffer.
+    uint32_t generation_number_{0};
+
+    // |buffers_| stores the buffers that have been dequeued from
+    // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets
+    // filled in with the result of |Dequeue|.
+    // TODO(jwcai) The buffer allocated to a slot will also be replaced if the
+    // requested buffer usage or geometry differs from that of the buffer
+    // allocated to a slot.
+    struct BufferHubSlot : public BufferSlot {
+        BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {}
+        // BufferSlot comes from android framework, using m prefix to comply with
+        // the name convention with the reset of data fields from BufferSlot.
+        std::shared_ptr<dvr::BufferProducer> mBufferProducer;
+        bool mIsReallocating;
+    };
+    BufferHubSlot buffers_[dvr::BufferHubQueue::kMaxQueueCapacity];
+
+    // A uniqueId used by IGraphicBufferProducer interface.
+    const uint64_t unique_id_{genUniqueId()};
+};
+
+} // namespace android
+
+#endif // ANDROID_GUI_BUFFERHUBPRODUCER_H_
diff --git a/libs/gui/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h
index 55637a9..7740b9f 100644
--- a/libs/gui/include/gui/BufferItem.h
+++ b/libs/gui/include/gui/BufferItem.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_GUI_BUFFERITEM_H
 #define ANDROID_GUI_BUFFERITEM_H
 
+#include <gui/HdrMetadata.h>
+
 #include <ui/FenceTime.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -86,6 +88,9 @@
     // dataSpace is format-dependent.
     android_dataspace mDataSpace;
 
+    // mHdrMetadata is the HDR metadata associated with this buffer slot.
+    HdrMetadata mHdrMetadata;
+
     // mFrameNumber is the number of the queued frame for this slot.
     uint64_t mFrameNumber;
 
diff --git a/libs/gui/include/gui/HdrMetadata.h b/libs/gui/include/gui/HdrMetadata.h
new file mode 100644
index 0000000..cd01952
--- /dev/null
+++ b/libs/gui/include/gui/HdrMetadata.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <system/graphics.h>
+#include <utils/Flattenable.h>
+
+namespace android {
+
+struct HdrMetadata : public LightFlattenable<HdrMetadata> {
+    enum Type : uint32_t {
+        SMPTE2086 = 1 << 0,
+        CTA861_3  = 1 << 1,
+    };
+    uint32_t validTypes{0};
+
+    android_smpte2086_metadata smpte2086{};
+    android_cta861_3_metadata cta8613{};
+
+    // LightFlattenable
+    bool isFixedSize() const { return false; }
+    size_t getFlattenedSize() const;
+    status_t flatten(void* buffer, size_t size) const;
+    status_t unflatten(void const* buffer, size_t size);
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 039dc0d..722833e 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -31,6 +31,7 @@
 #include <ui/Region.h>
 
 #include <gui/FrameTimestamps.h>
+#include <gui/HdrMetadata.h>
 
 #include <hidl/HybridInterface.h>
 #include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
@@ -354,6 +355,9 @@
         const Region& getSurfaceDamage() const { return surfaceDamage; }
         void setSurfaceDamage(const Region& damage) { surfaceDamage = damage; }
 
+        const HdrMetadata& getHdrMetadata() const { return hdrMetadata; }
+        void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; }
+
     private:
         int64_t timestamp{0};
         int isAutoTimestamp{0};
@@ -365,6 +369,7 @@
         sp<Fence> fence;
         Region surfaceDamage;
         bool getFrameTimestamps{false};
+        HdrMetadata hdrMetadata;
     };
 
     struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 354f23a..641d62c 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -17,8 +17,9 @@
 #ifndef ANDROID_GUI_SURFACE_H
 #define ANDROID_GUI_SURFACE_H
 
-#include <gui/IGraphicBufferProducer.h>
 #include <gui/BufferQueueDefs.h>
+#include <gui/HdrMetadata.h>
+#include <gui/IGraphicBufferProducer.h>
 
 #include <ui/ANativeObjectBase.h>
 #include <ui/Region.h>
@@ -214,6 +215,8 @@
     int dispatchUnlockAndPost(va_list args);
     int dispatchSetSidebandStream(va_list args);
     int dispatchSetBuffersDataSpace(va_list args);
+    int dispatchSetBuffersSmpte2086Metadata(va_list args);
+    int dispatchSetBuffersCta8613Metadata(va_list args);
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSharedBufferMode(va_list args);
     int dispatchSetAutoRefresh(va_list args);
@@ -243,6 +246,8 @@
     virtual int setBuffersStickyTransform(uint32_t transform);
     virtual int setBuffersTimestamp(int64_t timestamp);
     virtual int setBuffersDataSpace(android_dataspace dataSpace);
+    virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata);
+    virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata);
     virtual int setCrop(Rect const* rect);
     virtual int setUsage(uint64_t reqUsage);
     virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
@@ -339,6 +344,10 @@
     // means that the buffer contains some type of color data.
     android_dataspace mDataSpace;
 
+    // mHdrMetadata is the HDR metadata that will be used for the next buffer
+    // queue operation.  There is no HDR metadata by default.
+    HdrMetadata mHdrMetadata;
+
     // mCrop is the crop rectangle that will be used for the next buffer
     // that gets queued. It is set by calling setCrop.
     Rect mCrop;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 470a338..cd29d4a 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -47,6 +47,9 @@
 static bool hasWideColorDisplay =
         getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
 
+static bool hasHdrDisplay =
+        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+
 class FakeSurfaceComposer;
 class FakeProducerFrameEventHistory;
 
@@ -294,6 +297,68 @@
     ASSERT_EQ(hasWideColorDisplay, supported);
 }
 
+TEST_F(SurfaceTest, GetHdrSupport) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+    consumer->consumerConnect(dummyConsumer, false);
+    consumer->setConsumerName(String8("TestConsumer"));
+
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+    native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+
+    bool supported;
+    status_t result = surface->getHdrSupport(&supported);
+    ASSERT_EQ(NO_ERROR, result);
+
+    // NOTE: This is not a CTS test.
+    // This test verifies that when the BoardConfig TARGET_HAS_HDR_DISPLAY
+    // is TRUE, getHdrSupport is also true.
+    // TODO: Add check for an HDR color mode on the primary display.
+    ASSERT_EQ(hasHdrDisplay, supported);
+}
+
+TEST_F(SurfaceTest, SetHdrMetadata) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+    consumer->consumerConnect(dummyConsumer, false);
+    consumer->setConsumerName(String8("TestConsumer"));
+
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+    native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+
+    bool supported;
+    status_t result = surface->getHdrSupport(&supported);
+    ASSERT_EQ(NO_ERROR, result);
+
+    if (!hasHdrDisplay || !supported) {
+        return;
+    }
+    const android_smpte2086_metadata smpte2086 = {
+        {0.680, 0.320},
+        {0.265, 0.690},
+        {0.150, 0.060},
+        {0.3127, 0.3290},
+        100.0,
+        0.1,
+    };
+    const android_cta861_3_metadata cta861_3 = {
+        78.0,
+        62.0,
+    };
+    int error = native_window_set_buffers_smpte2086_metadata(window.get(), &smpte2086);
+    ASSERT_EQ(error, NO_ERROR);
+    error = native_window_set_buffers_cta861_3_metadata(window.get(), &cta861_3);
+    ASSERT_EQ(error, NO_ERROR);
+}
+
 TEST_F(SurfaceTest, DynamicSetBufferCount) {
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index e54f147..57eee12 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -107,7 +107,7 @@
 // this is the strategy that applications will actually use.  Be very careful
 // when adjusting the default strategy because it can dramatically affect
 // (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
+const char* VelocityTracker::DEFAULT_STRATEGY = "impulse";
 
 VelocityTracker::VelocityTracker(const char* strategy) :
         mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
diff --git a/libs/math/tests/mat_test.cpp b/libs/math/tests/mat_test.cpp
index 3217a1a..a14c7ea 100644
--- a/libs/math/tests/mat_test.cpp
+++ b/libs/math/tests/mat_test.cpp
@@ -35,7 +35,7 @@
 
 TEST_F(MatTest, Basics) {
     mat4 m0;
-    EXPECT_EQ(sizeof(mat4), sizeof(float)*16);
+    EXPECT_EQ(sizeof(m0), sizeof(float)*16);
 }
 
 TEST_F(MatTest, ComparisonOps) {
@@ -76,6 +76,7 @@
     EXPECT_EQ(m3, m1);
 
     mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4));
+    EXPECT_NE(m4, m1);
 }
 
 TEST_F(MatTest, ArithmeticOps) {
@@ -172,7 +173,7 @@
 
 TEST_F(Mat3Test, Basics) {
     mat3 m0;
-    EXPECT_EQ(sizeof(mat3), sizeof(float)*9);
+    EXPECT_EQ(sizeof(m0), sizeof(float)*9);
 }
 
 TEST_F(Mat3Test, ComparisonOps) {
@@ -279,7 +280,7 @@
 
 TEST_F(Mat2Test, Basics) {
     mat2 m0;
-    EXPECT_EQ(sizeof(mat2), sizeof(float)*4);
+    EXPECT_EQ(sizeof(m0), sizeof(float)*4);
 }
 
 TEST_F(Mat2Test, ComparisonOps) {
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index aa116bf..29555fd 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -47,6 +47,8 @@
         "-std=c++1z"
     ],
 
+    version_script: "libnativewindow.map.txt",
+
     srcs: [
         "AHardwareBuffer.cpp",
         "ANativeWindow.cpp",
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 6490804..69e0951 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -225,6 +225,8 @@
     NATIVE_WINDOW_GET_HDR_SUPPORT               = 29,
     NATIVE_WINDOW_SET_USAGE64                   = 30,
     NATIVE_WINDOW_GET_CONSUMER_USAGE64          = 31,
+    NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
+    NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
 // clang-format on
 };
 
@@ -700,6 +702,42 @@
 }
 
 /*
+ * native_window_set_buffers_smpte2086_metadata(..., metadata)
+ * All buffers queued after this call will be associated with the SMPTE
+ * ST.2086 metadata specified.
+ *
+ * metadata specifies additional information about the contents of the buffer
+ * that may affect how it's displayed.  When it is nullptr, it means no such
+ * information is available.  No SMPTE ST.2086 metadata is associated with the
+ * buffers by default.
+ */
+static inline int native_window_set_buffers_smpte2086_metadata(
+        struct ANativeWindow* window,
+        const struct android_smpte2086_metadata* metadata)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA,
+            metadata);
+}
+
+/*
+ * native_window_set_buffers_cta861_3_metadata(..., metadata)
+ * All buffers queued after this call will be associated with the CTA-861.3
+ * metadata specified.
+ *
+ * metadata specifies additional information about the contents of the buffer
+ * that may affect how it's displayed.  When it is nullptr, it means no such
+ * information is available.  No CTA-861.3 metadata is associated with the
+ * buffers by default.
+ */
+static inline int native_window_set_buffers_cta861_3_metadata(
+        struct ANativeWindow* window,
+        const struct android_cta861_3_metadata* metadata)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA,
+            metadata);
+}
+
+/*
  * native_window_set_buffers_transform(..., int transform)
  * All buffers queued after this call will be displayed transformed according
  * to the transform parameter specified.
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 58045be..105d01b 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -3,13 +3,11 @@
     AHardwareBuffer_acquire;
     AHardwareBuffer_allocate;
     AHardwareBuffer_describe;
-    AHardwareBuffer_fromHardwareBuffer;
     AHardwareBuffer_getNativeHandle; # vndk
     AHardwareBuffer_lock;
     AHardwareBuffer_recvHandleFromUnixSocket;
     AHardwareBuffer_release;
     AHardwareBuffer_sendHandleToUnixSocket;
-    AHardwareBuffer_toHardwareBuffer;
     AHardwareBuffer_unlock;
     ANativeWindowBuffer_getHardwareBuffer; # vndk
     ANativeWindow_OemStorageGet; # vndk
@@ -17,8 +15,6 @@
     ANativeWindow_acquire;
     ANativeWindow_cancelBuffer; # vndk
     ANativeWindow_dequeueBuffer; # vndk
-    ANativeWindow_fromSurface;
-    ANativeWindow_fromSurfaceTexture;
     ANativeWindow_getFormat;
     ANativeWindow_getHeight;
     ANativeWindow_getWidth;
@@ -42,3 +38,17 @@
   local:
     *;
 };
+
+LIBNATIVEWINDOW_PLATFORM {
+  global:
+    extern "C++" {
+      android::AHardwareBuffer_isValidPixelFormat*;
+      android::AHardwareBuffer_convertFromPixelFormat*;
+      android::AHardwareBuffer_convertToPixelFormat*;
+      android::AHardwareBuffer_convertFromGrallocUsageBits*;
+      android::AHardwareBuffer_convertToGrallocUsageBits*;
+      android::AHardwareBuffer_to_GraphicBuffer*;
+      android::AHardwareBuffer_to_ANativeWindowBuffer*;
+      android::AHardwareBuffer_from_GraphicBuffer*;
+    };
+} LIBNATIVEWINDOW;
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 102964f..f71f814 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -85,6 +85,7 @@
         "libhwbinder",
         "libsync",
         "libutils",
+        "libutilscallstack",
         "liblog",
     ],
 
@@ -95,6 +96,7 @@
     ],
 
     header_libs: [
+        "libbase_headers",
         "libnativebase_headers",
         "libhardware_headers",
     ],
@@ -107,6 +109,7 @@
     ],
 
     export_header_lib_headers: [
+        "libbase_headers",
         "libnativebase_headers",
         "libhardware_headers",
     ],
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index b67f4d9..ff53aa8 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -37,18 +37,12 @@
 
 const sp<Fence> Fence::NO_FENCE = sp<Fence>(new Fence);
 
-Fence::Fence() :
-    mFenceFd(-1) {
-}
-
 Fence::Fence(int fenceFd) :
     mFenceFd(fenceFd) {
 }
 
-Fence::~Fence() {
-    if (mFenceFd != -1) {
-        close(mFenceFd);
-    }
+Fence::Fence(base::unique_fd fenceFd) :
+    mFenceFd(std::move(fenceFd)) {
 }
 
 status_t Fence::wait(int timeout) {
@@ -68,7 +62,7 @@
     int warningTimeout = 3000;
     int err = sync_wait(mFenceFd, warningTimeout);
     if (err < 0 && errno == ETIME) {
-        ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd,
+        ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
                 warningTimeout);
         err = sync_wait(mFenceFd, TIMEOUT_NEVER);
     }
@@ -94,7 +88,7 @@
     if (result == -1) {
         status_t err = -errno;
         ALOGE("merge: sync_merge(\"%s\", %d, %d) returned an error: %s (%d)",
-                name, f1->mFenceFd, f2->mFenceFd,
+                name, f1->mFenceFd.get(), f2->mFenceFd.get(),
                 strerror(-err), err);
         return NO_FENCE;
     }
@@ -117,7 +111,7 @@
 
     struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd);
     if (finfo == NULL) {
-        ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd);
+        ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd.get());
         return SIGNAL_TIME_INVALID;
     }
     if (finfo->status != 1) {
@@ -181,7 +175,7 @@
     }
 
     if (numFds) {
-        mFenceFd = *fds++;
+        mFenceFd.reset(*fds++);
         count--;
     }
 
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 37811bc..ec67fa9 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include <android-base/unique_fd.h>
 #include <utils/Flattenable.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
@@ -49,12 +50,13 @@
     // Construct a new Fence object with an invalid file descriptor.  This
     // should be done when the Fence object will be set up by unflattening
     // serialized data.
-    Fence();
+    Fence() = default;
 
     // Construct a new Fence object to manage a given fence file descriptor.
     // When the new Fence object is destructed the file descriptor will be
     // closed.
     explicit Fence(int fenceFd);
+    explicit Fence(base::unique_fd fenceFd);
 
     // Not copyable or movable.
     Fence(const Fence& rhs) = delete;
@@ -136,9 +138,9 @@
 private:
     // Only allow instantiation using ref counting.
     friend class LightRefBase<Fence>;
-    ~Fence();
+    ~Fence() = default;
 
-    int mFenceFd;
+    base::unique_fd mFenceFd;
 };
 
 }; // namespace android
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 270675c..6a7479a 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -27,6 +27,17 @@
     float getWidth() const { return right - left; }
     float getHeight() const { return bottom - top; }
 
+    FloatRect intersect(const FloatRect& other) const {
+        return {
+            // Inline to avoid tromping on other min/max defines or adding a
+            // dependency on STL
+            (left > other.left) ? left : other.left,
+            (top > other.top) ? top : other.top,
+            (right < other.right) ? right : other.right,
+            (bottom < other.bottom) ? bottom : other.bottom
+        };
+    }
+
     float left = 0.0f;
     float top = 0.0f;
     float right = 0.0f;
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 437fc14..0bec0b7 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -69,6 +69,15 @@
         bottom = rb.y;
     }
 
+    inline explicit Rect(const FloatRect& floatRect) {
+        // Ideally we would use std::round, but we don't want to add an STL
+        // dependency here, so we use an approximation
+        left = static_cast<int32_t>(floatRect.left + 0.5f);
+        top = static_cast<int32_t>(floatRect.top + 0.5f);
+        right = static_cast<int32_t>(floatRect.right + 0.5f);
+        bottom = static_cast<int32_t>(floatRect.bottom + 0.5f);
+    }
+
     void makeInvalid();
 
     inline void clear() {
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 9481c37..7ea37a7 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -23,7 +23,6 @@
 ]
 
 staticLibraries = [
-    "libdvrcommon",
     "libpdx_default_transport",
 ]
 
@@ -60,6 +59,10 @@
     export_header_lib_headers: [
         "libnativebase_headers",
     ],
+    vendor_available: false,
+    vndk: {
+        enabled: true,
+    },
 }
 
 cc_test {
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
index f9fd42d..f105b02 100644
--- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -2,8 +2,8 @@
 #define ANDROID_DVR_BUFFERHUB_RPC_H_
 
 #include <cutils/native_handle.h>
-#include <gui/BufferQueueDefs.h>
 #include <sys/types.h>
+#include <ui/BufferQueueDefs.h>
 
 #include <dvr/dvr_api.h>
 #include <pdx/channel_handle.h>
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index 8241809..84e7427 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -15,7 +15,6 @@
 sourceFiles = [
     "buffer_hub_queue_client.cpp",
     "buffer_hub_queue_parcelable.cpp",
-    "buffer_hub_queue_producer.cpp",
 ]
 
 includeFiles = [
@@ -24,7 +23,6 @@
 
 staticLibraries = [
     "libbufferhub",
-    "libdvrcommon",
     "libpdx_default_transport",
 ]
 
@@ -36,7 +34,6 @@
     "liblog",
     "libui",
     "libutils",
-    "libgui",
 ]
 
 headerLibraries = [
@@ -62,6 +59,10 @@
     static_libs: staticLibraries,
     shared_libs: sharedLibraries,
     header_libs: headerLibraries,
+    vendor_available: false,
+    vndk: {
+        enabled: true,
+    },
 }
 
 subdirs = ["tests"]
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
deleted file mode 100644
index ace01a6..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
+++ /dev/null
@@ -1,729 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_producer.h"
-
-#include <dvr/dvr_api.h>
-#include <inttypes.h>
-#include <log/log.h>
-#include <system/window.h>
-
-namespace android {
-namespace dvr {
-
-/* static */
-sp<BufferHubQueueProducer> BufferHubQueueProducer::Create(
-    const std::shared_ptr<ProducerQueue>& queue) {
-  if (queue->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
-    ALOGE(
-        "BufferHubQueueProducer::Create producer's metadata size is different "
-        "than the size of DvrNativeBufferMetadata");
-    return nullptr;
-  }
-
-  sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer;
-  producer->queue_ = queue;
-  return producer;
-}
-
-/* static */
-sp<BufferHubQueueProducer> BufferHubQueueProducer::Create(
-    ProducerQueueParcelable parcelable) {
-  if (!parcelable.IsValid()) {
-    ALOGE("BufferHubQueueProducer::Create: Invalid producer parcelable.");
-    return nullptr;
-  }
-
-  sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer;
-  producer->queue_ = ProducerQueue::Import(parcelable.TakeChannelHandle());
-  return producer;
-}
-
-status_t BufferHubQueueProducer::requestBuffer(int slot,
-                                               sp<GraphicBuffer>* buf) {
-  ALOGD_IF(TRACE, "requestBuffer: slot=%d", slot);
-
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  if (connected_api_ == kNoConnectedApi) {
-    ALOGE("requestBuffer: BufferHubQueueProducer has no connected producer");
-    return NO_INIT;
-  }
-
-  if (slot < 0 || slot >= max_buffer_count_) {
-    ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot,
-          max_buffer_count_);
-    return BAD_VALUE;
-  } else if (!buffers_[slot].mBufferState.isDequeued()) {
-    ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)",
-          slot, buffers_[slot].mBufferState.string());
-    return BAD_VALUE;
-  } else if (buffers_[slot].mGraphicBuffer != nullptr) {
-    ALOGE("requestBuffer: slot %d is not empty.", slot);
-    return BAD_VALUE;
-  } else if (buffers_[slot].mBufferProducer == nullptr) {
-    ALOGE("requestBuffer: slot %d is not dequeued.", slot);
-    return BAD_VALUE;
-  }
-
-  const auto& buffer_producer = buffers_[slot].mBufferProducer;
-  sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer();
-
-  buffers_[slot].mGraphicBuffer = graphic_buffer;
-  buffers_[slot].mRequestBufferCalled = true;
-
-  *buf = graphic_buffer;
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::setMaxDequeuedBufferCount(
-    int max_dequeued_buffers) {
-  ALOGD_IF(TRACE, "setMaxDequeuedBufferCount: max_dequeued_buffers=%d",
-           max_dequeued_buffers);
-
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  if (max_dequeued_buffers <= 0 ||
-      max_dequeued_buffers >
-          static_cast<int>(BufferHubQueue::kMaxQueueCapacity -
-                           kDefaultUndequeuedBuffers)) {
-    ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]",
-          max_dequeued_buffers, BufferHubQueue::kMaxQueueCapacity);
-    return BAD_VALUE;
-  }
-
-  // The new dequeued_buffers count should not be violated by the number
-  // of currently dequeued buffers.
-  int dequeued_count = 0;
-  for (const auto& buf : buffers_) {
-    if (buf.mBufferState.isDequeued()) {
-      dequeued_count++;
-    }
-  }
-  if (dequeued_count > max_dequeued_buffers) {
-    ALOGE(
-        "setMaxDequeuedBufferCount: the requested dequeued_buffers"
-        "count (%d) exceeds the current dequeued buffer count (%d)",
-        max_dequeued_buffers, dequeued_count);
-    return BAD_VALUE;
-  }
-
-  max_dequeued_buffer_count_ = max_dequeued_buffers;
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::setAsyncMode(bool async) {
-  if (async) {
-    // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer
-    // automatically and behaves differently from IGraphicBufferConsumer. Thus,
-    // android::BufferQueue's async mode (a.k.a. allocating an additional buffer
-    // to prevent dequeueBuffer from being blocking) technically does not apply
-    // here.
-    //
-    // In Daydream, non-blocking producer side dequeue is guaranteed by careful
-    // buffer consumer implementations. In another word, BufferHubQueue based
-    // dequeueBuffer should never block whether setAsyncMode(true) is set or
-    // not.
-    //
-    // See: IGraphicBufferProducer::setAsyncMode and
-    // BufferQueueProducer::setAsyncMode for more about original implementation.
-    ALOGW(
-        "BufferHubQueueProducer::setAsyncMode: BufferHubQueue should always be "
-        "asynchronous. This call makes no effact.");
-    return NO_ERROR;
-  }
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::dequeueBuffer(
-    int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height,
-    PixelFormat format, uint64_t usage, uint64_t* /*outBufferAge*/,
-    FrameEventHistoryDelta* /* out_timestamps */) {
-  ALOGD_IF(TRACE, "dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width,
-           height, format, usage);
-
-  status_t ret;
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  if (connected_api_ == kNoConnectedApi) {
-    ALOGE("dequeueBuffer: BufferQueue has no connected producer");
-    return NO_INIT;
-  }
-
-  const uint32_t kLayerCount = 1;
-  if (static_cast<int32_t>(queue_->capacity()) <
-      max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) {
-    // Lazy allocation. When the capacity of |queue_| has not reached
-    // |max_dequeued_buffer_count_|, allocate new buffer.
-    // TODO(jwcai) To save memory, the really reasonable thing to do is to go
-    // over existing slots and find first existing one to dequeue.
-    ret = AllocateBuffer(width, height, kLayerCount, format, usage);
-    if (ret < 0)
-      return ret;
-  }
-
-  size_t slot;
-  std::shared_ptr<BufferProducer> buffer_producer;
-
-  for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
-    LocalHandle fence;
-    auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
-    if (!buffer_status)
-      return NO_MEMORY;
-
-    buffer_producer = buffer_status.take();
-    if (!buffer_producer)
-      return NO_MEMORY;
-
-    if (width == buffer_producer->width() &&
-        height == buffer_producer->height() &&
-        static_cast<uint32_t>(format) == buffer_producer->format()) {
-      // The producer queue returns a buffer producer matches the request.
-      break;
-    }
-
-    // Needs reallocation.
-    // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
-    ALOGI(
-        "dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
-        "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
-        "re-allocattion.",
-        width, height, format, slot, buffer_producer->width(),
-        buffer_producer->height(), buffer_producer->format());
-    // Mark the slot as reallocating, so that later we can set
-    // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
-    buffers_[slot].mIsReallocating = true;
-
-    // Remove the old buffer once the allocation before allocating its
-    // replacement.
-    RemoveBuffer(slot);
-
-    // Allocate a new producer buffer with new buffer configs. Note that if
-    // there are already multiple buffers in the queue, the next one returned
-    // from |queue_->Dequeue| may not be the new buffer we just reallocated.
-    // Retry up to BufferHubQueue::kMaxQueueCapacity times.
-    ret = AllocateBuffer(width, height, kLayerCount, format, usage);
-    if (ret < 0)
-      return ret;
-  }
-
-  // With the BufferHub backed solution. Buffer slot returned from
-  // |queue_->Dequeue| is guaranteed to avaiable for producer's use.
-  // It's either in free state (if the buffer has never been used before) or
-  // in queued state (if the buffer has been dequeued and queued back to
-  // BufferHubQueue).
-  LOG_ALWAYS_FATAL_IF(
-      (!buffers_[slot].mBufferState.isFree() &&
-       !buffers_[slot].mBufferState.isQueued()),
-      "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot,
-      buffers_[slot].mBufferState.string());
-
-  buffers_[slot].mBufferState.freeQueued();
-  buffers_[slot].mBufferState.dequeue();
-  ALOGD_IF(TRACE, "dequeueBuffer: slot=%zu", slot);
-
-  // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
-  // just need to exopose that through |BufferHubQueue| once we need fence.
-  *out_fence = Fence::NO_FENCE;
-  *out_slot = slot;
-  ret = NO_ERROR;
-
-  if (buffers_[slot].mIsReallocating) {
-    ret |= BUFFER_NEEDS_REALLOCATION;
-    buffers_[slot].mIsReallocating = false;
-  }
-
-  return ret;
-}
-
-status_t BufferHubQueueProducer::detachBuffer(int /* slot */) {
-  ALOGE("BufferHubQueueProducer::detachBuffer not implemented.");
-  return INVALID_OPERATION;
-}
-
-status_t BufferHubQueueProducer::detachNextBuffer(
-    sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */) {
-  ALOGE("BufferHubQueueProducer::detachNextBuffer not implemented.");
-  return INVALID_OPERATION;
-}
-
-status_t BufferHubQueueProducer::attachBuffer(
-    int* /* out_slot */, const sp<GraphicBuffer>& /* buffer */) {
-  // With this BufferHub backed implementation, we assume (for now) all buffers
-  // are allocated and owned by the BufferHub. Thus the attempt of transfering
-  // ownership of a buffer to the buffer queue is intentionally unsupported.
-  LOG_ALWAYS_FATAL("BufferHubQueueProducer::attachBuffer not supported.");
-  return INVALID_OPERATION;
-}
-
-status_t BufferHubQueueProducer::queueBuffer(int slot,
-                                             const QueueBufferInput& input,
-                                             QueueBufferOutput* output) {
-  ALOGD_IF(TRACE, "queueBuffer: slot %d", slot);
-
-  if (output == nullptr) {
-    return BAD_VALUE;
-  }
-
-  int64_t timestamp;
-  bool is_auto_timestamp;
-  android_dataspace dataspace;
-  Rect crop(Rect::EMPTY_RECT);
-  int scaling_mode;
-  uint32_t transform;
-  sp<Fence> fence;
-
-  input.deflate(&timestamp, &is_auto_timestamp, &dataspace, &crop,
-                &scaling_mode, &transform, &fence);
-
-  // Check input scaling mode is valid.
-  switch (scaling_mode) {
-    case NATIVE_WINDOW_SCALING_MODE_FREEZE:
-    case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
-    case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
-    case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
-      break;
-    default:
-      ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode);
-      return BAD_VALUE;
-  }
-
-  // Check input fence is valid.
-  if (fence == nullptr) {
-    ALOGE("queueBuffer: fence is NULL");
-    return BAD_VALUE;
-  }
-
-  status_t ret;
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  if (connected_api_ == kNoConnectedApi) {
-    ALOGE("queueBuffer: BufferQueue has no connected producer");
-    return NO_INIT;
-  }
-
-  if (slot < 0 || slot >= max_buffer_count_) {
-    ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot,
-          max_buffer_count_);
-    return BAD_VALUE;
-  } else if (!buffers_[slot].mBufferState.isDequeued()) {
-    ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)",
-          slot, buffers_[slot].mBufferState.string());
-    return BAD_VALUE;
-  } else if ((!buffers_[slot].mRequestBufferCalled ||
-              buffers_[slot].mGraphicBuffer == nullptr)) {
-    ALOGE(
-        "queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, "
-        "mGraphicBuffer=%p)",
-        slot, buffers_[slot].mRequestBufferCalled,
-        buffers_[slot].mGraphicBuffer.get());
-    return BAD_VALUE;
-  }
-
-  // Post the buffer producer with timestamp in the metadata.
-  const auto& buffer_producer = buffers_[slot].mBufferProducer;
-
-  // Check input crop is not out of boundary of current buffer.
-  Rect buffer_rect(buffer_producer->width(), buffer_producer->height());
-  Rect cropped_rect(Rect::EMPTY_RECT);
-  crop.intersect(buffer_rect, &cropped_rect);
-  if (cropped_rect != crop) {
-    ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot);
-    return BAD_VALUE;
-  }
-
-  LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
-
-  DvrNativeBufferMetadata meta_data;
-  meta_data.timestamp = timestamp;
-  meta_data.is_auto_timestamp = static_cast<int32_t>(is_auto_timestamp);
-  meta_data.dataspace = static_cast<int32_t>(dataspace);
-  meta_data.crop_left = crop.left;
-  meta_data.crop_top = crop.top;
-  meta_data.crop_right = crop.right;
-  meta_data.crop_bottom = crop.bottom;
-  meta_data.scaling_mode = static_cast<int32_t>(scaling_mode);
-  meta_data.transform = static_cast<int32_t>(transform);
-
-  buffer_producer->PostAsync(&meta_data, fence_fd);
-  buffers_[slot].mBufferState.queue();
-
-  output->width = buffer_producer->width();
-  output->height = buffer_producer->height();
-  output->transformHint = 0;  // default value, we don't use it yet.
-
-  // |numPendingBuffers| counts of the number of buffers that has been enqueued
-  // by the producer but not yet acquired by the consumer. Due to the nature
-  // of BufferHubQueue design, this is hard to trace from the producer's client
-  // side, but it's safe to assume it's zero.
-  output->numPendingBuffers = 0;
-
-  // Note that we are not setting nextFrameNumber here as it seems to be only
-  // used by surface flinger. See more at b/22802885, ag/791760.
-  output->nextFrameNumber = 0;
-
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::cancelBuffer(int slot,
-                                              const sp<Fence>& fence) {
-  ALOGD_IF(TRACE, __FUNCTION__);
-
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  if (connected_api_ == kNoConnectedApi) {
-    ALOGE("cancelBuffer: BufferQueue has no connected producer");
-    return NO_INIT;
-  }
-
-  if (slot < 0 || slot >= max_buffer_count_) {
-    ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
-          max_buffer_count_);
-    return BAD_VALUE;
-  } else if (!buffers_[slot].mBufferState.isDequeued()) {
-    ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)",
-          slot, buffers_[slot].mBufferState.string());
-    return BAD_VALUE;
-  } else if (fence == nullptr) {
-    ALOGE("cancelBuffer: fence is NULL");
-    return BAD_VALUE;
-  }
-
-  auto buffer_producer = buffers_[slot].mBufferProducer;
-  queue_->Enqueue(buffer_producer, slot, 0ULL);
-  buffers_[slot].mBufferState.cancel();
-  buffers_[slot].mFence = fence;
-  ALOGD_IF(TRACE, "cancelBuffer: slot %d", slot);
-
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::query(int what, int* out_value) {
-  ALOGD_IF(TRACE, __FUNCTION__);
-
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  if (out_value == nullptr) {
-    ALOGE("query: out_value was NULL");
-    return BAD_VALUE;
-  }
-
-  int value = 0;
-  switch (what) {
-    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
-      // TODO(b/36187402) This should be the maximum number of buffers that this
-      // producer queue's consumer can acquire. Set to be at least one. Need to
-      // find a way to set from the consumer side.
-      value = kDefaultUndequeuedBuffers;
-      break;
-    case NATIVE_WINDOW_BUFFER_AGE:
-      value = 0;
-      break;
-    case NATIVE_WINDOW_WIDTH:
-      value = queue_->default_width();
-      break;
-    case NATIVE_WINDOW_HEIGHT:
-      value = queue_->default_height();
-      break;
-    case NATIVE_WINDOW_FORMAT:
-      value = queue_->default_format();
-      break;
-    case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
-      // BufferHubQueue is always operating in async mode, thus semantically
-      // consumer can never be running behind. See BufferQueueCore.cpp core
-      // for more information about the original meaning of this flag.
-      value = 0;
-      break;
-    case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
-      // TODO(jwcai) This is currently not implement as we don't need
-      // IGraphicBufferConsumer parity.
-      value = 0;
-      break;
-    case NATIVE_WINDOW_DEFAULT_DATASPACE:
-      // TODO(jwcai) Return the default value android::BufferQueue is using as
-      // there is no way dvr::ConsumerQueue can set it.
-      value = 0;  // HAL_DATASPACE_UNKNOWN
-      break;
-    case NATIVE_WINDOW_STICKY_TRANSFORM:
-      // TODO(jwcai) Return the default value android::BufferQueue is using as
-      // there is no way dvr::ConsumerQueue can set it.
-      value = 0;
-      break;
-    case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
-      // In Daydream's implementation, the consumer end (i.e. VR Compostior)
-      // knows how to handle protected buffers.
-      value = 1;
-      break;
-    default:
-      return BAD_VALUE;
-  }
-
-  ALOGD_IF(TRACE, "query: key=%d, v=%d", what, value);
-  *out_value = value;
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::connect(
-    const sp<IProducerListener>& /* listener */, int api,
-    bool /* producer_controlled_by_app */, QueueBufferOutput* output) {
-  // Consumer interaction are actually handled by buffer hub, and we need
-  // to maintain consumer operations here. We only need to perform basic input
-  // parameter checks here.
-  ALOGD_IF(TRACE, __FUNCTION__);
-
-  if (output == nullptr) {
-    return BAD_VALUE;
-  }
-
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  if (connected_api_ != kNoConnectedApi) {
-    return BAD_VALUE;
-  }
-
-  if (!queue_->is_connected()) {
-    ALOGE(
-        "BufferHubQueueProducer::connect: This BufferHubQueueProducer is not "
-        "connected to bufferhud. Has it been taken out as a parcelable?");
-    return BAD_VALUE;
-  }
-
-  switch (api) {
-    case NATIVE_WINDOW_API_EGL:
-    case NATIVE_WINDOW_API_CPU:
-    case NATIVE_WINDOW_API_MEDIA:
-    case NATIVE_WINDOW_API_CAMERA:
-      connected_api_ = api;
-
-      output->width = queue_->default_width();
-      output->height = queue_->default_height();
-
-      // default values, we don't use them yet.
-      output->transformHint = 0;
-      output->numPendingBuffers = 0;
-      output->nextFrameNumber = 0;
-      output->bufferReplaced = false;
-
-      break;
-    default:
-      ALOGE("BufferHubQueueProducer::connect: unknow API %d", api);
-      return BAD_VALUE;
-  }
-
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::disconnect(int api, DisconnectMode /*mode*/) {
-  // Consumer interaction are actually handled by buffer hub, and we need
-  // to maintain consumer operations here.  We only need to perform basic input
-  // parameter checks here.
-  ALOGD_IF(TRACE, __FUNCTION__);
-
-  std::unique_lock<std::mutex> lock(mutex_);
-
-  if (kNoConnectedApi == connected_api_) {
-    return NO_INIT;
-  } else if (api != connected_api_) {
-    return BAD_VALUE;
-  }
-
-  FreeAllBuffers();
-  connected_api_ = kNoConnectedApi;
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::setSidebandStream(
-    const sp<NativeHandle>& stream) {
-  if (stream != nullptr) {
-    // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
-    // metadata.
-    ALOGE("SidebandStream is not currently supported.");
-    return INVALID_OPERATION;
-  }
-  return NO_ERROR;
-}
-
-void BufferHubQueueProducer::allocateBuffers(uint32_t /* width */,
-                                             uint32_t /* height */,
-                                             PixelFormat /* format */,
-                                             uint64_t /* usage */) {
-  // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
-  // of buffers permitted by the current BufferQueue configuration (aka
-  // |max_buffer_count_|).
-  ALOGE("BufferHubQueueProducer::allocateBuffers not implemented.");
-}
-
-status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) {
-  ALOGE("BufferHubQueueProducer::allowAllocation not implemented.");
-  return INVALID_OPERATION;
-}
-
-status_t BufferHubQueueProducer::setGenerationNumber(
-    uint32_t generation_number) {
-  ALOGD_IF(TRACE, __FUNCTION__);
-
-  std::unique_lock<std::mutex> lock(mutex_);
-  generation_number_ = generation_number;
-  return NO_ERROR;
-}
-
-String8 BufferHubQueueProducer::getConsumerName() const {
-  // BufferHub based implementation could have one to many producer/consumer
-  // relationship, thus |getConsumerName| from the producer side does not
-  // make any sense.
-  ALOGE("BufferHubQueueProducer::getConsumerName not supported.");
-  return String8("BufferHubQueue::DummyConsumer");
-}
-
-status_t BufferHubQueueProducer::setSharedBufferMode(bool shared_buffer_mode) {
-  if (shared_buffer_mode) {
-    ALOGE(
-        "BufferHubQueueProducer::setSharedBufferMode(true) is not supported.");
-    // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow.
-    return INVALID_OPERATION;
-  }
-  // Setting to default should just work as a no-op.
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::setAutoRefresh(bool auto_refresh) {
-  if (auto_refresh) {
-    ALOGE("BufferHubQueueProducer::setAutoRefresh(true) is not supported.");
-    return INVALID_OPERATION;
-  }
-  // Setting to default should just work as a no-op.
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) {
-  ALOGD_IF(TRACE, __FUNCTION__);
-
-  std::unique_lock<std::mutex> lock(mutex_);
-  dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::getLastQueuedBuffer(
-    sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */,
-    float /*out_transform_matrix*/[16]) {
-  ALOGE("BufferHubQueueProducer::getLastQueuedBuffer not implemented.");
-  return INVALID_OPERATION;
-}
-
-void BufferHubQueueProducer::getFrameTimestamps(
-    FrameEventHistoryDelta* /*outDelta*/) {
-  ALOGE("BufferHubQueueProducer::getFrameTimestamps not implemented.");
-}
-
-status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const {
-  ALOGD_IF(TRACE, __FUNCTION__);
-
-  *out_id = unique_id_;
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::getConsumerUsage(uint64_t* out_usage) const {
-  ALOGD_IF(TRACE, __FUNCTION__);
-
-  // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS
-  *out_usage = 0;
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::TakeAsParcelable(
-    ProducerQueueParcelable* out_parcelable) {
-  if (!out_parcelable || out_parcelable->IsValid())
-    return BAD_VALUE;
-
-  if (connected_api_ != kNoConnectedApi) {
-    ALOGE(
-        "BufferHubQueueProducer::TakeAsParcelable: BufferHubQueueProducer has "
-        "connected client. Must disconnect first.");
-    return BAD_VALUE;
-  }
-
-  if (!queue_->is_connected()) {
-    ALOGE(
-        "BufferHubQueueProducer::TakeAsParcelable: This BufferHubQueueProducer "
-        "is not connected to bufferhud. Has it been taken out as a "
-        "parcelable?");
-    return BAD_VALUE;
-  }
-
-  auto status = queue_->TakeAsParcelable();
-  if (!status) {
-    ALOGE(
-        "BufferHubQueueProducer::TakeAsParcelable: Failed to take out "
-        "ProducuerQueueParcelable from the producer queue, error: %s.",
-        status.GetErrorMessage().c_str());
-    return BAD_VALUE;
-  }
-
-  *out_parcelable = status.take();
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::AllocateBuffer(uint32_t width, uint32_t height,
-                                                uint32_t layer_count,
-                                                PixelFormat format,
-                                                uint64_t usage) {
-  auto status =
-      queue_->AllocateBuffer(width, height, layer_count, format, usage);
-  if (!status) {
-    ALOGE(
-        "BufferHubQueueProducer::AllocateBuffer: Failed to allocate buffer: %s",
-        status.GetErrorMessage().c_str());
-    return NO_MEMORY;
-  }
-
-  size_t slot = status.get();
-  auto buffer_producer = queue_->GetBuffer(slot);
-
-  LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr,
-                      "Failed to get buffer producer at slot: %zu", slot);
-
-  buffers_[slot].mBufferProducer = buffer_producer;
-
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::RemoveBuffer(size_t slot) {
-  auto status = queue_->RemoveBuffer(slot);
-  if (!status) {
-    ALOGE("BufferHubQueueProducer::RemoveBuffer: Failed to remove buffer: %s",
-          status.GetErrorMessage().c_str());
-    return INVALID_OPERATION;
-  }
-
-  // Reset in memory objects related the the buffer.
-  buffers_[slot].mBufferProducer = nullptr;
-  buffers_[slot].mGraphicBuffer = nullptr;
-  buffers_[slot].mBufferState.detachProducer();
-  return NO_ERROR;
-}
-
-status_t BufferHubQueueProducer::FreeAllBuffers() {
-  for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
-    // Reset in memory objects related the the buffer.
-    buffers_[slot].mGraphicBuffer = nullptr;
-    buffers_[slot].mBufferState.reset();
-    buffers_[slot].mRequestBufferCalled = false;
-    buffers_[slot].mBufferProducer = nullptr;
-    buffers_[slot].mFence = Fence::NO_FENCE;
-  }
-
-  auto status = queue_->FreeAllBuffers();
-  if (!status) {
-    ALOGE(
-        "BufferHubQueueProducer::FreeAllBuffers: Failed to free all buffers on "
-        "the queue: %s",
-        status.GetErrorMessage().c_str());
-  }
-
-  if (queue_->capacity() != 0 || queue_->count() != 0) {
-    LOG_ALWAYS_FATAL(
-        "BufferHubQueueProducer::FreeAllBuffers: Not all buffers are freed.");
-  }
-
-  return NO_ERROR;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 5b320a4..8965530 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -1,7 +1,7 @@
 #ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
 #define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
 
-#include <gui/BufferQueueDefs.h>
+#include <ui/BufferQueueDefs.h>
 
 #include <pdx/client.h>
 #include <pdx/status.h>
@@ -9,7 +9,6 @@
 #include <private/dvr/buffer_hub_queue_parcelable.h>
 #include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/epoll_file_descriptor.h>
-#include <private/dvr/ring_buffer.h>
 
 #include <memory>
 #include <queue>
@@ -157,7 +156,7 @@
                                       int poll_events);
   pdx::Status<void> HandleQueueEvent(int poll_events);
 
-  // Entry in the ring buffer of available buffers that stores related
+  // Entry in the priority queue of available buffers that stores related
   // per-buffer data.
   struct Entry {
     Entry() : slot(0) {}
@@ -242,7 +241,6 @@
   std::array<std::shared_ptr<BufferHubBuffer>, kMaxQueueCapacity> buffers_;
 
   // Buffers and related data that are available for dequeue.
-  // RingBuffer<Entry> available_buffers_{kMaxQueueCapacity};
   std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
       available_buffers_;
 
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
deleted file mode 100644
index 9c85048..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
+++ /dev/null
@@ -1,202 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
-
-#include <gui/IGraphicBufferProducer.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-
-namespace android {
-namespace dvr {
-
-class BufferHubQueueProducer : public BnGraphicBufferProducer {
- public:
-  static constexpr int kNoConnectedApi = -1;
-
-  // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer
-  // side logic doesn't limit the number of buffer it can acquire
-  // simultaneously. We need a way for consumer logic to configure and enforce
-  // that.
-  static constexpr int kDefaultUndequeuedBuffers = 1;
-
-  // Creates a BufferHubQueueProducer instance by importing an existing prodcuer
-  // queue.
-  static sp<BufferHubQueueProducer> Create(
-      const std::shared_ptr<ProducerQueue>& producer);
-
-  // Creates a BufferHubQueueProducer instance by importing an existing prodcuer
-  // parcelable. Note that this call takes the ownership of the parcelable
-  // object and is guaranteed to succeed if parcelable object is valid.
-  static sp<BufferHubQueueProducer> Create(ProducerQueueParcelable parcelable);
-
-  // See |IGraphicBufferProducer::requestBuffer|
-  status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
-
-  // For the BufferHub based implementation. All buffers in the queue are
-  // allowed to be dequeued from the consumer side. It call always returns
-  // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting
-  // |max_dequeued_buffers| here can be considered the same as setting queue
-  // capacity.
-  //
-  // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info
-  status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override;
-
-  // See |IGraphicBufferProducer::setAsyncMode|
-  status_t setAsyncMode(bool async) override;
-
-  // See |IGraphicBufferProducer::dequeueBuffer|
-  status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
-                         uint32_t height, PixelFormat format, uint64_t usage,
-                         uint64_t* outBufferAge,
-                         FrameEventHistoryDelta* outTimestamps) override;
-
-  // See |IGraphicBufferProducer::detachBuffer|
-  status_t detachBuffer(int slot) override;
-
-  // See |IGraphicBufferProducer::detachNextBuffer|
-  status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer,
-                            sp<Fence>* out_fence) override;
-
-  // See |IGraphicBufferProducer::attachBuffer|
-  status_t attachBuffer(int* out_slot,
-                        const sp<GraphicBuffer>& buffer) override;
-
-  // See |IGraphicBufferProducer::queueBuffer|
-  status_t queueBuffer(int slot, const QueueBufferInput& input,
-                       QueueBufferOutput* output) override;
-
-  // See |IGraphicBufferProducer::cancelBuffer|
-  status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
-
-  // See |IGraphicBufferProducer::query|
-  status_t query(int what, int* out_value) override;
-
-  // See |IGraphicBufferProducer::connect|
-  status_t connect(const sp<IProducerListener>& listener, int api,
-                   bool producer_controlled_by_app,
-                   QueueBufferOutput* output) override;
-
-  // See |IGraphicBufferProducer::disconnect|
-  status_t disconnect(int api,
-                      DisconnectMode mode = DisconnectMode::Api) override;
-
-  // See |IGraphicBufferProducer::setSidebandStream|
-  status_t setSidebandStream(const sp<NativeHandle>& stream) override;
-
-  // See |IGraphicBufferProducer::allocateBuffers|
-  void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
-                       uint64_t usage) override;
-
-  // See |IGraphicBufferProducer::allowAllocation|
-  status_t allowAllocation(bool allow) override;
-
-  // See |IGraphicBufferProducer::setGenerationNumber|
-  status_t setGenerationNumber(uint32_t generation_number) override;
-
-  // See |IGraphicBufferProducer::getConsumerName|
-  String8 getConsumerName() const override;
-
-  // See |IGraphicBufferProducer::setSharedBufferMode|
-  status_t setSharedBufferMode(bool shared_buffer_mode) override;
-
-  // See |IGraphicBufferProducer::setAutoRefresh|
-  status_t setAutoRefresh(bool auto_refresh) override;
-
-  // See |IGraphicBufferProducer::setDequeueTimeout|
-  status_t setDequeueTimeout(nsecs_t timeout) override;
-
-  // See |IGraphicBufferProducer::getLastQueuedBuffer|
-  status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer,
-                               sp<Fence>* out_fence,
-                               float out_transform_matrix[16]) override;
-
-  // See |IGraphicBufferProducer::getFrameTimestamps|
-  void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override;
-
-  // See |IGraphicBufferProducer::getUniqueId|
-  status_t getUniqueId(uint64_t* out_id) const override;
-
-  // See |IGraphicBufferProducer::getConsumerUsage|
-  status_t getConsumerUsage(uint64_t* out_usage) const override;
-
-  // Takes out the current producer as a binder parcelable object. Note that the
-  // producer must be disconnected to be exportable. After successful export,
-  // the producer queue can no longer be connected again. Returns NO_ERROR when
-  // takeout is successful and out_parcelable will hold the new parcelable
-  // object. Also note that out_parcelable cannot be NULL and must points to an
-  // invalid parcelable.
-  status_t TakeAsParcelable(ProducerQueueParcelable* out_parcelable);
-
- private:
-  using LocalHandle = pdx::LocalHandle;
-
-  // Private constructor to force use of |Create|.
-  BufferHubQueueProducer() {}
-
-  static uint64_t genUniqueId() {
-    static std::atomic<uint32_t> counter{0};
-    static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
-    return id | counter++;
-  }
-
-  // Allocate new buffer through BufferHub and add it into |queue_| for
-  // bookkeeping.
-  status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
-                          PixelFormat format, uint64_t usage);
-
-  // Remove a buffer via BufferHubRPC.
-  status_t RemoveBuffer(size_t slot);
-
-  // Free all buffers which are owned by the prodcuer. Note that if graphic
-  // buffers are acquired by the consumer, we can't .
-  status_t FreeAllBuffers();
-
-  // Concreate implementation backed by BufferHubBuffer.
-  std::shared_ptr<ProducerQueue> queue_;
-
-  // Mutex for thread safety.
-  std::mutex mutex_;
-
-  // Connect client API, should be one of the NATIVE_WINDOW_API_* flags.
-  int connected_api_{kNoConnectedApi};
-
-  // |max_buffer_count_| sets the capacity of the underlying buffer queue.
-  int32_t max_buffer_count_{BufferHubQueue::kMaxQueueCapacity};
-
-  // |max_dequeued_buffer_count_| set the maximum number of buffers that can
-  // be dequeued at the same momment.
-  int32_t max_dequeued_buffer_count_{1};
-
-  // Sets how long dequeueBuffer or attachBuffer will block if a buffer or
-  // slot is not yet available. The timeout is stored in milliseconds.
-  int dequeue_timeout_ms_{BufferHubQueue::kNoTimeOut};
-
-  // |generation_number_| stores the current generation number of the attached
-  // producer. Any attempt to attach a buffer with a different generation
-  // number will fail.
-  // TOOD(b/38137191) Currently not used as we don't support
-  // IGraphicBufferProducer::detachBuffer.
-  uint32_t generation_number_{0};
-
-  // |buffers_| stores the buffers that have been dequeued from
-  // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets
-  // filled in with the result of |Dequeue|.
-  // TODO(jwcai) The buffer allocated to a slot will also be replaced if the
-  // requested buffer usage or geometry differs from that of the buffer
-  // allocated to a slot.
-  struct BufferHubSlot : public BufferSlot {
-    BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {}
-    // BufferSlot comes from android framework, using m prefix to comply with
-    // the name convention with the reset of data fields from BufferSlot.
-    std::shared_ptr<BufferProducer> mBufferProducer;
-    bool mIsReallocating;
-  };
-  BufferHubSlot buffers_[BufferHubQueue::kMaxQueueCapacity];
-
-  // A uniqueId used by IGraphicBufferProducer interface.
-  const uint64_t unique_id_{genUniqueId()};
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
similarity index 84%
rename from libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
rename to libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
index 099a409..6e303a5 100644
--- a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
@@ -1,5 +1,5 @@
-#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
-#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+#ifndef ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
+#define ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
 
 #include <android-base/unique_fd.h>
 #include <log/log.h>
@@ -61,4 +61,4 @@
 }  // namespace dvr
 }  // namespace android
 
-#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+#endif  // ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index 96f5404..3e96989 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -1,6 +1,5 @@
-#include <private/dvr/buffer_hub_queue_producer.h>
-
 #include <base/logging.h>
+#include <gui/BufferHubProducer.h>
 #include <gui/IProducerListener.h>
 #include <gui/Surface.h>
 #include <pdx/default_transport/channel_parcelable.h>
@@ -101,7 +100,7 @@
     auto queue = ProducerQueue::Create(config, UsagePolicy{});
     ASSERT_TRUE(queue != nullptr);
 
-    mProducer = BufferHubQueueProducer::Create(std::move(queue));
+    mProducer = BufferHubProducer::Create(std::move(queue));
     ASSERT_TRUE(mProducer != nullptr);
     mSurface = new Surface(mProducer, true);
     ASSERT_TRUE(mSurface != nullptr);
@@ -145,7 +144,7 @@
 
   const sp<IProducerListener> kDummyListener{new DummyProducerListener};
 
-  sp<BufferHubQueueProducer> mProducer;
+  sp<BufferHubProducer> mProducer;
   sp<Surface> mSurface;
 };
 
@@ -596,8 +595,8 @@
 
   // Create a new producer from the parcelable and connect to kTestApi should
   // succeed.
-  sp<BufferHubQueueProducer> new_producer =
-      BufferHubQueueProducer::Create(std::move(producer_parcelable));
+  sp<BufferHubProducer> new_producer =
+      BufferHubProducer::Create(std::move(producer_parcelable));
   ASSERT_TRUE(new_producer != nullptr);
   EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi,
                                   kTestControlledByApp, &output),
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
index d4d25b0..73932c2 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
@@ -5,10 +5,10 @@
 #include <binder/IServiceManager.h>
 #include <dvr/dvr_api.h>
 #include <dvr/performance_client_api.h>
+#include <gui/BufferHubProducer.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/Surface.h>
-#include <private/dvr/buffer_hub_queue_producer.h>
 #include <utils/Trace.h>
 
 #include <chrono>
@@ -338,7 +338,7 @@
         }
       });
 
-      producer_ = BufferHubQueueProducer::Create(producer_queue_);
+      producer_ = BufferHubProducer::Create(producer_queue_);
     }
 
     int count_ = 0;
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 04418d2..4f0e561 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -15,8 +15,11 @@
 
 cc_library_headers {
     name: "libdvr_headers",
-    owner: "google",
     export_include_dirs: ["include"],
+    vendor_available: false,
+    vndk: {
+        enabled: true,
+    },
 }
 
 cflags = [
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
index 09a49dd..c36d190 100644
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -2,7 +2,7 @@
 #include "include/dvr/dvr_buffer_queue.h"
 
 #include <android/native_window.h>
-#include <private/dvr/buffer_hub_queue_producer.h>
+#include <gui/BufferHubProducer.h>
 
 #include "dvr_internal.h"
 #include "dvr_buffer_queue_internal.h"
@@ -10,7 +10,6 @@
 using namespace android;
 using android::dvr::BufferConsumer;
 using android::dvr::BufferHubBuffer;
-using android::dvr::BufferHubQueueProducer;
 using android::dvr::BufferProducer;
 using android::dvr::ConsumerQueue;
 using android::dvr::ProducerQueue;
@@ -30,8 +29,7 @@
   if (native_window_ == nullptr) {
     // Lazy creation of |native_window|, as not everyone is using
     // DvrWriteBufferQueue as an external surface.
-    sp<IGraphicBufferProducer> gbp =
-        BufferHubQueueProducer::Create(producer_queue_);
+    sp<IGraphicBufferProducer> gbp = BufferHubProducer::Create(producer_queue_);
     native_window_ = new Surface(gbp, true);
   }
 
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 10c0b31..9b84d65 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -16,6 +16,16 @@
         "service_dispatcher.cpp",
         "status.cpp",
     ],
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "libutils",
+        "liblog",
+    ],
+    vendor_available: false,
+    vndk: {
+        enabled: true,
+    },
 }
 
 cc_test {
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
index 80b1769..2cc9664 100644
--- a/libs/vr/libpdx/private/pdx/rpc/variant.h
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -450,12 +450,19 @@
   Variant(Variant&& other)
       : index_{other.index_}, value_{std::move(other.value_), other.index_} {}
 
+// Recent Clang versions has a regression that produces bogus
+// unused-lambda-capture warning. Suppress the warning as a temporary
+// workaround. http://b/71356631
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-lambda-capture"
   // Copy and move construction from Variant types. Each element of OtherTypes
   // must be convertible to an element of Types.
   template <typename... OtherTypes>
   explicit Variant(const Variant<OtherTypes...>& other) {
     other.Visit([this](const auto& value) { Construct(value); });
   }
+#pragma clang diagnostic pop
+
   template <typename... OtherTypes>
   explicit Variant(Variant<OtherTypes...>&& other) {
     other.Visit([this](auto&& value) { Construct(std::move(value)); });
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index cda3c95..779e3a1 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -12,6 +12,10 @@
     name: "pdx_default_transport_lib_defaults",
     export_include_dirs: ["private"],
     whole_static_libs: ["libpdx"],
+    vendor_available: false,
+    vndk: {
+        enabled: true,
+    },
 }
 
 cc_defaults {
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index d640950..79cfdf6 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -30,6 +30,10 @@
     whole_static_libs: [
         "libselinux",
     ],
+    vendor_available: false,
+    vndk: {
+        enabled: true,
+    },
 }
 
 cc_test {
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index a3ee7bb..316f15a 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -71,6 +71,7 @@
 
   void GrantDisplayOwnership() { hardware_composer_.Enable(); }
   void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
+  void OnBootFinished() { hardware_composer_.OnBootFinished(); }
 
  private:
   friend BASE;
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index be17ecf..4f5d548 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -53,6 +53,9 @@
 
 const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
 
+// How long to wait after boot finishes before we turn the display off.
+constexpr int kBootFinishedDisplayOffTimeoutSec = 10;
+
 // Get time offset from a vsync to when the pose for that vsync should be
 // predicted out to. For example, if scanout gets halfway through the frame
 // at the halfway point between vsyncs, then this could be half the period.
@@ -190,6 +193,16 @@
   UpdatePostThreadState(PostThreadState::Suspended, true);
 }
 
+void HardwareComposer::OnBootFinished() {
+  std::lock_guard<std::mutex> lock(post_thread_mutex_);
+  if (boot_finished_)
+    return;
+  boot_finished_ = true;
+  post_thread_wait_.notify_one();
+  if (is_standalone_device_)
+    request_display_callback_(true);
+}
+
 // Update the post thread quiescent state based on idle and suspended inputs.
 void HardwareComposer::UpdatePostThreadState(PostThreadStateType state,
                                              bool suspend) {
@@ -276,6 +289,25 @@
   property_set(kDvrPerformanceProperty, "idle");
 }
 
+bool HardwareComposer::PostThreadCondWait(std::unique_lock<std::mutex>& lock,
+                                          int timeout_sec,
+                                          const std::function<bool()>& pred) {
+  auto pred_with_quit = [&] {
+    return pred() || (post_thread_state_ & PostThreadState::Quit);
+  };
+  if (timeout_sec >= 0) {
+    post_thread_wait_.wait_for(lock, std::chrono::seconds(timeout_sec),
+                               pred_with_quit);
+  } else {
+    post_thread_wait_.wait(lock, pred_with_quit);
+  }
+  if (post_thread_state_ & PostThreadState::Quit) {
+    ALOGI("HardwareComposer::PostThread: Quitting.");
+    return true;
+  }
+  return false;
+}
+
 HWC::Error HardwareComposer::Validate(hwc2_display_t display) {
   uint32_t num_types;
   uint32_t num_requests;
@@ -425,13 +457,6 @@
     layer.Prepare();
   }
 
-  HWC::Error error = Validate(composer_callback_->GetDisplayId());
-  if (error != HWC::Error::None) {
-    ALOGE("HardwareComposer::PostLayers: Validate failed: %s",
-          error.to_string().c_str());
-    return;
-  }
-
   // Now that we have taken in a frame from the application, we have a chance
   // to drop the frame before passing the frame along to HWC.
   // If the display driver has become backed up, we detect it here and then
@@ -472,6 +497,13 @@
   }
 #endif
 
+  HWC::Error error = Validate(composer_callback_->GetDisplayId());
+  if (error != HWC::Error::None) {
+    ALOGE("HardwareComposer::PostLayers: Validate failed: %s",
+          error.to_string().c_str());
+    return;
+  }
+
   error = Present(composer_callback_->GetDisplayId());
   if (error != HWC::Error::None) {
     ALOGE("HardwareComposer::PostLayers: Present failed: %s",
@@ -508,7 +540,7 @@
     pending_surfaces_ = std::move(surfaces);
   }
 
-  if (request_display_callback_ && (!is_standalone_device_ || !composer_))
+  if (request_display_callback_ && !is_standalone_device_)
     request_display_callback_(!display_idle);
 
   // Set idle state based on whether there are any surfaces to handle.
@@ -697,6 +729,28 @@
 
   bool was_running = false;
 
+  if (is_standalone_device_) {
+    // First, wait until boot finishes.
+    std::unique_lock<std::mutex> lock(post_thread_mutex_);
+    if (PostThreadCondWait(lock, -1, [this] { return boot_finished_; })) {
+      return;
+    }
+
+    // Then, wait until we're either leaving the quiescent state, or the boot
+    // finished display off timeout expires.
+    if (PostThreadCondWait(lock, kBootFinishedDisplayOffTimeoutSec,
+                           [this] { return !post_thread_quiescent_; })) {
+      return;
+    }
+
+    LOG_ALWAYS_FATAL_IF(post_thread_state_ & PostThreadState::Suspended,
+                        "Vr flinger should own the display by now.");
+    post_thread_resumed_ = true;
+    post_thread_ready_.notify_all();
+    OnPostThreadResumed();
+    was_running = true;
+  }
+
   while (1) {
     ATRACE_NAME("HardwareComposer::PostThread");
 
@@ -715,13 +769,12 @@
       post_thread_resumed_ = false;
       post_thread_ready_.notify_all();
 
-      if (post_thread_state_ & PostThreadState::Quit) {
-        ALOGI("HardwareComposer::PostThread: Quitting.");
+      if (PostThreadCondWait(lock, -1,
+                             [this] { return !post_thread_quiescent_; })) {
+        // A true return value means we've been asked to quit.
         return;
       }
 
-      post_thread_wait_.wait(lock, [this] { return !post_thread_quiescent_; });
-
       post_thread_resumed_ = true;
       post_thread_ready_.notify_all();
 
@@ -809,17 +862,13 @@
       ATRACE_INT64("sleep_time_ns", sleep_time_ns);
       if (sleep_time_ns > 0) {
         int error = SleepUntil(wakeup_time_ns);
-        ALOGE_IF(error < 0, "HardwareComposer::PostThread: Failed to sleep: %s",
+        ALOGE_IF(error < 0 && error != kPostThreadInterrupted,
+                 "HardwareComposer::PostThread: Failed to sleep: %s",
                  strerror(-error));
-        if (error == kPostThreadInterrupted) {
-          if (layer_config_changed) {
-            // If the layer config changed we need to validateDisplay() even if
-            // we're going to drop the frame, to flush the Composer object's
-            // internal command buffer and apply our layer changes.
-            Validate(composer_callback_->GetDisplayId());
-          }
-          continue;
-        }
+        // If the sleep was interrupted (error == kPostThreadInterrupted),
+        // we still go through and present this frame because we may have set
+        // layers earlier and we want to flush the Composer's internal command
+        // buffer by continuing through to validate and present.
       }
     }
 
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index 9ed4b22..1d0c7ef 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -327,6 +327,9 @@
   // it's paused. This should only be called from surface flinger's main thread.
   void Disable();
 
+  // Called on a binder thread.
+  void OnBootFinished();
+
   // Get the HMD display metrics for the current display.
   display::Metrics GetHmdDisplayMetrics() const;
 
@@ -434,6 +437,16 @@
   // Called on the post thread when the post thread is paused or quits.
   void OnPostThreadPaused();
 
+  // Use post_thread_wait_ to wait for a specific condition, specified by pred.
+  // timeout_sec < 0 means wait indefinitely, otherwise it specifies the timeout
+  // in seconds.
+  // The lock must be held when this function is called.
+  // Returns true if the wait was interrupted because the post thread was asked
+  // to quit.
+  bool PostThreadCondWait(std::unique_lock<std::mutex>& lock,
+                          int timeout_sec,
+                          const std::function<bool()>& pred);
+
   // Map the given shared memory buffer to our broadcast ring to track updates
   // to the config parameters.
   int MapConfigBuffer(IonBuffer& ion_buffer);
@@ -482,6 +495,10 @@
   std::condition_variable post_thread_wait_;
   std::condition_variable post_thread_ready_;
 
+  // When boot is finished this will be set to true and the post thread will be
+  // notified via post_thread_wait_.
+  bool boot_finished_ = false;
+
   // Backlight LED brightness sysfs node.
   pdx::LocalHandle backlight_brightness_fd_;
 
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index 5e7abd7..26aed4f 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -115,6 +115,7 @@
 }
 
 void VrFlinger::OnBootFinished() {
+  display_service_->OnBootFinished();
   sp<IVrManager> vr_manager = interface_cast<IVrManager>(
       defaultServiceManager()->checkService(String16("vrmanager")));
   if (vr_manager.get()) {
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index ff608a3..7923715 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -16,6 +16,7 @@
 
 #include "FileBlobCache.h"
 
+#include <errno.h>
 #include <inttypes.h>
 #include <log/log.h>
 #include <sys/mman.h>
@@ -182,4 +183,4 @@
     }
 }
 
-}
\ No newline at end of file
+}
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 9822849..91a3455 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -407,9 +407,8 @@
 
             DIR* d = opendir(search);
             if (d != NULL) {
-                struct dirent cur;
                 struct dirent* e;
-                while (readdir_r(d, &cur, &e) == 0 && e) {
+                while ((e = readdir(d)) != NULL) {
                     if (e->d_type == DT_DIR) {
                         continue;
                     }
diff --git a/opengl/specs/README b/opengl/specs/README
index cba4453..fdafb1b 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -24,7 +24,8 @@
 0x314A               EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
 0x314B               EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
 0x314C               EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
-0x314D - 0x314F      (unused)
+0x314D               EGL_GL_COLORSPACE_DEFAULT_EXT (EGL_EXT_image_gl_colorspace)
+0x314E - 0x314F      (unused)
 
      Value                       Extension
 ================     ==================================
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 4194dea..86492fd 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -1805,7 +1805,7 @@
     ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
             "xOffset=%f, yOffset=%f, scaleFactor=%f, "
             "pointerIds=0x%x",
-            connection->getInputChannelName(), inputTarget->flags,
+            connection->getInputChannelName().c_str(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
             inputTarget->scaleFactor, inputTarget->pointerIds.value);
 #endif
@@ -1815,7 +1815,7 @@
     if (connection->status != Connection::STATUS_NORMAL) {
 #if DEBUG_DISPATCH_CYCLE
         ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
-                connection->getInputChannelName(), connection->getStatusLabel());
+                connection->getInputChannelName().c_str(), connection->getStatusLabel());
 #endif
         return;
     }
@@ -1833,7 +1833,7 @@
             }
 #if DEBUG_FOCUS
             ALOGD("channel '%s' ~ Split motion event.",
-                    connection->getInputChannelName());
+                    connection->getInputChannelName().c_str());
             logOutboundMotionDetailsLocked("  ", splitMotionEntry);
 #endif
             enqueueDispatchEntriesLocked(currentTime, connection,
@@ -1897,7 +1897,7 @@
                 dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
 #if DEBUG_DISPATCH_CYCLE
             ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
-                    connection->getInputChannelName());
+                    connection->getInputChannelName().c_str());
 #endif
             delete dispatchEntry;
             return; // skip the inconsistent event
@@ -1925,7 +1925,7 @@
                         motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) {
 #if DEBUG_DISPATCH_CYCLE
         ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event",
-                connection->getInputChannelName());
+                connection->getInputChannelName().c_str());
 #endif
             dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
         }
@@ -1942,7 +1942,7 @@
                 dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
 #if DEBUG_DISPATCH_CYCLE
             ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event",
-                    connection->getInputChannelName());
+                    connection->getInputChannelName().c_str());
 #endif
             delete dispatchEntry;
             return; // skip the inconsistent event
@@ -1965,7 +1965,7 @@
         const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ startDispatchCycle",
-            connection->getInputChannelName());
+            connection->getInputChannelName().c_str());
 #endif
 
     while (connection->status == Connection::STATUS_NORMAL
@@ -2048,7 +2048,8 @@
                     ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
                             "This is unexpected because the wait queue is empty, so the pipe "
                             "should be empty and we shouldn't have any problems writing an "
-                            "event to it, status=%d", connection->getInputChannelName(), status);
+                            "event to it, status=%d", connection->getInputChannelName().c_str(),
+                            status);
                     abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                 } else {
                     // Pipe is full and we are waiting for the app to finish process some events
@@ -2056,13 +2057,13 @@
 #if DEBUG_DISPATCH_CYCLE
                     ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
                             "waiting for the application to catch up",
-                            connection->getInputChannelName());
+                            connection->getInputChannelName().c_str());
 #endif
                     connection->inputPublisherBlocked = true;
                 }
             } else {
                 ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
-                        "status=%d", connection->getInputChannelName(), status);
+                        "status=%d", connection->getInputChannelName().c_str(), status);
                 abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
             }
             return;
@@ -2080,7 +2081,7 @@
         const sp<Connection>& connection, uint32_t seq, bool handled) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
-            connection->getInputChannelName(), seq, toString(handled));
+            connection->getInputChannelName().c_str(), seq, toString(handled));
 #endif
 
     connection->inputPublisherBlocked = false;
@@ -2098,7 +2099,7 @@
         const sp<Connection>& connection, bool notify) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
-            connection->getInputChannelName(), toString(notify));
+            connection->getInputChannelName().c_str(), toString(notify));
 #endif
 
     // Clear the dispatch queues.
@@ -2151,7 +2152,7 @@
         if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
             if (!(events & ALOOPER_EVENT_INPUT)) {
                 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
-                        "events=0x%x", connection->getInputChannelName(), events);
+                        "events=0x%x", connection->getInputChannelName().c_str(), events);
                 return 1;
             }
 
@@ -2178,7 +2179,7 @@
             notify = status != DEAD_OBJECT || !connection->monitor;
             if (notify) {
                 ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
-                        connection->getInputChannelName(), status);
+                        connection->getInputChannelName().c_str(), status);
             }
         } else {
             // Monitor channels are never explicitly unregistered.
@@ -2187,7 +2188,7 @@
             notify = !connection->monitor;
             if (notify) {
                 ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
-                        "events=0x%x", connection->getInputChannelName(), events);
+                        "events=0x%x", connection->getInputChannelName().c_str(), events);
             }
         }
 
@@ -2237,7 +2238,7 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
         ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
                 "with reality: %s, mode=%d.",
-                connection->getInputChannelName(), cancelationEvents.size(),
+                connection->getInputChannelName().c_str(), cancelationEvents.size(),
                 options.reason, options.mode);
 #endif
         for (size_t i = 0; i < cancelationEvents.size(); i++) {
@@ -3285,7 +3286,8 @@
             const sp<Connection>& connection = mConnectionsByFd.valueAt(i);
             dump += StringPrintf(INDENT2 "%zu: channelName='%s', windowName='%s', "
                     "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
-                    i, connection->getInputChannelName(), connection->getWindowName(),
+                    i, connection->getInputChannelName().c_str(),
+                    connection->getWindowName().c_str(),
                     connection->getStatusLabel(), toString(connection->monitor),
                     toString(connection->inputPublisherBlocked));
 
@@ -3451,7 +3453,7 @@
 void InputDispatcher::onDispatchCycleBrokenLocked(
         nsecs_t currentTime, const sp<Connection>& connection) {
     ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
-            connection->getInputChannelName());
+            connection->getInputChannelName().c_str());
 
     CommandEntry* commandEntry = postCommandLocked(
             & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
@@ -3568,7 +3570,7 @@
         if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
             std::string msg =
                     StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
-                    connection->getWindowName(), eventDuration * 0.000001f);
+                    connection->getWindowName().c_str(), eventDuration * 0.000001f);
             dispatchEntry->eventEntry->appendDescription(msg);
             ALOGI("%s", msg.c_str());
         }
@@ -3811,7 +3813,7 @@
 void InputDispatcher::traceOutboundQueueLengthLocked(const sp<Connection>& connection) {
     if (ATRACE_ENABLED()) {
         char counterName[40];
-        snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName());
+        snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str());
         ATRACE_INT(counterName, connection->outboundQueue.count());
     }
 }
@@ -3819,7 +3821,7 @@
 void InputDispatcher::traceWaitQueueLengthLocked(const sp<Connection>& connection) {
     if (ATRACE_ENABLED()) {
         char counterName[40];
-        snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName());
+        snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str());
         ATRACE_INT(counterName, connection->waitQueue.count());
     }
 }
@@ -4397,9 +4399,9 @@
 InputDispatcher::Connection::~Connection() {
 }
 
-const char* InputDispatcher::Connection::getWindowName() const {
+const std::string InputDispatcher::Connection::getWindowName() const {
     if (inputWindowHandle != NULL) {
-        return inputWindowHandle->getName().c_str();
+        return inputWindowHandle->getName();
     }
     if (monitor) {
         return "monitor";
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 92b3a9c..8da8450 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -831,9 +831,9 @@
         explicit Connection(const sp<InputChannel>& inputChannel,
                 const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
 
-        inline const char* getInputChannelName() const { return inputChannel->getName().c_str(); }
+        inline const std::string getInputChannelName() const { return inputChannel->getName(); }
 
-        const char* getWindowName() const;
+        const std::string getWindowName() const;
         const char* getStatusLabel() const;
 
         DispatchEntry* findWaitQueueEntry(uint32_t seq);
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 2ee222b..520fea4 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -69,13 +69,15 @@
 NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
         uint32_t policyFlags,
         int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
-        int32_t buttonState, int32_t edgeFlags, int32_t displayId, uint32_t pointerCount,
+        int32_t buttonState, int32_t edgeFlags, int32_t displayId, uint32_t deviceTimestamp,
+        uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
         float xPrecision, float yPrecision, nsecs_t downTime) :
         eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
         action(action), actionButton(actionButton),
         flags(flags), metaState(metaState), buttonState(buttonState),
-        edgeFlags(edgeFlags), displayId(displayId), pointerCount(pointerCount),
+        edgeFlags(edgeFlags), displayId(displayId), deviceTimestamp(deviceTimestamp),
+        pointerCount(pointerCount),
         xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
@@ -88,7 +90,8 @@
         policyFlags(other.policyFlags),
         action(other.action), actionButton(other.actionButton), flags(other.flags),
         metaState(other.metaState), buttonState(other.buttonState),
-        edgeFlags(other.edgeFlags), displayId(other.displayId), pointerCount(other.pointerCount),
+        edgeFlags(other.edgeFlags), displayId(other.displayId),
+        deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount),
         xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
diff --git a/services/inputflinger/InputListener.h b/services/inputflinger/InputListener.h
index ea3dd1c..77afb34 100644
--- a/services/inputflinger/InputListener.h
+++ b/services/inputflinger/InputListener.h
@@ -90,6 +90,13 @@
     int32_t buttonState;
     int32_t edgeFlags;
     int32_t displayId;
+    /**
+     * A timestamp in the input device's time base, not the platform's.
+     * The units are microseconds since the last reset.
+     * This can only be compared to other device timestamps from the same device.
+     * This value will overflow after a little over an hour.
+     */
+    uint32_t deviceTimestamp;
     uint32_t pointerCount;
     PointerProperties pointerProperties[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
@@ -102,7 +109,7 @@
     NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
             int32_t action, int32_t actionButton, int32_t flags,
             int32_t metaState, int32_t buttonState,
-            int32_t edgeFlags, int32_t displayId, uint32_t pointerCount,
+            int32_t edgeFlags, int32_t displayId, uint32_t deviceTimestamp, uint32_t pointerCount,
             const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
             float xPrecision, float yPrecision, nsecs_t downTime);
 
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index e398a84..e0cd8a0 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -1787,7 +1787,7 @@
 
 MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() :
         mCurrentSlot(-1), mSlots(NULL), mSlotCount(0), mUsingSlotsProtocol(false),
-        mHaveStylus(false) {
+        mHaveStylus(false), mDeviceTimestamp(0) {
 }
 
 MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
@@ -1828,6 +1828,7 @@
     } else {
         clearSlots(-1);
     }
+    mDeviceTimestamp = 0;
 }
 
 void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
@@ -1921,6 +1922,8 @@
     } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
         // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
         mCurrentSlot += 1;
+    } else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
+        mDeviceTimestamp = rawEvent->value;
     }
 }
 
@@ -2894,7 +2897,7 @@
                 NotifyMotionArgs releaseArgs(when, getDeviceId(), mSource, policyFlags,
                         AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                         metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                        displayId, 1, &pointerProperties, &pointerCoords,
+                        displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
                         mXPrecision, mYPrecision, downTime);
                 getListener()->notifyMotion(&releaseArgs);
             }
@@ -2903,7 +2906,7 @@
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 motionEventAction, 0, 0, metaState, currentButtonState,
                 AMOTION_EVENT_EDGE_FLAG_NONE,
-                displayId, 1, &pointerProperties, &pointerCoords,
+                displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
                 mXPrecision, mYPrecision, downTime);
         getListener()->notifyMotion(&args);
 
@@ -2915,7 +2918,7 @@
                 NotifyMotionArgs pressArgs(when, getDeviceId(), mSource, policyFlags,
                         AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                         metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                        displayId, 1, &pointerProperties, &pointerCoords,
+                        displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
                         mXPrecision, mYPrecision, downTime);
                 getListener()->notifyMotion(&pressArgs);
             }
@@ -2929,7 +2932,7 @@
             NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
                     metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                    displayId, 1, &pointerProperties, &pointerCoords,
+                    displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
                     mXPrecision, mYPrecision, downTime);
             getListener()->notifyMotion(&hoverArgs);
         }
@@ -2942,7 +2945,7 @@
             NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState,
                     AMOTION_EVENT_EDGE_FLAG_NONE,
-                    displayId, 1, &pointerProperties, &pointerCoords,
+                    displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
                     mXPrecision, mYPrecision, downTime);
             getListener()->notifyMotion(&scrollArgs);
         }
@@ -3072,7 +3075,7 @@
         NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, 0,
                 AMOTION_EVENT_EDGE_FLAG_NONE,
-                displayId, 1, &pointerProperties, &pointerCoords,
+                displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
                 0, 0, 0);
         getListener()->notifyMotion(&scrollArgs);
     }
@@ -3705,11 +3708,13 @@
 
         // Pressure factors.
         mPressureScale = 0;
+        float pressureMax = 1.0;
         if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL
                 || mCalibration.pressureCalibration
                         == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
             if (mCalibration.havePressureScale) {
                 mPressureScale = mCalibration.pressureScale;
+                pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
             } else if (mRawPointerAxes.pressure.valid
                     && mRawPointerAxes.pressure.maxValue != 0) {
                 mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
@@ -3719,7 +3724,7 @@
         mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
         mOrientedRanges.pressure.source = mSource;
         mOrientedRanges.pressure.min = 0;
-        mOrientedRanges.pressure.max = 1.0;
+        mOrientedRanges.pressure.max = pressureMax;
         mOrientedRanges.pressure.flat = 0;
         mOrientedRanges.pressure.fuzz = 0;
         mOrientedRanges.pressure.resolution = 0;
@@ -4727,6 +4732,7 @@
         int32_t buttonState = mCurrentCookedState.buttonState;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
                 metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                mCurrentCookedState.deviceTimestamp,
                 mCurrentCookedState.cookedPointerData.pointerProperties,
                 mCurrentCookedState.cookedPointerData.pointerCoords,
                 mCurrentCookedState.cookedPointerData.idToIndex,
@@ -4749,6 +4755,7 @@
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
                     AMOTION_EVENT_EDGE_FLAG_NONE,
+                    mCurrentCookedState.deviceTimestamp,
                     mCurrentCookedState.cookedPointerData.pointerProperties,
                     mCurrentCookedState.cookedPointerData.pointerCoords,
                     mCurrentCookedState.cookedPointerData.idToIndex,
@@ -4783,6 +4790,7 @@
 
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,
+                    mCurrentCookedState.deviceTimestamp,
                     mLastCookedState.cookedPointerData.pointerProperties,
                     mLastCookedState.cookedPointerData.pointerCoords,
                     mLastCookedState.cookedPointerData.idToIndex,
@@ -4797,6 +4805,7 @@
             ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,
+                    mCurrentCookedState.deviceTimestamp,
                     mCurrentCookedState.cookedPointerData.pointerProperties,
                     mCurrentCookedState.cookedPointerData.pointerCoords,
                     mCurrentCookedState.cookedPointerData.idToIndex,
@@ -4815,6 +4824,7 @@
 
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
+                    mCurrentCookedState.deviceTimestamp,
                     mCurrentCookedState.cookedPointerData.pointerProperties,
                     mCurrentCookedState.cookedPointerData.pointerCoords,
                     mCurrentCookedState.cookedPointerData.idToIndex,
@@ -4830,6 +4840,7 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         dispatchMotion(when, policyFlags, mSource,
                 AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastCookedState.buttonState, 0,
+                mLastCookedState.deviceTimestamp,
                 mLastCookedState.cookedPointerData.pointerProperties,
                 mLastCookedState.cookedPointerData.pointerCoords,
                 mLastCookedState.cookedPointerData.idToIndex,
@@ -4846,6 +4857,7 @@
         if (!mSentHoverEnter) {
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
                     0, 0, metaState, mCurrentRawState.buttonState, 0,
+                    mCurrentCookedState.deviceTimestamp,
                     mCurrentCookedState.cookedPointerData.pointerProperties,
                     mCurrentCookedState.cookedPointerData.pointerCoords,
                     mCurrentCookedState.cookedPointerData.idToIndex,
@@ -4857,6 +4869,7 @@
         dispatchMotion(when, policyFlags, mSource,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                 mCurrentRawState.buttonState, 0,
+                mCurrentCookedState.deviceTimestamp,
                 mCurrentCookedState.cookedPointerData.pointerProperties,
                 mCurrentCookedState.cookedPointerData.pointerCoords,
                 mCurrentCookedState.cookedPointerData.idToIndex,
@@ -4876,6 +4889,7 @@
         dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton,
                     0, metaState, buttonState, 0,
+                    mCurrentCookedState.deviceTimestamp,
                     mCurrentCookedState.cookedPointerData.pointerProperties,
                     mCurrentCookedState.cookedPointerData.pointerCoords,
                     mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -4893,6 +4907,7 @@
         buttonState |= actionButton;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton,
                     0, metaState, buttonState, 0,
+                    mCurrentCookedState.deviceTimestamp,
                     mCurrentCookedState.cookedPointerData.pointerProperties,
                     mCurrentCookedState.cookedPointerData.pointerCoords,
                     mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -4911,6 +4926,8 @@
     uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
 
     mCurrentCookedState.cookedPointerData.clear();
+    mCurrentCookedState.deviceTimestamp =
+            mCurrentRawState.deviceTimestamp;
     mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
     mCurrentCookedState.cookedPointerData.hoveringIdBits =
             mCurrentRawState.rawPointerData.hoveringIdBits;
@@ -5303,7 +5320,7 @@
         if (cancelPreviousGesture) {
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState,
-                    AMOTION_EVENT_EDGE_FLAG_NONE,
+                    AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                     mPointerGesture.lastGestureProperties,
                     mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
                     dispatchedGestureIdBits, -1, 0,
@@ -5324,6 +5341,7 @@
                 dispatchMotion(when, policyFlags, mSource,
                         AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
                         metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                        /* deviceTimestamp */ 0,
                         mPointerGesture.lastGestureProperties,
                         mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
                         dispatchedGestureIdBits, id,
@@ -5338,7 +5356,7 @@
     if (moveNeeded) {
         dispatchMotion(when, policyFlags, mSource,
                 AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
-                AMOTION_EVENT_EDGE_FLAG_NONE,
+                AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                 mPointerGesture.currentGestureProperties,
                 mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
                 dispatchedGestureIdBits, -1,
@@ -5359,6 +5377,7 @@
 
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
+                    /* deviceTimestamp */ 0,
                     mPointerGesture.currentGestureProperties,
                     mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
                     dispatchedGestureIdBits, id,
@@ -5370,7 +5389,7 @@
     if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
         dispatchMotion(when, policyFlags, mSource,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                 mPointerGesture.currentGestureProperties,
                 mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
                 mPointerGesture.currentGestureIdBits, -1,
@@ -5397,7 +5416,7 @@
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
                 metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                mViewport.displayId, 1, &pointerProperties, &pointerCoords,
+                mViewport.displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
                 0, 0, mPointerGesture.downTime);
         getListener()->notifyMotion(&args);
     }
@@ -5427,7 +5446,7 @@
         int32_t buttonState = mCurrentRawState.buttonState;
         dispatchMotion(when, policyFlags, mSource,
                 AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState,
-                AMOTION_EVENT_EDGE_FLAG_NONE,
+                AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                 mPointerGesture.lastGestureProperties,
                 mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
                 mPointerGesture.lastGestureIdBits, -1,
@@ -6319,7 +6338,7 @@
         // Send up.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                  AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0,
-                 mViewport.displayId,
+                 mViewport.displayId, /* deviceTimestamp */ 0,
                  1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
                  mOrientedXPrecision, mOrientedYPrecision,
                  mPointerSimple.downTime);
@@ -6332,7 +6351,7 @@
         // Send hover exit.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, 0,
-                mViewport.displayId,
+                mViewport.displayId, /* deviceTimestamp */ 0,
                 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
                 mPointerSimple.downTime);
@@ -6347,7 +6366,7 @@
             // Send down.
             NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, 0,
-                    mViewport.displayId,
+                    mViewport.displayId, /* deviceTimestamp */ 0,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
                     mPointerSimple.downTime);
@@ -6357,7 +6376,7 @@
         // Send move.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0,
-                mViewport.displayId,
+                mViewport.displayId, /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
                 mPointerSimple.downTime);
@@ -6372,7 +6391,7 @@
             NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
                     mCurrentRawState.buttonState, 0,
-                    mViewport.displayId,
+                    mViewport.displayId, /* deviceTimestamp */ 0,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
                     mPointerSimple.downTime);
@@ -6383,7 +6402,7 @@
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                 mCurrentRawState.buttonState, 0,
-                mViewport.displayId,
+                mViewport.displayId, /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
                 mPointerSimple.downTime);
@@ -6404,7 +6423,7 @@
 
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, 0,
-                mViewport.displayId,
+                mViewport.displayId, /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &pointerCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
                 mPointerSimple.downTime);
@@ -6429,7 +6448,7 @@
 
 void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
         int32_t action, int32_t actionButton, int32_t flags,
-        int32_t metaState, int32_t buttonState, int32_t edgeFlags,
+        int32_t metaState, int32_t buttonState, int32_t edgeFlags, uint32_t deviceTimestamp,
         const PointerProperties* properties, const PointerCoords* coords,
         const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId,
         float xPrecision, float yPrecision, nsecs_t downTime) {
@@ -6467,7 +6486,7 @@
 
     NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
             action, actionButton, flags, metaState, buttonState, edgeFlags,
-            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
+            mViewport.displayId, deviceTimestamp, pointerCount, pointerProperties, pointerCoords,
             xPrecision, yPrecision, downTime);
     getListener()->notifyMotion(&args);
 }
@@ -6947,6 +6966,7 @@
         outCount += 1;
     }
 
+    outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp();
     outState->rawPointerData.pointerCount = outCount;
     mPointerIdBits = newPointerIdBits;
 
@@ -7386,7 +7406,8 @@
 
     NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags,
             AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-            ADISPLAY_ID_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, 0);
+            ADISPLAY_ID_NONE, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+            0, 0, 0);
     getListener()->notifyMotion(&args);
 }
 
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 4f48262..cef3212 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -947,6 +947,7 @@
 
     inline size_t getSlotCount() const { return mSlotCount; }
     inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
+    inline uint32_t getDeviceTimestamp() const { return mDeviceTimestamp; }
 
 private:
     int32_t mCurrentSlot;
@@ -954,6 +955,7 @@
     size_t mSlotCount;
     bool mUsingSlotsProtocol;
     bool mHaveStylus;
+    uint32_t mDeviceTimestamp;
 
     void clearSlots(int32_t initialSlot);
 };
@@ -1396,6 +1398,7 @@
 
     struct RawState {
         nsecs_t when;
+        uint32_t deviceTimestamp;
 
         // Raw pointer sample data.
         RawPointerData rawPointerData;
@@ -1408,6 +1411,7 @@
 
         void copyFrom(const RawState& other) {
             when = other.when;
+            deviceTimestamp = other.deviceTimestamp;
             rawPointerData.copyFrom(other.rawPointerData);
             buttonState = other.buttonState;
             rawVScroll = other.rawVScroll;
@@ -1416,6 +1420,7 @@
 
         void clear() {
             when = 0;
+            deviceTimestamp = 0;
             rawPointerData.clear();
             buttonState = 0;
             rawVScroll = 0;
@@ -1424,6 +1429,7 @@
     };
 
     struct CookedState {
+        uint32_t deviceTimestamp;
         // Cooked pointer sample data.
         CookedPointerData cookedPointerData;
 
@@ -1435,6 +1441,7 @@
         int32_t buttonState;
 
         void copyFrom(const CookedState& other) {
+            deviceTimestamp = other.deviceTimestamp;
             cookedPointerData.copyFrom(other.cookedPointerData);
             fingerIdBits = other.fingerIdBits;
             stylusIdBits = other.stylusIdBits;
@@ -1443,6 +1450,7 @@
         }
 
         void clear() {
+            deviceTimestamp = 0;
             cookedPointerData.clear();
             fingerIdBits.clear();
             stylusIdBits.clear();
@@ -1837,6 +1845,7 @@
     void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
             int32_t action, int32_t actionButton,
             int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
+            uint32_t deviceTimestamp,
             const PointerProperties* properties, const PointerCoords* coords,
             const uint32_t* idToIndex, BitSet32 idBits,
             int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 0344ead..22f15a0 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2954,8 +2954,8 @@
 const int32_t TouchInputMapperTest::RAW_TOUCH_MAX = 31;
 const int32_t TouchInputMapperTest::RAW_TOOL_MIN = 0;
 const int32_t TouchInputMapperTest::RAW_TOOL_MAX = 15;
-const int32_t TouchInputMapperTest::RAW_PRESSURE_MIN = RAW_TOUCH_MIN;
-const int32_t TouchInputMapperTest::RAW_PRESSURE_MAX = RAW_TOUCH_MAX;
+const int32_t TouchInputMapperTest::RAW_PRESSURE_MIN = 0;
+const int32_t TouchInputMapperTest::RAW_PRESSURE_MAX = 255;
 const int32_t TouchInputMapperTest::RAW_ORIENTATION_MIN = -7;
 const int32_t TouchInputMapperTest::RAW_ORIENTATION_MAX = 7;
 const int32_t TouchInputMapperTest::RAW_DISTANCE_MIN = 0;
@@ -4384,6 +4384,7 @@
     void processSlot(MultiTouchInputMapper* mapper, int32_t slot);
     void processToolType(MultiTouchInputMapper* mapper, int32_t toolType);
     void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value);
+    void processTimestamp(MultiTouchInputMapper* mapper, uint32_t value);
     void processMTSync(MultiTouchInputMapper* mapper);
     void processSync(MultiTouchInputMapper* mapper);
 };
@@ -4499,6 +4500,10 @@
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, value);
 }
 
+void MultiTouchInputMapperTest::processTimestamp(MultiTouchInputMapper* mapper, uint32_t value) {
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_MSC, MSC_TIMESTAMP, value);
+}
+
 void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_MT_REPORT, 0);
 }
@@ -5316,6 +5321,12 @@
     addConfigurationProperty("touch.pressure.scale", "0.01");
     addMapperAndConfigure(mapper);
 
+    InputDeviceInfo info;
+    mapper->populateDeviceInfo(&info);
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
+            AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TOUCHSCREEN,
+            0.0f, RAW_PRESSURE_MAX * 0.01, 0.0f, 0.0f));
+
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
     int32_t rawY = 200;
@@ -5875,5 +5886,63 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
+TEST_F(MultiTouchInputMapperTest, Process_HandlesTimestamp) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+    NotifyMotionArgs args;
+
+    // By default, deviceTimestamp should be zero
+    processPosition(mapper, 100, 100);
+    processMTSync(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(0U, args.deviceTimestamp);
+
+    // Now the timestamp of 1000 is reported by evdev and should appear in MotionArgs
+    processPosition(mapper, 0, 0);
+    processTimestamp(mapper, 1000);
+    processMTSync(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(1000U, args.deviceTimestamp);
+}
+
+TEST_F(MultiTouchInputMapperTest, WhenMapperIsReset_TimestampIsCleared) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+    NotifyMotionArgs args;
+
+    // Send a touch event with a timestamp
+    processPosition(mapper, 100, 100);
+    processTimestamp(mapper, 1);
+    processMTSync(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(1U, args.deviceTimestamp);
+
+    // Since the data accumulates, and new timestamp has not arrived, deviceTimestamp won't change
+    processPosition(mapper, 100, 200);
+    processMTSync(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(1U, args.deviceTimestamp);
+
+    mapper->reset(/* when */ 0);
+    // After the mapper is reset, deviceTimestamp should become zero again
+    processPosition(mapper, 100, 300);
+    processMTSync(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(0U, args.deviceTimestamp);
+}
+
 
 } // namespace android
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index deead06..5b6c1ca 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -15,13 +15,13 @@
     GpuService.cpp \
     Layer.cpp \
     BufferLayer.cpp \
+    BufferLayerConsumer.cpp \
     ColorLayer.cpp \
     LayerRejecter.cpp \
     LayerVector.cpp \
     MessageQueue.cpp \
     MonitoredProducer.cpp \
     SurfaceFlinger.cpp \
-    SurfaceFlingerConsumer.cpp \
     SurfaceInterceptor.cpp \
     SurfaceTracing.cpp \
     Transform.cpp \
@@ -30,12 +30,12 @@
     DisplayHardware/HWC2.cpp \
     DisplayHardware/HWComposer.cpp \
     DisplayHardware/HWComposerBufferCache.cpp \
-    DisplayHardware/PowerHAL.cpp \
     DisplayHardware/VirtualDisplaySurface.cpp \
     Effects/Daltonizer.cpp \
     EventLog/EventLogTags.logtags \
     EventLog/EventLog.cpp \
     RenderEngine/Description.cpp \
+    RenderEngine/Image.cpp \
     RenderEngine/Mesh.cpp \
     RenderEngine/Program.cpp \
     RenderEngine/ProgramCache.cpp \
@@ -89,7 +89,6 @@
     libbinder \
     libui \
     libgui \
-    libpowermanager \
     libvulkan \
     libsync \
     libprotobuf-cpp-lite \
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 7d9d4f6..d860f58 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -53,7 +53,7 @@
 BufferLayer::BufferLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
                          uint32_t w, uint32_t h, uint32_t flags)
       : Layer(flinger, client, name, w, h, flags),
-        mSurfaceFlingerConsumer(nullptr),
+        mConsumer(nullptr),
         mTextureName(UINT32_MAX),
         mFormat(PIXEL_FORMAT_NONE),
         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
@@ -89,7 +89,7 @@
     if (mFlinger->mForceFullDamage) {
         surfaceDamageRegion = Region::INVALID_REGION;
     } else {
-        surfaceDamageRegion = mSurfaceFlingerConsumer->getSurfaceDamage();
+        surfaceDamageRegion = mConsumer->getSurfaceDamage();
     }
 }
 
@@ -105,7 +105,8 @@
 
 bool BufferLayer::isVisible() const {
     return !(isHiddenByPolicy()) && getAlpha() > 0.0f &&
-            (getBE().compositionInfo.mBuffer != NULL || getBE().compositionInfo.hwc.sidebandStream != NULL);
+            (getBE().compositionInfo.mBuffer != nullptr ||
+             getBE().compositionInfo.hwc.sidebandStream != nullptr);
 }
 
 bool BufferLayer::isFixedSize() const {
@@ -129,9 +130,9 @@
     mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false;
     mCurrentOpacity = getOpacityForFormat(format);
 
-    mSurfaceFlingerConsumer->setDefaultBufferSize(w, h);
-    mSurfaceFlingerConsumer->setDefaultBufferFormat(format);
-    mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
+    mConsumer->setDefaultBufferSize(w, h);
+    mConsumer->setDefaultBufferFormat(format);
+    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
 
     return NO_ERROR;
 }
@@ -190,7 +191,7 @@
 
     // Bind the current buffer to the GL texture, and wait for it to be
     // ready for us to draw into.
-    status_t err = mSurfaceFlingerConsumer->bindTextureImage();
+    status_t err = mConsumer->bindTextureImage();
     if (err != NO_ERROR) {
         ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
         // Go ahead and draw the buffer anyway; no matter what we do the screen
@@ -207,8 +208,8 @@
 
         // Query the texture matrix given our current filtering mode.
         float textureMatrix[16];
-        mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering);
-        mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
+        mConsumer->setFilteringEnabled(useFiltering);
+        mConsumer->getTransformMatrix(textureMatrix);
 
         if (getTransformToDisplayInverse()) {
             /*
@@ -253,11 +254,11 @@
 }
 
 void BufferLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
-    mSurfaceFlingerConsumer->setReleaseFence(releaseFence);
+    mConsumer->setReleaseFence(releaseFence);
 }
 
 void BufferLayer::abandon() {
-    mSurfaceFlingerConsumer->abandon();
+    mConsumer->abandon();
 }
 
 bool BufferLayer::shouldPresentNow(const DispSync& dispSync) const {
@@ -270,7 +271,7 @@
         return false;
     }
     auto timestamp = mQueueItems[0].mTimestamp;
-    nsecs_t expectedPresent = mSurfaceFlingerConsumer->computeExpectedPresent(dispSync);
+    nsecs_t expectedPresent = mConsumer->computeExpectedPresent(dispSync);
 
     // Ignore timestamps more than a second in the future
     bool isPlausible = timestamp < (expectedPresent + s2ns(1));
@@ -284,7 +285,7 @@
 }
 
 void BufferLayer::setTransformHint(uint32_t orientation) const {
-    mSurfaceFlingerConsumer->setTransformHint(orientation);
+    mConsumer->setTransformHint(orientation);
 }
 
 bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
@@ -312,10 +313,10 @@
     }
 
     // Update mFrameTracker.
-    nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
+    nsecs_t desiredPresentTime = mConsumer->getTimestamp();
     mFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
-    std::shared_ptr<FenceTime> frameReadyFence = mSurfaceFlingerConsumer->getCurrentFenceTime();
+    std::shared_ptr<FenceTime> frameReadyFence = mConsumer->getCurrentFenceTime();
     if (frameReadyFence->isValid()) {
         mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
     } else {
@@ -340,7 +341,7 @@
 
 std::vector<OccupancyTracker::Segment> BufferLayer::getOccupancyHistory(bool forceFlush) {
     std::vector<OccupancyTracker::Segment> history;
-    status_t result = mSurfaceFlingerConsumer->getOccupancyHistory(forceFlush, &history);
+    status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
     if (result != NO_ERROR) {
         ALOGW("[%s] Failed to obtain occupancy history (%d)", mName.string(), result);
         return {};
@@ -349,16 +350,16 @@
 }
 
 bool BufferLayer::getTransformToDisplayInverse() const {
-    return mSurfaceFlingerConsumer->getTransformToDisplayInverse();
+    return mConsumer->getTransformToDisplayInverse();
 }
 
 void BufferLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
-    if (!mSurfaceFlingerConsumer->releasePendingBuffer()) {
+    if (!mConsumer->releasePendingBuffer()) {
         return;
     }
 
     auto releaseFenceTime =
-            std::make_shared<FenceTime>(mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+            std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence());
     mReleaseTimeline.updateSignalTimes();
     mReleaseTimeline.push(releaseFenceTime);
 
@@ -374,10 +375,10 @@
 
     if (android_atomic_acquire_cas(true, false, &mSidebandStreamChanged) == 0) {
         // mSidebandStreamChanged was true
-        mSidebandStream = mSurfaceFlingerConsumer->getSidebandStream();
+        mSidebandStream = mConsumer->getSidebandStream();
         // replicated in LayerBE until FE/BE is ready to be synchronized
         getBE().compositionInfo.hwc.sidebandStream = mSidebandStream;
-        if (getBE().compositionInfo.hwc.sidebandStream != NULL) {
+        if (getBE().compositionInfo.hwc.sidebandStream != nullptr) {
             setTransactionFlags(eTransactionNeeded);
             mFlinger->setTransactionFlags(eTraversalNeeded);
         }
@@ -427,7 +428,7 @@
                     getProducerStickyTransform() != 0, mName.string(),
                     mOverrideScalingMode, mFreezeGeometryUpdates);
     status_t updateResult =
-            mSurfaceFlingerConsumer->updateTexImage(&r, mFlinger->mPrimaryDispSync,
+            mConsumer->updateTexImage(&r, mFlinger->mPrimaryDispSync,
                                                     &mAutoRefresh, &queuedBuffer,
                                                     mLastFrameNumberReceived);
     if (updateResult == BufferQueue::PRESENT_LATER) {
@@ -435,7 +436,7 @@
         // layer update so we check again at the next opportunity.
         mFlinger->signalLayerUpdate();
         return outDirtyRegion;
-    } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
+    } else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) {
         // If the buffer has been rejected, remove it from the shadow queue
         // and return early
         if (queuedBuffer) {
@@ -466,7 +467,7 @@
 
     if (queuedBuffer) {
         // Autolock scope
-        auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+        auto currentFrameNumber = mConsumer->getFrameNumber();
 
         Mutex::Autolock lock(mQueueItemLock);
 
@@ -489,17 +490,17 @@
 
     // update the active buffer
     getBE().compositionInfo.mBuffer =
-            mSurfaceFlingerConsumer->getCurrentBuffer(&getBE().compositionInfo.mBufferSlot);
+            mConsumer->getCurrentBuffer(&getBE().compositionInfo.mBufferSlot);
     // replicated in LayerBE until FE/BE is ready to be synchronized
     mActiveBuffer = getBE().compositionInfo.mBuffer;
-    if (getBE().compositionInfo.mBuffer == NULL) {
+    if (getBE().compositionInfo.mBuffer == nullptr) {
         // this can only happen if the very first buffer was rejected.
         return outDirtyRegion;
     }
 
     mBufferLatched = true;
     mPreviousFrameNumber = mCurrentFrameNumber;
-    mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+    mCurrentFrameNumber = mConsumer->getFrameNumber();
 
     {
         Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -508,17 +509,17 @@
 
     mRefreshPending = true;
     mFrameLatencyNeeded = true;
-    if (oldBuffer == NULL) {
+    if (oldBuffer == nullptr) {
         // the first time we receive a buffer, we need to trigger a
         // geometry invalidation.
         recomputeVisibleRegions = true;
     }
 
-    setDataSpace(mSurfaceFlingerConsumer->getCurrentDataSpace());
+    setDataSpace(mConsumer->getCurrentDataSpace());
 
-    Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
-    const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
-    const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
+    Rect crop(mConsumer->getCurrentCrop());
+    const uint32_t transform(mConsumer->getCurrentTransform());
+    const uint32_t scalingMode(mConsumer->getCurrentScalingMode());
     if ((crop != mCurrentCrop) ||
         (transform != mCurrentTransform) ||
         (scalingMode != mCurrentScalingMode)) {
@@ -528,7 +529,7 @@
         recomputeVisibleRegions = true;
     }
 
-    if (oldBuffer != NULL) {
+    if (oldBuffer != nullptr) {
         uint32_t bufWidth = getBE().compositionInfo.mBuffer->getWidth();
         uint32_t bufHeight = getBE().compositionInfo.mBuffer->getHeight();
         if (bufWidth != uint32_t(oldBuffer->width) ||
@@ -573,7 +574,7 @@
 }
 
 void BufferLayer::setDefaultBufferSize(uint32_t w, uint32_t h) {
-    mSurfaceFlingerConsumer->setDefaultBufferSize(w, h);
+    mConsumer->setDefaultBufferSize(w, h);
 }
 
 void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
@@ -633,7 +634,7 @@
     hwcInfo.bufferCache.getHwcBuffer(getBE().compositionInfo.mBufferSlot,
                                      getBE().compositionInfo.mBuffer, &hwcSlot, &hwcBuffer);
 
-    auto acquireFence = mSurfaceFlingerConsumer->getCurrentFence();
+    auto acquireFence = mConsumer->getCurrentFence();
     error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
@@ -660,10 +661,11 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer, true);
     mProducer = new MonitoredProducer(producer, mFlinger, this);
-    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName, this);
-    mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-    mSurfaceFlingerConsumer->setContentsChangedListener(this);
-    mSurfaceFlingerConsumer->setName(mName);
+    mConsumer = new BufferLayerConsumer(consumer,
+            mFlinger->getRenderEngine(), mTextureName, this);
+    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
+    mConsumer->setContentsChangedListener(this);
+    mConsumer->setName(mName);
 
     if (mFlinger->isLayerTripleBufferingDisabled()) {
         mProducer->setMaxDequeuedBufferCount(2);
@@ -787,16 +789,17 @@
      * minimal value)? Or, we could make GL behave like HWC -- but this feel
      * like more of a hack.
      */
-    Rect win(computeBounds());
+    const Rect bounds{computeBounds()}; // Rounds from FloatRect
 
     Transform t = getTransform();
+    Rect win = bounds;
     if (!s.finalCrop.isEmpty()) {
         win = t.transform(win);
         if (!win.intersect(s.finalCrop, &win)) {
             win.clear();
         }
         win = t.inverse().transform(win);
-        if (!win.intersect(computeBounds(), &win)) {
+        if (!win.intersect(bounds, &win)) {
             win.clear();
         }
     }
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 1127952..6b02f8c 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "BufferLayerConsumer.h"
 #include "Client.h"
 #include "Layer.h"
 #include "DisplayHardware/HWComposer.h"
@@ -26,7 +27,6 @@
 #include "RenderEngine/Mesh.h"
 #include "RenderEngine/Texture.h"
 #include "SurfaceFlinger.h"
-#include "SurfaceFlingerConsumer.h"
 #include "Transform.h"
 
 #include <gui/ISurfaceComposerClient.h>
@@ -48,13 +48,13 @@
 namespace android {
 
 /*
- * A new BufferQueue and a new SurfaceFlingerConsumer are created when the
+ * A new BufferQueue and a new BufferLayerConsumer are created when the
  * BufferLayer is first referenced.
  *
  * This also implements onFrameAvailable(), which notifies SurfaceFlinger
  * that new data has arrived.
  */
-class BufferLayer : public Layer, public SurfaceFlingerConsumer::ContentsChangedListener {
+class BufferLayer : public Layer, public BufferLayerConsumer::ContentsChangedListener {
 public:
     BufferLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
                 uint32_t h, uint32_t flags);
@@ -137,7 +137,7 @@
     void onFirstRef() override;
 
     // Interface implementation for
-    // SurfaceFlingerConsumer::ContentsChangedListener
+    // BufferLayerConsumer::ContentsChangedListener
     void onFrameAvailable(const BufferItem& item) override;
     void onFrameReplaced(const BufferItem& item) override;
     void onSidebandStreamChanged() override;
@@ -170,7 +170,7 @@
     sp<IGraphicBufferProducer> getProducer() const;
 
 private:
-    sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer;
+    sp<BufferLayerConsumer> mConsumer;
 
     // Check all of the local sync points to ensure that all transactions
     // which need to have been applied prior to the frame which is about to
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
new file mode 100644
index 0000000..8f5c9c7
--- /dev/null
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "BufferLayerConsumer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include "BufferLayerConsumer.h"
+
+#include "DispSync.h"
+#include "Layer.h"
+#include "RenderEngine/RenderEngine.h"
+
+#include <inttypes.h>
+
+#include <cutils/compiler.h>
+
+#include <hardware/hardware.h>
+
+#include <math/mat4.h>
+
+#include <gui/BufferItem.h>
+#include <gui/GLConsumer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/gui/ComposerService.h>
+#include <private/gui/SyncFeatures.h>
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+// Macros for including the BufferLayerConsumer name in log messages
+#define BLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
+//#define BLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+
+static const mat4 mtxIdentity;
+
+BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, RenderEngine& engine,
+                                         uint32_t tex, Layer* layer)
+      : ConsumerBase(bq, false),
+        mCurrentCrop(Rect::EMPTY_RECT),
+        mCurrentTransform(0),
+        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+        mCurrentFence(Fence::NO_FENCE),
+        mCurrentTimestamp(0),
+        mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+        mCurrentFrameNumber(0),
+        mCurrentTransformToDisplayInverse(false),
+        mCurrentSurfaceDamage(),
+        mDefaultWidth(1),
+        mDefaultHeight(1),
+        mFilteringEnabled(true),
+        mRE(engine),
+        mTexName(tex),
+        mLayer(layer),
+        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {
+    BLC_LOGV("BufferLayerConsumer");
+
+    memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+    mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+status_t BufferLayerConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        BLC_LOGE("setDefaultBufferSize: BufferLayerConsumer is abandoned!");
+        return NO_INIT;
+    }
+    mDefaultWidth = w;
+    mDefaultHeight = h;
+    return mConsumer->setDefaultBufferSize(w, h);
+}
+
+void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) {
+    setFrameAvailableListener(listener);
+    Mutex::Autolock lock(mMutex);
+    mContentsChangedListener = listener;
+}
+
+// We need to determine the time when a buffer acquired now will be
+// displayed.  This can be calculated:
+//   time when previous buffer's actual-present fence was signaled
+//    + current display refresh rate * HWC latency
+//    + a little extra padding
+//
+// Buffer producers are expected to set their desired presentation time
+// based on choreographer time stamps, which (coming from vsync events)
+// will be slightly later then the actual-present timing.  If we get a
+// desired-present time that is unintentionally a hair after the next
+// vsync, we'll hold the frame when we really want to display it.  We
+// need to take the offset between actual-present and reported-vsync
+// into account.
+//
+// If the system is configured without a DispSync phase offset for the app,
+// we also want to throw in a bit of padding to avoid edge cases where we
+// just barely miss.  We want to do it here, not in every app.  A major
+// source of trouble is the app's use of the display's ideal refresh time
+// (via Display.getRefreshRate()), which could be off of the actual refresh
+// by a few percent, with the error multiplied by the number of frames
+// between now and when the buffer should be displayed.
+//
+// If the refresh reported to the app has a phase offset, we shouldn't need
+// to tweak anything here.
+nsecs_t BufferLayerConsumer::computeExpectedPresent(const DispSync& dispSync) {
+    // The HWC doesn't currently have a way to report additional latency.
+    // Assume that whatever we submit now will appear right after the flip.
+    // For a smart panel this might be 1.  This is expressed in frames,
+    // rather than time, because we expect to have a constant frame delay
+    // regardless of the refresh rate.
+    const uint32_t hwcLatency = 0;
+
+    // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
+    const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency);
+
+    // The DispSync time is already adjusted for the difference between
+    // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so
+    // we don't need to factor that in here.  Pad a little to avoid
+    // weird effects if apps might be requesting times right on the edge.
+    nsecs_t extraPadding = 0;
+    if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
+        extraPadding = 1000000; // 1ms (6% of 60Hz)
+    }
+
+    return nextRefresh + extraPadding;
+}
+
+status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
+                                             bool* autoRefresh, bool* queuedBuffer,
+                                             uint64_t maxFrameNumber) {
+    ATRACE_CALL();
+    BLC_LOGV("updateTexImage");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        BLC_LOGE("updateTexImage: BufferLayerConsumer is abandoned!");
+        return NO_INIT;
+    }
+
+    // Make sure RenderEngine is current
+    if (!mRE.isCurrent()) {
+        BLC_LOGE("updateTexImage: RenderEngine is not current");
+        return INVALID_OPERATION;
+    }
+
+    BufferItem item;
+
+    // Acquire the next buffer.
+    // In asynchronous mode the list is guaranteed to be one buffer
+    // deep, while in synchronous mode we use the oldest buffer.
+    status_t err = acquireBufferLocked(&item, computeExpectedPresent(dispSync), maxFrameNumber);
+    if (err != NO_ERROR) {
+        if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+            err = NO_ERROR;
+        } else if (err == BufferQueue::PRESENT_LATER) {
+            // return the error, without logging
+        } else {
+            BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
+        }
+        return err;
+    }
+
+    if (autoRefresh) {
+        *autoRefresh = item.mAutoRefresh;
+    }
+
+    if (queuedBuffer) {
+        *queuedBuffer = item.mQueuedBuffer;
+    }
+
+    // We call the rejecter here, in case the caller has a reason to
+    // not accept this buffer.  This is used by SurfaceFlinger to
+    // reject buffers which have the wrong size
+    int slot = item.mSlot;
+    if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) {
+        releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
+        return BUFFER_REJECTED;
+    }
+
+    // Release the previous buffer.
+    err = updateAndReleaseLocked(item, &mPendingRelease);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    if (!SyncFeatures::getInstance().useNativeFenceSync()) {
+        // Bind the new buffer to the GL texture.
+        //
+        // Older devices require the "implicit" synchronization provided
+        // by glEGLImageTargetTexture2DOES, which this method calls.  Newer
+        // devices will either call this in Layer::onDraw, or (if it's not
+        // a GL-composited layer) not at all.
+        err = bindTextureImageLocked();
+    }
+
+    return err;
+}
+
+status_t BufferLayerConsumer::bindTextureImage() {
+    Mutex::Autolock lock(mMutex);
+    return bindTextureImageLocked();
+}
+
+void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
+    if (!fence->isValid()) {
+        return;
+    }
+
+    auto slot = mPendingRelease.isPending ? mPendingRelease.currentTexture : mCurrentTexture;
+    if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
+        return;
+    }
+
+    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
+                                            : mCurrentTextureImage->graphicBuffer();
+    auto err = addReleaseFence(slot, buffer, fence);
+    if (err != OK) {
+        BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
+    }
+}
+
+bool BufferLayerConsumer::releasePendingBuffer() {
+    if (!mPendingRelease.isPending) {
+        BLC_LOGV("Pending buffer already released");
+        return false;
+    }
+    BLC_LOGV("Releasing pending buffer");
+    Mutex::Autolock lock(mMutex);
+    status_t result =
+            releaseBufferLocked(mPendingRelease.currentTexture, mPendingRelease.graphicBuffer);
+    if (result < NO_ERROR) {
+        BLC_LOGE("releasePendingBuffer failed: %s (%d)", strerror(-result), result);
+    }
+    mPendingRelease = PendingRelease();
+    return true;
+}
+
+sp<Fence> BufferLayerConsumer::getPrevFinalReleaseFence() const {
+    Mutex::Autolock lock(mMutex);
+    return ConsumerBase::mPrevFinalReleaseFence;
+}
+
+status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+                                                  uint64_t maxFrameNumber) {
+    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // If item->mGraphicBuffer is not null, this buffer has not been acquired
+    // before, so any prior EglImage created is using a stale buffer. This
+    // replaces any old EglImage with a new one (using the new buffer).
+    if (item->mGraphicBuffer != nullptr) {
+        mImages[item->mSlot] = new Image(item->mGraphicBuffer, mRE);
+    }
+
+    return NO_ERROR;
+}
+
+bool BufferLayerConsumer::canUseImageCrop(const Rect& crop) const {
+    // If the crop rect is not at the origin, we can't set the crop on the
+    // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
+    // extension.  In the future we can add a layered extension that
+    // removes this restriction if there is hardware that can support it.
+    return mRE.supportsImageCrop() && crop.left == 0 && crop.top == 0;
+}
+
+status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
+                                                     PendingRelease* pendingRelease) {
+    status_t err = NO_ERROR;
+
+    int slot = item.mSlot;
+
+    // Do whatever sync ops we need to do before releasing the old slot.
+    if (slot != mCurrentTexture) {
+        err = syncForReleaseLocked();
+        if (err != NO_ERROR) {
+            // Release the buffer we just acquired.  It's not safe to
+            // release the old buffer, so instead we just drop the new frame.
+            // As we are still under lock since acquireBuffer, it is safe to
+            // release by slot.
+            releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
+            return err;
+        }
+    }
+
+    BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
+             mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : 0,
+             slot, mSlots[slot].mGraphicBuffer->handle);
+
+    // Hang onto the pointer so that it isn't freed in the call to
+    // releaseBufferLocked() if we're in shared buffer mode and both buffers are
+    // the same.
+    sp<Image> nextTextureImage = mImages[slot];
+
+    // release old buffer
+    if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+        if (pendingRelease == nullptr) {
+            status_t status =
+                    releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer());
+            if (status < NO_ERROR) {
+                BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
+                         status);
+                err = status;
+                // keep going, with error raised [?]
+            }
+        } else {
+            pendingRelease->currentTexture = mCurrentTexture;
+            pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+            pendingRelease->isPending = true;
+        }
+    }
+
+    // Update the BufferLayerConsumer state.
+    mCurrentTexture = slot;
+    mCurrentTextureImage = nextTextureImage;
+    mCurrentCrop = item.mCrop;
+    mCurrentTransform = item.mTransform;
+    mCurrentScalingMode = item.mScalingMode;
+    mCurrentTimestamp = item.mTimestamp;
+    mCurrentDataSpace = item.mDataSpace;
+    mCurrentHdrMetadata = item.mHdrMetadata;
+    mCurrentFence = item.mFence;
+    mCurrentFenceTime = item.mFenceTime;
+    mCurrentFrameNumber = item.mFrameNumber;
+    mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse;
+    mCurrentSurfaceDamage = item.mSurfaceDamage;
+
+    computeCurrentTransformMatrixLocked();
+
+    return err;
+}
+
+status_t BufferLayerConsumer::bindTextureImageLocked() {
+    mRE.checkErrors();
+
+    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
+        BLC_LOGE("bindTextureImage: no currently-bound texture");
+        mRE.bindExternalTextureImage(mTexName, RE::Image(mRE));
+        return NO_INIT;
+    }
+
+    const Rect& imageCrop = canUseImageCrop(mCurrentCrop) ? mCurrentCrop : Rect::EMPTY_RECT;
+    status_t err = mCurrentTextureImage->createIfNeeded(imageCrop);
+    if (err != NO_ERROR) {
+        BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
+        mRE.bindExternalTextureImage(mTexName, RE::Image(mRE));
+        return UNKNOWN_ERROR;
+    }
+
+    mRE.bindExternalTextureImage(mTexName, mCurrentTextureImage->image());
+
+    // Wait for the new buffer to be ready.
+    return doFenceWaitLocked();
+}
+
+status_t BufferLayerConsumer::syncForReleaseLocked() {
+    BLC_LOGV("syncForReleaseLocked");
+
+    if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+        if (SyncFeatures::getInstance().useNativeFenceSync()) {
+            base::unique_fd fenceFd = mRE.flush();
+            if (fenceFd == -1) {
+                BLC_LOGE("syncForReleaseLocked: failed to flush RenderEngine");
+                return UNKNOWN_ERROR;
+            }
+            sp<Fence> fence(new Fence(std::move(fenceFd)));
+            status_t err = addReleaseFenceLocked(mCurrentTexture,
+                                                 mCurrentTextureImage->graphicBuffer(), fence);
+            if (err != OK) {
+                BLC_LOGE("syncForReleaseLocked: error adding release fence: "
+                         "%s (%d)",
+                         strerror(-err), err);
+                return err;
+            }
+        }
+    }
+
+    return OK;
+}
+
+void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
+    Mutex::Autolock lock(mMutex);
+    memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void BufferLayerConsumer::setFilteringEnabled(bool enabled) {
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        BLC_LOGE("setFilteringEnabled: BufferLayerConsumer is abandoned!");
+        return;
+    }
+    bool needsRecompute = mFilteringEnabled != enabled;
+    mFilteringEnabled = enabled;
+
+    if (needsRecompute && mCurrentTextureImage == nullptr) {
+        BLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == nullptr");
+    }
+
+    if (needsRecompute && mCurrentTextureImage != nullptr) {
+        computeCurrentTransformMatrixLocked();
+    }
+}
+
+void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
+    BLC_LOGV("computeCurrentTransformMatrixLocked");
+    sp<GraphicBuffer> buf =
+            (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer();
+    if (buf == nullptr) {
+        BLC_LOGD("computeCurrentTransformMatrixLocked: "
+                 "mCurrentTextureImage is nullptr");
+    }
+    const Rect& cropRect = canUseImageCrop(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop;
+    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, cropRect, mCurrentTransform,
+                                       mFilteringEnabled);
+}
+
+nsecs_t BufferLayerConsumer::getTimestamp() {
+    BLC_LOGV("getTimestamp");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTimestamp;
+}
+
+android_dataspace BufferLayerConsumer::getCurrentDataSpace() {
+    BLC_LOGV("getCurrentDataSpace");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentDataSpace;
+}
+
+const HdrMetadata& BufferLayerConsumer::getCurrentHdrMetadata() const {
+    BLC_LOGV("getCurrentHdrMetadata");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentHdrMetadata;
+}
+
+uint64_t BufferLayerConsumer::getFrameNumber() {
+    BLC_LOGV("getFrameNumber");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFrameNumber;
+}
+
+bool BufferLayerConsumer::getTransformToDisplayInverse() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTransformToDisplayInverse;
+}
+
+const Region& BufferLayerConsumer::getSurfaceDamage() const {
+    return mCurrentSurfaceDamage;
+}
+
+sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const {
+    Mutex::Autolock lock(mMutex);
+
+    if (outSlot != nullptr) {
+        *outSlot = mCurrentTexture;
+    }
+
+    return (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer();
+}
+
+Rect BufferLayerConsumer::getCurrentCrop() const {
+    Mutex::Autolock lock(mMutex);
+    return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
+            ? GLConsumer::scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
+            : mCurrentCrop;
+}
+
+uint32_t BufferLayerConsumer::getCurrentTransform() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTransform;
+}
+
+uint32_t BufferLayerConsumer::getCurrentScalingMode() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentScalingMode;
+}
+
+sp<Fence> BufferLayerConsumer::getCurrentFence() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFence;
+}
+
+std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFenceTime;
+}
+
+status_t BufferLayerConsumer::doFenceWaitLocked() const {
+    if (!mRE.isCurrent()) {
+        BLC_LOGE("doFenceWait: RenderEngine is not current");
+        return INVALID_OPERATION;
+    }
+
+    if (mCurrentFence->isValid()) {
+        if (SyncFeatures::getInstance().useWaitSync()) {
+            base::unique_fd fenceFd(mCurrentFence->dup());
+            if (fenceFd == -1) {
+                BLC_LOGE("doFenceWait: error dup'ing fence fd: %d", errno);
+                return -errno;
+            }
+            if (!mRE.waitFence(std::move(fenceFd))) {
+                BLC_LOGE("doFenceWait: failed to wait on fence fd");
+                return UNKNOWN_ERROR;
+            }
+        } else {
+            status_t err = mCurrentFence->waitForever("BufferLayerConsumer::doFenceWaitLocked");
+            if (err != NO_ERROR) {
+                BLC_LOGE("doFenceWait: error waiting for fence: %d", err);
+                return err;
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
+    BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+    if (slotIndex == mCurrentTexture) {
+        mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+    }
+    mImages[slotIndex].clear();
+    ConsumerBase::freeBufferLocked(slotIndex);
+}
+
+void BufferLayerConsumer::onDisconnect() {
+    sp<Layer> l = mLayer.promote();
+    if (l.get()) {
+        l->onDisconnect();
+    }
+}
+
+void BufferLayerConsumer::onSidebandStreamChanged() {
+    FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
+    {
+        Mutex::Autolock lock(mFrameAvailableMutex);
+        unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
+    }
+    sp<ContentsChangedListener> listener;
+    { // scope for the lock
+        Mutex::Autolock lock(mMutex);
+        ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
+        listener = mContentsChangedListener.promote();
+    }
+
+    if (listener != nullptr) {
+        listener->onSidebandStreamChanged();
+    }
+}
+
+void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+                                                   FrameEventHistoryDelta* outDelta) {
+    sp<Layer> l = mLayer.promote();
+    if (l.get()) {
+        l->addAndGetFrameTimestamps(newTimestamps, outDelta);
+    }
+}
+
+void BufferLayerConsumer::abandonLocked() {
+    BLC_LOGV("abandonLocked");
+    mCurrentTextureImage.clear();
+    ConsumerBase::abandonLocked();
+}
+
+status_t BufferLayerConsumer::setConsumerUsageBits(uint64_t usage) {
+    return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
+}
+
+void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const {
+    result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
+                        "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
+                        prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
+                        mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+                        mCurrentTransform);
+
+    ConsumerBase::dumpLocked(result, prefix);
+}
+
+BufferLayerConsumer::Image::Image(sp<GraphicBuffer> graphicBuffer, const RenderEngine& engine)
+      : mGraphicBuffer(graphicBuffer),
+        mImage{engine},
+        mCreated(false),
+        mCropWidth(0),
+        mCropHeight(0) {}
+
+status_t BufferLayerConsumer::Image::createIfNeeded(const Rect& imageCrop) {
+    const int32_t cropWidth = imageCrop.width();
+    const int32_t cropHeight = imageCrop.height();
+    if (mCreated && mCropWidth == cropWidth && mCropHeight == cropHeight) {
+        return OK;
+    }
+
+    mCreated = mImage.setNativeWindowBuffer(mGraphicBuffer->getNativeBuffer(),
+                                            mGraphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED,
+                                            cropWidth, cropHeight);
+    if (mCreated) {
+        mCropWidth = cropWidth;
+        mCropHeight = cropHeight;
+    } else {
+        mCropWidth = 0;
+        mCropHeight = 0;
+
+        const sp<GraphicBuffer>& buffer = mGraphicBuffer;
+        ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+              buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+              buffer->getPixelFormat());
+    }
+
+    return mCreated ? OK : UNKNOWN_ERROR;
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
new file mode 100644
index 0000000..f473390
--- /dev/null
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2010 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_BUFFERLAYERCONSUMER_H
+#define ANDROID_BUFFERLAYERCONSUMER_H
+
+#include "RenderEngine/Image.h"
+
+#include <gui/BufferQueueDefs.h>
+#include <gui/ConsumerBase.h>
+#include <gui/HdrMetadata.h>
+
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Region.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class DispSync;
+class Layer;
+class RenderEngine;
+class String8;
+
+/*
+ * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue,
+ * and makes them available to RenderEngine as a texture.
+ *
+ * A typical usage pattern is to call updateTexImage() when a new frame is
+ * desired.  If a new frame is available, the frame is latched.  If not, the
+ * previous contents are retained.  The texture is attached and updated after
+ * bindTextureImage() is called.
+ *
+ * All calls to updateTexImage must be made with RenderEngine being current.
+ * The texture is attached to the TEXTURE_EXTERNAL texture target.
+ */
+class BufferLayerConsumer : public ConsumerBase {
+public:
+    static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
+
+    class BufferRejecter {
+        friend class BufferLayerConsumer;
+        virtual bool reject(const sp<GraphicBuffer>& buf, const BufferItem& item) = 0;
+
+    protected:
+        virtual ~BufferRejecter() {}
+    };
+
+    struct ContentsChangedListener : public FrameAvailableListener {
+        virtual void onSidebandStreamChanged() = 0;
+    };
+
+    // BufferLayerConsumer constructs a new BufferLayerConsumer object.  The
+    // tex parameter indicates the name of the RenderEngine texture to which
+    // images are to be streamed.
+    BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, RenderEngine& engine, uint32_t tex,
+                        Layer* layer);
+
+    // Sets the contents changed listener. This should be used instead of
+    // ConsumerBase::setFrameAvailableListener().
+    void setContentsChangedListener(const wp<ContentsChangedListener>& listener);
+
+    nsecs_t computeExpectedPresent(const DispSync& dispSync);
+
+    // updateTexImage acquires the most recently queued buffer, and sets the
+    // image contents of the target texture to it.
+    //
+    // This call may only be made while RenderEngine is current.
+    //
+    // This calls doFenceWait to ensure proper synchronization unless native
+    // fence is supported.
+    //
+    // Unlike the GLConsumer version, this version takes a functor that may be
+    // used to reject the newly acquired buffer.  It also does not bind the
+    // RenderEngine texture until bindTextureImage is called.
+    status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync, bool* autoRefresh,
+                            bool* queuedBuffer, uint64_t maxFrameNumber);
+
+    // See BufferLayerConsumer::bindTextureImageLocked().
+    status_t bindTextureImage();
+
+    // setReleaseFence stores a fence that will signal when the current buffer
+    // is no longer being read. This fence will be returned to the producer
+    // when the current buffer is released by updateTexImage(). Multiple
+    // fences can be set for a given buffer; they will be merged into a single
+    // union fence.
+    void setReleaseFence(const sp<Fence>& fence);
+
+    bool releasePendingBuffer();
+
+    sp<Fence> getPrevFinalReleaseFence() const;
+
+    // See GLConsumer::getTransformMatrix.
+    void getTransformMatrix(float mtx[16]);
+
+    // getTimestamp retrieves the timestamp associated with the texture image
+    // set by the most recent call to updateTexImage.
+    //
+    // The timestamp is in nanoseconds, and is monotonically increasing. Its
+    // other semantics (zero point, etc) are source-dependent and should be
+    // documented by the source.
+    int64_t getTimestamp();
+
+    // getDataSpace retrieves the DataSpace associated with the texture image
+    // set by the most recent call to updateTexImage.
+    android_dataspace getCurrentDataSpace();
+
+    // getCurrentHdrMetadata retrieves the HDR metadata associated with the
+    // texture image set by the most recent call to updateTexImage.
+    const HdrMetadata& getCurrentHdrMetadata() const;
+
+    // getFrameNumber retrieves the frame number associated with the texture
+    // image set by the most recent call to updateTexImage.
+    //
+    // The frame number is an incrementing counter set to 0 at the creation of
+    // the BufferQueue associated with this consumer.
+    uint64_t getFrameNumber();
+
+    bool getTransformToDisplayInverse() const;
+
+    // must be called from SF main thread
+    const Region& getSurfaceDamage() const;
+
+    // See GLConsumer::setDefaultBufferSize.
+    status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+    // setFilteringEnabled sets whether the transform matrix should be computed
+    // for use with bilinear filtering.
+    void setFilteringEnabled(bool enabled);
+
+    // getCurrentBuffer returns the buffer associated with the current image.
+    // When outSlot is not nullptr, the current buffer slot index is also
+    // returned.
+    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
+
+    // getCurrentCrop returns the cropping rectangle of the current buffer.
+    Rect getCurrentCrop() const;
+
+    // getCurrentTransform returns the transform of the current buffer.
+    uint32_t getCurrentTransform() const;
+
+    // getCurrentScalingMode returns the scaling mode of the current buffer.
+    uint32_t getCurrentScalingMode() const;
+
+    // getCurrentFence returns the fence indicating when the current buffer is
+    // ready to be read from.
+    sp<Fence> getCurrentFence() const;
+
+    // getCurrentFence returns the FenceTime indicating when the current
+    // buffer is ready to be read from.
+    std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
+    // setConsumerUsageBits overrides the ConsumerBase method to OR
+    // DEFAULT_USAGE_FLAGS to usage.
+    status_t setConsumerUsageBits(uint64_t usage);
+
+protected:
+    // abandonLocked overrides the ConsumerBase method to clear
+    // mCurrentTextureImage in addition to the ConsumerBase behavior.
+    virtual void abandonLocked();
+
+    // dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
+    // specific info in addition to the ConsumerBase behavior.
+    virtual void dumpLocked(String8& result, const char* prefix) const;
+
+    // acquireBufferLocked overrides the ConsumerBase method to update the
+    // mImages array in addition to the ConsumerBase behavior.
+    virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+                                         uint64_t maxFrameNumber = 0) override;
+
+    bool canUseImageCrop(const Rect& crop) const;
+
+    struct PendingRelease {
+        PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {}
+
+        bool isPending;
+        int currentTexture;
+        sp<GraphicBuffer> graphicBuffer;
+    };
+
+    // This releases the buffer in the slot referenced by mCurrentTexture,
+    // then updates state to refer to the BufferItem, which must be a
+    // newly-acquired buffer. If pendingRelease is not null, the parameters
+    // which would have been passed to releaseBufferLocked upon the successful
+    // completion of the method will instead be returned to the caller, so that
+    // it may call releaseBufferLocked itself later.
+    status_t updateAndReleaseLocked(const BufferItem& item,
+                                    PendingRelease* pendingRelease = nullptr);
+
+    // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.  Uses
+    // mCurrentTexture if it's set, mCurrentTextureImage if not.  If the
+    // bind succeeds, this calls doFenceWait.
+    status_t bindTextureImageLocked();
+
+private:
+    // Image is a utility class for tracking and creating RE::Images. There
+    // is primarily just one image per slot, but there is also special cases:
+    //  - After freeBuffer, we must still keep the current image/buffer
+    // Reference counting RE::Images lets us handle all these cases easily while
+    // also only creating new RE::Images from buffers when required.
+    class Image : public LightRefBase<Image> {
+    public:
+        Image(sp<GraphicBuffer> graphicBuffer, const RenderEngine& engine);
+
+        Image(const Image& rhs) = delete;
+        Image& operator=(const Image& rhs) = delete;
+
+        // createIfNeeded creates an RE::Image if required (we haven't created
+        // one yet, or the crop-rect has changed).
+        status_t createIfNeeded(const Rect& imageCrop);
+
+        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+        const native_handle* graphicBufferHandle() {
+            return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
+        }
+
+        const RE::Image& image() const { return mImage; }
+
+    private:
+        // Only allow instantiation using ref counting.
+        friend class LightRefBase<Image>;
+        virtual ~Image() = default;
+
+        // mGraphicBuffer is the buffer that was used to create this image.
+        sp<GraphicBuffer> mGraphicBuffer;
+
+        // mImage is the image created from mGraphicBuffer.
+        RE::Image mImage;
+        bool mCreated;
+        int32_t mCropWidth;
+        int32_t mCropHeight;
+    };
+
+    // freeBufferLocked frees up the given buffer slot. If the slot has been
+    // initialized this will release the reference to the GraphicBuffer in
+    // that slot and destroy the RE::Image in that slot.  Otherwise it has no
+    // effect.
+    //
+    // This method must be called with mMutex locked.
+    virtual void freeBufferLocked(int slotIndex);
+
+    // IConsumerListener interface
+    void onDisconnect() override;
+    void onSidebandStreamChanged() override;
+    void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+                                  FrameEventHistoryDelta* outDelta) override;
+
+    // computeCurrentTransformMatrixLocked computes the transform matrix for the
+    // current texture.  It uses mCurrentTransform and the current GraphicBuffer
+    // to compute this matrix and stores it in mCurrentTransformMatrix.
+    // mCurrentTextureImage must not be nullptr.
+    void computeCurrentTransformMatrixLocked();
+
+    // doFenceWaitLocked inserts a wait command into the RenderEngine command
+    // stream to ensure that it is safe for future RenderEngine commands to
+    // access the current texture buffer.
+    status_t doFenceWaitLocked() const;
+
+    // syncForReleaseLocked performs the synchronization needed to release the
+    // current slot from RenderEngine.  If needed it will set the current
+    // slot's fence to guard against a producer accessing the buffer before
+    // the outstanding accesses have completed.
+    status_t syncForReleaseLocked();
+
+    // The default consumer usage flags that BufferLayerConsumer always sets on its
+    // BufferQueue instance; these will be OR:d with any additional flags passed
+    // from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always
+    // consume buffers as hardware textures.
+    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+    // mCurrentTextureImage is the Image/buffer of the current texture. It's
+    // possible that this buffer is not associated with any buffer slot, so we
+    // must track it separately in order to support the getCurrentBuffer method.
+    sp<Image> mCurrentTextureImage;
+
+    // mCurrentCrop is the crop rectangle that applies to the current texture.
+    // It gets set each time updateTexImage is called.
+    Rect mCurrentCrop;
+
+    // mCurrentTransform is the transform identifier for the current texture. It
+    // gets set each time updateTexImage is called.
+    uint32_t mCurrentTransform;
+
+    // mCurrentScalingMode is the scaling mode for the current texture. It gets
+    // set each time updateTexImage is called.
+    uint32_t mCurrentScalingMode;
+
+    // mCurrentFence is the fence received from BufferQueue in updateTexImage.
+    sp<Fence> mCurrentFence;
+
+    // The FenceTime wrapper around mCurrentFence.
+    std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
+    // mCurrentTransformMatrix is the transform matrix for the current texture.
+    // It gets computed by computeTransformMatrix each time updateTexImage is
+    // called.
+    float mCurrentTransformMatrix[16];
+
+    // mCurrentTimestamp is the timestamp for the current texture. It
+    // gets set each time updateTexImage is called.
+    int64_t mCurrentTimestamp;
+
+    // mCurrentDataSpace is the dataspace for the current texture. It
+    // gets set each time updateTexImage is called.
+    android_dataspace mCurrentDataSpace;
+
+    // mCurrentHdrMetadata is the HDR metadata for the current texture. It
+    // gets set each time updateTexImage is called.
+    HdrMetadata mCurrentHdrMetadata;
+
+    // mCurrentFrameNumber is the frame counter for the current texture.
+    // It gets set each time updateTexImage is called.
+    uint64_t mCurrentFrameNumber;
+
+    // Indicates this buffer must be transformed by the inverse transform of the screen
+    // it is displayed onto. This is applied after BufferLayerConsumer::mCurrentTransform.
+    // This must be set/read from SurfaceFlinger's main thread.
+    bool mCurrentTransformToDisplayInverse;
+
+    // The portion of this surface that has changed since the previous frame
+    Region mCurrentSurfaceDamage;
+
+    uint32_t mDefaultWidth, mDefaultHeight;
+
+    // mFilteringEnabled indicates whether the transform matrix is computed for
+    // use with bilinear filtering. It defaults to true and is changed by
+    // setFilteringEnabled().
+    bool mFilteringEnabled;
+
+    RenderEngine& mRE;
+
+    // mTexName is the name of the RenderEngine texture to which streamed
+    // images will be bound when bindTexImage is called. It is set at
+    // construction time.
+    const uint32_t mTexName;
+
+    // The layer for this BufferLayerConsumer
+    const wp<Layer> mLayer;
+
+    wp<ContentsChangedListener> mContentsChangedListener;
+
+    // mImages stores the buffers that have been allocated by the BufferQueue
+    // for each buffer slot.  It is initialized to null pointers, and gets
+    // filled in with the result of BufferQueue::acquire when the
+    // client dequeues a buffer from a
+    // slot that has not yet been used. The buffer allocated to a slot will also
+    // be replaced if the requested buffer usage or geometry differs from that
+    // of the buffer allocated to a slot.
+    sp<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+    // mCurrentTexture is the buffer slot index of the buffer that is currently
+    // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
+    // indicating that no buffer slot is currently bound to the texture. Note,
+    // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+    // that no buffer is bound to the texture. A call to setBufferCount will
+    // reset mCurrentTexture to INVALID_BUFFER_SLOT.
+    int mCurrentTexture;
+
+    // A release that is pending on the receipt of a new release fence from
+    // presentDisplay
+    PendingRelease mPendingRelease;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_BUFFERLAYERCONSUMER_H
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index ea6541a..a69940a 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -206,7 +206,7 @@
 
 status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
     sp<Layer> layer = getLayerUser(handle);
-    if (layer == NULL) {
+    if (layer == nullptr) {
         return NAME_NOT_FOUND;
     }
     layer->clearFrameStats();
@@ -215,7 +215,7 @@
 
 status_t Client::getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const {
     sp<Layer> layer = getLayerUser(handle);
-    if (layer == NULL) {
+    if (layer == nullptr) {
         return NAME_NOT_FOUND;
     }
     layer->getFrameStats(outStats);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 2753f11..d121a86 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -127,20 +127,6 @@
     mPowerMode = (mType >= DisplayDevice::DISPLAY_VIRTUAL) ?
                   HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
 
-    // Name the display.  The name will be replaced shortly if the display
-    // was created with createDisplay().
-    switch (mType) {
-        case DISPLAY_PRIMARY:
-            mDisplayName = "Built-in Screen";
-            break;
-        case DISPLAY_EXTERNAL:
-            mDisplayName = "HDMI Screen";
-            break;
-        default:
-            mDisplayName = "Virtual Screen";    // e.g. Overlay #n
-            break;
-    }
-
     // initialize the display orientation transform.
     setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
 
@@ -160,7 +146,7 @@
 }
 
 bool DisplayDevice::isValid() const {
-    return mFlinger != NULL;
+    return mFlinger != nullptr;
 }
 
 int DisplayDevice::getWidth() const {
@@ -217,7 +203,7 @@
 }
 
 void DisplayDevice::swapBuffers(HWComposer& hwc) const {
-    if (hwc.hasClientComposition(mHwcDisplayId)) {
+    if (hwc.hasClientComposition(mHwcDisplayId) || hwc.hasFlipClientTargetRequest(mHwcDisplayId)) {
         mSurface.swapBuffers();
     }
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 8b5d4c9..4faba3b 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -76,6 +76,10 @@
             SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
 }
 
+void FramebufferSurface::resizeBuffers(const uint32_t width, const uint32_t height) {
+    mConsumer->setDefaultBufferSize(width, height);
+}
+
 status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 4186b7a..ed756c4 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -46,9 +46,7 @@
     virtual void onFrameCommitted();
     virtual void dumpAsString(String8& result) const;
 
-    // Cannot resize a buffers in a FramebufferSurface. Only works with virtual
-    // displays.
-    virtual void resizeBuffers(const uint32_t /*w*/, const uint32_t /*h*/) { };
+    virtual void resizeBuffers(const uint32_t width, const uint32_t height);
 
     virtual const sp<Fence>& getClientTargetAcquireFence() const override;
 
@@ -80,7 +78,7 @@
     // on/off.
     android_dataspace mDataSpace;
 
-    // mCurrentBuffer is the current buffer or NULL to indicate that there is
+    // mCurrentBuffer is the current buffer or nullptr to indicate that there is
     // no current buffer.
     sp<GraphicBuffer> mCurrentBuffer;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index ab4a4b2..070b691 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -52,24 +52,13 @@
 class ComposerCallbackBridge : public Hwc2::IComposerCallback {
 public:
     ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
-            : mCallback(callback), mSequenceId(sequenceId),
-              mHasPrimaryDisplay(false) {}
+            : mCallback(callback), mSequenceId(sequenceId) {}
 
     Return<void> onHotplug(Hwc2::Display display,
                            IComposerCallback::Connection conn) override
     {
         HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
-        if (!mHasPrimaryDisplay) {
-            LOG_ALWAYS_FATAL_IF(connection != HWC2::Connection::Connected,
-                    "Initial onHotplug callback should be "
-                    "primary display connected");
-            mHasPrimaryDisplay = true;
-            mCallback->onHotplugReceived(mSequenceId, display,
-                                         connection, true);
-        } else {
-            mCallback->onHotplugReceived(mSequenceId, display,
-                                         connection, false);
-        }
+        mCallback->onHotplugReceived(mSequenceId, display, connection);
         return Void();
     }
 
@@ -85,12 +74,9 @@
         return Void();
     }
 
-    bool HasPrimaryDisplay() { return mHasPrimaryDisplay; }
-
 private:
     ComposerCallback* mCallback;
     int32_t mSequenceId;
-    bool mHasPrimaryDisplay;
 };
 
 } // namespace anonymous
@@ -117,8 +103,6 @@
     sp<ComposerCallbackBridge> callbackBridge(
             new ComposerCallbackBridge(callback, sequenceId));
     mComposer->registerCallback(callbackBridge);
-    LOG_ALWAYS_FATAL_IF(!callbackBridge->HasPrimaryDisplay(),
-            "Registered composer callback but didn't get primary display");
 }
 
 // Required by HWC2 device
@@ -467,7 +451,6 @@
 Error Display::getHdrCapabilities(
         std::unique_ptr<HdrCapabilities>* outCapabilities) const
 {
-    uint32_t numTypes = 0;
     float maxLuminance = -1.0f;
     float maxAverageLuminance = -1.0f;
     float minLuminance = -1.0f;
@@ -480,7 +463,6 @@
     for (auto type : intTypes) {
         types.push_back(static_cast<int32_t>(type));
     }
-    numTypes = types.size();
     if (error != Error::None) {
         return error;
     }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index a15c6d9..7b98b3e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -65,8 +65,7 @@
 class ComposerCallback {
  public:
     virtual void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
-                                   Connection connection,
-                                   bool primaryDisplay) = 0;
+                                   Connection connection) = 0;
     virtual void onRefreshReceived(int32_t sequenceId,
                                    hwc2_display_t display) = 0;
     virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 60b85c5..7eb3998 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -119,23 +119,22 @@
     }
 }
 
-void HWComposer::onHotplug(hwc2_display_t displayId,
+void HWComposer::onHotplug(hwc2_display_t displayId, int32_t displayType,
                            HWC2::Connection connection) {
-    ALOGV("hotplug: %" PRIu64 ", %s", displayId,
+    if (displayType >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
+        ALOGE("Invalid display type of %d", displayType);
+        return;
+    }
+
+    ALOGV("hotplug: %" PRIu64 ", %s %s", displayId,
+            displayType == DisplayDevice::DISPLAY_PRIMARY ? "primary" : "external",
             to_string(connection).c_str());
     mHwcDevice->onHotplug(displayId, connection);
-    if (!mDisplayData[0].hwcDisplay) {
-        ALOGE_IF(connection != HWC2::Connection::Connected, "Assumed primary"
-                " display would be connected");
-        mDisplayData[0].hwcDisplay = mHwcDevice->getDisplayById(displayId);
-        mHwcDisplaySlots[displayId] = 0;
-    } else {
-        // Disconnect is handled through HWComposer::disconnectDisplay via
-        // SurfaceFlinger's onHotplugReceived callback handling
-        if (connection == HWC2::Connection::Connected) {
-            mDisplayData[1].hwcDisplay = mHwcDevice->getDisplayById(displayId);
-            mHwcDisplaySlots[displayId] = 1;
-        }
+    // Disconnect is handled through HWComposer::disconnectDisplay via
+    // SurfaceFlinger's onHotplugReceived callback handling
+    if (connection == HWC2::Connection::Connected) {
+        mDisplayData[displayType].hwcDisplay = mHwcDevice->getDisplayById(displayId);
+        mHwcDisplaySlots[displayId] = displayType;
     }
 }
 
@@ -444,10 +443,16 @@
 
     HWC2::Error error = HWC2::Error::None;
 
-    // First try to skip validate altogether if the HWC supports it.
+    // First try to skip validate altogether when there is no client
+    // composition.  When there is client composition, since we haven't
+    // rendered to the client target yet, we should not attempt to skip
+    // validate.
+    //
+    // displayData.hasClientComposition hasn't been updated for this frame.
+    // The check below is incorrect.  We actually rely on HWC here to fall
+    // back to validate when there is any client layer.
     displayData.validateWasSkipped = false;
-    if (hasCapability(HWC2::Capability::SkipValidate) &&
-            !displayData.hasClientComposition) {
+    if (!displayData.hasClientComposition) {
         sp<android::Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
         error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
@@ -559,6 +564,20 @@
     return mDisplayData[displayId].hasDeviceComposition;
 }
 
+bool HWComposer::hasFlipClientTargetRequest(int32_t displayId) const {
+    if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
+        // Displays without a corresponding HWC display are never composed by
+        // the device
+        return false;
+    }
+    if (!isValidDisplay(displayId)) {
+        ALOGE("hasFlipClientTargetRequest: Invalid display %d", displayId);
+        return false;
+    }
+    return ((static_cast<uint32_t>(mDisplayData[displayId].displayRequests) &
+             static_cast<uint32_t>(HWC2::DisplayRequest::FlipClientTarget)) != 0);
+}
+
 bool HWComposer::hasClientComposition(int32_t displayId) const {
     if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
         // Displays without a corresponding HWC display are always composed by
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 74b3a38..2e4f5d4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -108,6 +108,9 @@
     // does this display have layers handled by HWC
     bool hasDeviceComposition(int32_t displayId) const;
 
+    // does this display have pending request to flip client target
+    bool hasFlipClientTargetRequest(int32_t displayId) const;
+
     // does this display have layers handled by GLES
     bool hasClientComposition(int32_t displayId) const;
 
@@ -136,7 +139,7 @@
     // DisplayDevice::DisplayType of the display is returned as an output param.
     bool onVsync(hwc2_display_t displayId, int64_t timestamp,
                  int32_t* outDisplay);
-    void onHotplug(hwc2_display_t displayId, HWC2::Connection connection);
+    void onHotplug(hwc2_display_t displayId, int32_t displayType, HWC2::Connection connection);
 
     void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
index 4bc63bb..fe7944f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
@@ -190,7 +190,7 @@
         virtual status_t setLayer(size_t index) = 0;
         virtual HWCLayer* dup() = 0;
         static HWCLayer* copy(HWCLayer *rhs) {
-            return rhs ? rhs->dup() : NULL;
+            return rhs ? rhs->dup() : nullptr;
         }
     protected:
         virtual ~HWCLayer() { }
@@ -205,7 +205,7 @@
         HWCLayer* const mLayerList;
         size_t mIndex;
 
-        LayerListIterator() : mLayerList(NULL), mIndex(0) { }
+        LayerListIterator() : mLayerList(nullptr), mIndex(0) { }
 
         LayerListIterator(HWCLayer* layer, size_t index)
             : mLayerList(layer), mIndex(index) { }
@@ -371,8 +371,8 @@
     sp<SurfaceFlinger>              mFlinger;
     framebuffer_device_t*           mFbDev;
     struct hwc_composer_device_1*   mHwc;
-    // invariant: mLists[0] != NULL iff mHwc != NULL
-    // mLists[i>0] can be NULL. that display is to be ignored
+    // invariant: mLists[0] != nullptr iff mHwc != nullptr
+    // mLists[i>0] can be nullptr. that display is to be ignored
     struct hwc_display_contents_1*  mLists[MAX_HWC_DISPLAYS];
     DisplayData                     mDisplayData[MAX_HWC_DISPLAYS];
     // protect mDisplayData from races between prepare and dump
diff --git a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
deleted file mode 100644
index a6f076e..0000000
--- a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2012 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/hardware/power/1.0/IPower.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <android/log.h>
-#include <utils/Errors.h>
-
-#include <binder/IServiceManager.h>
-#include <powermanager/IPowerManager.h>
-#include <powermanager/PowerManager.h>
-
-#include "PowerHAL.h"
-
-using android::hardware::power::V1_0::PowerHint;
-namespace android {
-// ---------------------------------------------------------------------------
-
-status_t PowerHAL::vsyncHint(bool enabled) {
-    Mutex::Autolock _l(mlock);
-    if (mPowerManager == NULL) {
-        const String16 serviceName("power");
-        sp<IBinder> bs = defaultServiceManager()->checkService(serviceName);
-        if (bs == NULL) {
-            return NAME_NOT_FOUND;
-        }
-        mPowerManager = interface_cast<IPowerManager>(bs);
-    }
-    status_t status;
-    status = mPowerManager->powerHint(static_cast<int>(PowerHint::VSYNC),
-            enabled ? 1 : 0);
-    if(status == DEAD_OBJECT) {
-        mPowerManager = NULL;
-    }
-    return status;
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
diff --git a/services/surfaceflinger/DisplayHardware/PowerHAL.h b/services/surfaceflinger/DisplayHardware/PowerHAL.h
deleted file mode 100644
index e5f82a9..0000000
--- a/services/surfaceflinger/DisplayHardware/PowerHAL.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012 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_SF_POWER_HAL_H
-#define ANDROID_SF_POWER_HAL_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Mutex.h>
-
-#include <powermanager/IPowerManager.h>
-#include <hardware/power.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class PowerHAL
-{
-public:
-    status_t vsyncHint(bool enabled);
-
-private:
-    sp<IPowerManager> mPowerManager;
-    Mutex mlock;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_POWER_HAL_H
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index cde96e4..3480b24 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -108,7 +108,7 @@
     mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight);
     sink->setAsyncMode(true);
     IGraphicBufferProducer::QueueBufferOutput output;
-    mSource[SOURCE_SCRATCH]->connect(NULL, NATIVE_WINDOW_API_EGL, false, &output);
+    mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output);
 }
 
 VirtualDisplaySurface::~VirtualDisplaySurface() {
@@ -203,7 +203,7 @@
     }
 
     sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
-            mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(NULL);
+            mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
     sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
     VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)",
             mFbProducerSlot, fbBuffer.get(),
@@ -214,7 +214,7 @@
     mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
 
     status_t result = NO_ERROR;
-    if (fbBuffer != NULL) {
+    if (fbBuffer != nullptr) {
         uint32_t hwcSlot = 0;
         sp<GraphicBuffer> hwcBuffer;
         mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer,
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index f647742..5c0e3b3 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -34,14 +34,6 @@
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
-// time to wait between VSYNC requests before sending a VSYNC OFF power hint: 40msec.
-const long vsyncHintOffDelay = 40000000;
-
-static void vsyncOffCallback(union sigval val) {
-    EventThread *ev = (EventThread *)val.sival_ptr;
-    ev->sendVsyncHintOff();
-    return;
-}
 
 EventThread::EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger, bool interceptVSyncs)
     : mVSyncSource(src),
@@ -49,7 +41,6 @@
       mUseSoftwareVSync(false),
       mVsyncEnabled(false),
       mDebugVsyncEnabled(false),
-      mVsyncHintSent(false),
       mInterceptVSyncs(interceptVSyncs) {
 
     for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
@@ -58,18 +49,6 @@
         mVSyncEvent[i].header.timestamp = 0;
         mVSyncEvent[i].vsync.count =  0;
     }
-    struct sigevent se;
-    se.sigev_notify = SIGEV_THREAD;
-    se.sigev_value.sival_ptr = this;
-    se.sigev_notify_function = vsyncOffCallback;
-    se.sigev_notify_attributes = NULL;
-    timer_create(CLOCK_MONOTONIC, &se, &mTimerId);
-}
-
-void EventThread::sendVsyncHintOff() {
-    Mutex::Autolock _l(mLock);
-    mPowerHAL.vsyncHint(false);
-    mVsyncHintSent = false;
 }
 
 void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
@@ -77,19 +56,6 @@
     mVSyncSource->setPhaseOffset(phaseOffset);
 }
 
-void EventThread::sendVsyncHintOnLocked() {
-    struct itimerspec ts;
-    if(!mVsyncHintSent) {
-        mPowerHAL.vsyncHint(true);
-        mVsyncHintSent = true;
-    }
-    ts.it_value.tv_sec = 0;
-    ts.it_value.tv_nsec = vsyncHintOffDelay;
-    ts.it_interval.tv_sec = 0;
-    ts.it_interval.tv_nsec = 0;
-    timer_settime(mTimerId, 0, &ts, NULL);
-}
-
 void EventThread::onFirstRef() {
     run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
 }
@@ -250,7 +216,7 @@
         size_t count = mDisplayEventConnections.size();
         for (size_t i=0 ; i<count ; ) {
             sp<Connection> connection(mDisplayEventConnections[i].promote());
-            if (connection != NULL) {
+            if (connection != nullptr) {
                 bool added = false;
                 if (connection->count >= 0) {
                     // we need vsync events because at least
@@ -357,7 +323,6 @@
         }
     }
     mDebugVsyncEnabled = true;
-    sendVsyncHintOnLocked();
 }
 
 void EventThread::disableVSyncLocked() {
@@ -381,7 +346,7 @@
         sp<Connection> connection =
                 mDisplayEventConnections.itemAt(i).promote();
         result.appendFormat("    %p: count=%d\n",
-                connection.get(), connection!=NULL ? connection->count : 0);
+                connection.get(), connection != nullptr ? connection->count : 0);
     }
 }
 
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 6a59fbb..0823839 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -29,7 +29,6 @@
 #include <utils/SortedVector.h>
 
 #include "DisplayDevice.h"
-#include "DisplayHardware/PowerHAL.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -99,7 +98,6 @@
             DisplayEventReceiver::Event* event);
 
     void dump(String8& result) const;
-    void sendVsyncHintOff();
 
     void setPhaseOffset(nsecs_t phaseOffset);
 
@@ -112,11 +110,9 @@
     void removeDisplayEventConnection(const wp<Connection>& connection);
     void enableVSyncLocked();
     void disableVSyncLocked();
-    void sendVsyncHintOnLocked();
 
     // constants
     sp<VSyncSource> mVSyncSource;
-    PowerHAL mPowerHAL;
     SurfaceFlinger& mFlinger;
 
     mutable Mutex mLock;
@@ -132,9 +128,7 @@
     // for debugging
     bool mDebugVsyncEnabled;
 
-    bool mVsyncHintSent;
     const bool mInterceptVSyncs;
-    timer_t mTimerId;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 99c4daa..1539873 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -82,17 +82,17 @@
     mFrameRecords[mOffset].frameReadyTime = INT64_MAX;
     mFrameRecords[mOffset].actualPresentTime = INT64_MAX;
 
-    if (mFrameRecords[mOffset].frameReadyFence != NULL) {
+    if (mFrameRecords[mOffset].frameReadyFence != nullptr) {
         // We're clobbering an unsignaled fence, so we need to decrement the
         // fence count.
-        mFrameRecords[mOffset].frameReadyFence = NULL;
+        mFrameRecords[mOffset].frameReadyFence = nullptr;
         mNumFences--;
     }
 
-    if (mFrameRecords[mOffset].actualPresentFence != NULL) {
+    if (mFrameRecords[mOffset].actualPresentFence != nullptr) {
         // We're clobbering an unsignaled fence, so we need to decrement the
         // fence count.
-        mFrameRecords[mOffset].actualPresentFence = NULL;
+        mFrameRecords[mOffset].actualPresentFence = nullptr;
         mNumFences--;
     }
 }
@@ -153,10 +153,10 @@
         bool updated = false;
 
         const std::shared_ptr<FenceTime>& rfence = records[idx].frameReadyFence;
-        if (rfence != NULL) {
+        if (rfence != nullptr) {
             records[idx].frameReadyTime = rfence->getSignalTime();
             if (records[idx].frameReadyTime < INT64_MAX) {
-                records[idx].frameReadyFence = NULL;
+                records[idx].frameReadyFence = nullptr;
                 numFences--;
                 updated = true;
             }
@@ -164,10 +164,10 @@
 
         const std::shared_ptr<FenceTime>& pfence =
                 records[idx].actualPresentFence;
-        if (pfence != NULL) {
+        if (pfence != nullptr) {
             records[idx].actualPresentTime = pfence->getSignalTime();
             if (records[idx].actualPresentTime < INT64_MAX) {
-                records[idx].actualPresentFence = NULL;
+                records[idx].actualPresentFence = nullptr;
                 numFences--;
                 updated = true;
             }
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index adcdfb5..b4a9fd6 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -35,7 +35,7 @@
 // possible.
 //
 // Some of the time values tracked may be set either as a specific timestamp
-// or a fence.  When a non-NULL fence is set for a given time value, the
+// or a fence.  When a non-nullptr fence is set for a given time value, the
 // signal time of that fence is used instead of the timestamp.
 class FrameTracker {
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 13df1e2..067a09f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -265,7 +265,7 @@
     if (!mCurrentCrop.isEmpty()) {
         // if the buffer crop is defined, we use that
         crop = mCurrentCrop;
-    } else if (getBE().compositionInfo.mBuffer != NULL) {
+    } else if (getBE().compositionInfo.mBuffer != nullptr) {
         // otherwise we use the whole buffer
         crop = getBE().compositionInfo.mBuffer->getBounds();
     } else {
@@ -285,6 +285,14 @@
     return Region(win).subtract(exclude).getBounds();
 }
 
+static FloatRect reduce(const FloatRect& win, const Region& exclude) {
+    if (CC_LIKELY(exclude.isEmpty())) {
+        return win;
+    }
+    // Convert through Rect (by rounding) for lack of FloatRegion
+    return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
+}
+
 Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
     const Layer::State& s(getDrawingState());
     Rect win(s.active.w, s.active.h);
@@ -323,12 +331,12 @@
     return win;
 }
 
-Rect Layer::computeBounds() const {
+FloatRect Layer::computeBounds() const {
     const Layer::State& s(getDrawingState());
     return computeBounds(s.activeTransparentRegion);
 }
 
-Rect Layer::computeBounds(const Region& activeTransparentRegion) const {
+FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const {
     const Layer::State& s(getDrawingState());
     Rect win(s.active.w, s.active.h);
 
@@ -345,14 +353,16 @@
     }
 
     Transform t = getTransform();
+
+    FloatRect floatWin = win.toFloatRect();
     if (p != nullptr) {
-        win = t.transform(win);
-        win.intersect(bounds, &win);
-        win = t.inverse().transform(win);
+        floatWin = t.transform(floatWin);
+        floatWin = floatWin.intersect(bounds.toFloatRect());
+        floatWin = t.inverse().transform(floatWin);
     }
 
     // subtract the transparent region and snap to the bounds
-    return reduce(win, activeTransparentRegion);
+    return reduce(floatWin, activeTransparentRegion);
 }
 
 Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const {
@@ -528,7 +538,9 @@
                 Rect(activeCrop.right, activeCrop.top, s.active.w, activeCrop.bottom));
     }
 
-    Rect frame(t.transform(computeBounds(activeTransparentRegion)));
+    // computeBounds returns a FloatRect to provide more accuracy during the
+    // transformation. We then round upon constructing 'frame'.
+    Rect frame{t.transform(computeBounds(activeTransparentRegion))};
     if (!s.finalCrop.isEmpty()) {
         if (!frame.intersect(s.finalCrop, &frame)) {
             frame.clear();
@@ -614,9 +626,15 @@
         transform = Transform(invTransform) * tr * bufferOrientation;
     }
 
+    // STOPSHIP (b/72106793): If we have less than 25% scaling, HWC usually needs to use the rotator
+    // to handle it. However, there is one guaranteed frame of jank when we switch to using the
+    // rotator. In the meantime, we force GL composition instead until we have a better fix for the
+    // HWC issue.
+    bool extremeScaling = abs(t[0][0]) <= 0.25 || abs(t[1][1]) <= 0.25;
+
     // this gives us only the "orientation" component of the transform
     const uint32_t orientation = transform.getOrientation();
-    if (orientation & Transform::ROT_INVALID) {
+    if (orientation & Transform::ROT_INVALID || extremeScaling) {
         // we can only handle simple transformation
         hwcInfo.forceClientComposition = true;
     } else {
@@ -807,7 +825,7 @@
     const Layer::State& s(getDrawingState());
     const Transform renderAreaTransform(renderArea.getTransform());
     const uint32_t height = renderArea.getHeight();
-    Rect win = computeBounds();
+    FloatRect win = computeBounds();
 
     vec2 lt = vec2(win.left, win.top);
     vec2 lb = vec2(win.left, win.bottom);
@@ -859,6 +877,12 @@
     this->visibleNonTransparentRegion = setVisibleNonTransparentRegion;
 }
 
+void Layer::clearVisibilityRegions() {
+    visibleRegion.clear();
+    visibleNonTransparentRegion.clear();
+    coveredRegion.clear();
+}
+
 // ----------------------------------------------------------------------------
 // transaction
 // ----------------------------------------------------------------------------
@@ -1117,8 +1141,9 @@
     if (childLayer->setLayer(z)) {
         mCurrentChildren.removeAt(idx);
         mCurrentChildren.add(childLayer);
+        return true;
     }
-    return true;
+    return false;
 }
 
 bool Layer::setChildRelativeLayer(const sp<Layer>& childLayer,
@@ -1130,12 +1155,13 @@
     if (childLayer->setRelativeLayer(relativeToHandle, relativeZ)) {
         mCurrentChildren.removeAt(idx);
         mCurrentChildren.add(childLayer);
+        return true;
     }
-    return true;
+    return false;
 }
 
 bool Layer::setLayer(int32_t z) {
-    if (mCurrentState.z == z) return false;
+    if (mCurrentState.z == z && !usingRelativeZ(LayerVector::StateSet::Current)) return false;
     mCurrentState.sequence++;
     mCurrentState.z = z;
     mCurrentState.modified = true;
@@ -1166,7 +1192,7 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
-bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t z) {
+bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
     sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get());
     if (handle == nullptr) {
         return false;
@@ -1176,9 +1202,14 @@
         return false;
     }
 
+    if (mCurrentState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) &&
+            mCurrentState.zOrderRelativeOf == relative) {
+        return false;
+    }
+
     mCurrentState.sequence++;
     mCurrentState.modified = true;
-    mCurrentState.z = z;
+    mCurrentState.z = relativeZ;
 
     auto oldZOrderRelativeOf = mCurrentState.zOrderRelativeOf.promote();
     if (oldZOrderRelativeOf != nullptr) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index cf7fc50..c63399e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -24,6 +24,7 @@
 #include <utils/String8.h>
 #include <utils/Timers.h>
 
+#include <ui/FloatRect.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
@@ -303,8 +304,8 @@
     }
 
     void computeGeometry(const RenderArea& renderArea, Mesh& mesh, bool useIdentityTransform) const;
-    Rect computeBounds(const Region& activeTransparentRegion) const;
-    Rect computeBounds() const;
+    FloatRect computeBounds(const Region& activeTransparentRegion) const;
+    FloatRect computeBounds() const;
 
     int32_t getSequence() const { return sequence; }
 
@@ -434,6 +435,11 @@
     void setVisibleNonTransparentRegion(const Region& visibleNonTransparentRegion);
 
     /*
+     * Clear the visible, covered, and non-transparent regions.
+     */
+    void clearVisibilityRegions();
+
+    /*
      * latchBuffer - called each time the screen is redrawn and returns whether
      * the visible regions need to be recomputed (this is a fairly heavy
      * operation, so this should be set only if needed). Typically this is used
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index e864607..a5f0b98 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -41,7 +41,7 @@
     mFreezeGeometryUpdates(freezePositionUpdates) {}
 
 bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
-    if (buf == NULL) {
+    if (buf == nullptr) {
         return false;
     }
 
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
index 828cd8b..40972aa 100644
--- a/services/surfaceflinger/LayerRejecter.h
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -18,10 +18,10 @@
 #define ANDROID_LAYER_REJECTER_H
 
 #include "Layer.h"
-#include "SurfaceFlingerConsumer.h"
+#include "BufferLayerConsumer.h"
 
 namespace android {
-    class LayerRejecter : public SurfaceFlingerConsumer::BufferRejecter {
+    class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
     public:
         LayerRejecter(Layer::State &front,
                       Layer::State &current,
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
index e014406..1b5a466 100644
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ b/services/surfaceflinger/RenderEngine/Description.cpp
@@ -26,15 +26,6 @@
 
 namespace android {
 
-Description::Description() {
-    mPremultipliedAlpha = false;
-    mOpaque = true;
-    mTextureEnabled = false;
-    mColorMatrixEnabled = false;
-}
-
-Description::~Description() {}
-
 void Description::setPremultipliedAlpha(bool premultipliedAlpha) {
     mPremultipliedAlpha = premultipliedAlpha;
 }
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
index cbac855..1811952 100644
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ b/services/surfaceflinger/RenderEngine/Description.h
@@ -36,27 +36,27 @@
     friend class ProgramCache;
 
     // whether textures are premultiplied
-    bool mPremultipliedAlpha;
+    bool mPremultipliedAlpha = false;
     // whether this layer is marked as opaque
-    bool mOpaque;
+    bool mOpaque = true;
 
     // Texture this layer uses
     Texture mTexture;
-    bool mTextureEnabled;
+    bool mTextureEnabled = false;
 
     // color used when texturing is disabled or when setting alpha.
     half4 mColor;
     // projection matrix
     mat4 mProjectionMatrix;
 
-    bool mColorMatrixEnabled;
+    bool mColorMatrixEnabled = false;
     mat4 mColorMatrix;
 
-    bool mIsWideGamut;
+    bool mIsWideGamut = false;
 
 public:
-    Description();
-    ~Description();
+    Description() = default;
+    ~Description() = default;
 
     void setPremultipliedAlpha(bool premultipliedAlpha);
     void setOpaque(bool opaque);
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 3161888..d1ee6f8 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -154,12 +154,12 @@
 void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
                                                   size_t hwh, bool yswap,
                                                   Transform::orientation_flags rotation) {
-    size_t l = sourceCrop.left;
-    size_t r = sourceCrop.right;
+    int32_t l = sourceCrop.left;
+    int32_t r = sourceCrop.right;
 
     // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
-    size_t t = hwh - sourceCrop.top;
-    size_t b = hwh - sourceCrop.bottom;
+    int32_t t = hwh - sourceCrop.top;
+    int32_t b = hwh - sourceCrop.bottom;
 
     mat4 m;
     if (yswap) {
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.cpp b/services/surfaceflinger/RenderEngine/GLExtensions.cpp
index 7ffcc96..dc09a37 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.cpp
+++ b/services/surfaceflinger/RenderEngine/GLExtensions.cpp
@@ -25,7 +25,22 @@
 
 ANDROID_SINGLETON_STATIC_INSTANCE(GLExtensions)
 
-GLExtensions::GLExtensions() : mHaveFramebufferObject(false) {}
+SortedVector<String8> GLExtensions::parseExtensionString(char const* extensions) {
+    SortedVector<String8> list;
+
+    char const* curr = extensions;
+    char const* head = curr;
+    do {
+        head = strchr(curr, ' ');
+        String8 s(curr, head ? head - curr : strlen(curr));
+        if (s.length()) {
+            list.add(s);
+        }
+        curr = head + 1;
+    } while (head);
+
+    return list;
+}
 
 void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer,
                                      GLubyte const* version, GLubyte const* extensions) {
@@ -33,21 +48,7 @@
     mRenderer = (char const*)renderer;
     mVersion = (char const*)version;
     mExtensions = (char const*)extensions;
-
-    char const* curr = (char const*)extensions;
-    char const* head = curr;
-    do {
-        head = strchr(curr, ' ');
-        String8 s(curr, head ? head - curr : strlen(curr));
-        if (s.length()) {
-            mExtensionList.add(s);
-        }
-        curr = head + 1;
-    } while (head);
-
-    if (hasExtension("GL_OES_framebuffer_object")) {
-        mHaveFramebufferObject = true;
-    }
+    mExtensionList = parseExtensionString(mExtensions);
 }
 
 bool GLExtensions::hasExtension(char const* extension) const {
@@ -67,9 +68,61 @@
     return mVersion.string();
 }
 
-char const* GLExtensions::getExtension() const {
+char const* GLExtensions::getExtensions() const {
     return mExtensions.string();
 }
 
+void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) {
+    mEGLVersion = eglVersion;
+    mEGLExtensions = eglExtensions;
+    mEGLExtensionList = parseExtensionString(mEGLExtensions);
+
+    // EGL_ANDROIDX_no_config_context is an experimental extension with no
+    // written specification. It will be replaced by something more formal.
+    // SurfaceFlinger is using it to allow a single EGLContext to render to
+    // both a 16-bit primary display framebuffer and a 32-bit virtual display
+    // framebuffer.
+    //
+    // EGL_KHR_no_config_context is official extension to allow creating a
+    // context that works with any surface of a display.
+    if (hasEGLExtension("EGL_ANDROIDX_no_config_context") ||
+        hasEGLExtension("EGL_KHR_no_config_context")) {
+        mHasNoConfigContext = true;
+    }
+
+    if (hasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+        mHasNativeFenceSync = true;
+    }
+    if (hasEGLExtension("EGL_KHR_fence_sync")) {
+        mHasFenceSync = true;
+    }
+    if (hasEGLExtension("EGL_KHR_wait_sync")) {
+        mHasWaitSync = true;
+    }
+
+    if (hasEGLExtension("EGL_ANDROID_image_crop")) {
+        mHasImageCrop = true;
+    }
+    if (hasEGLExtension("EGL_EXT_protected_content")) {
+        mHasProtectedContent = true;
+    }
+    if (hasEGLExtension("EGL_IMG_context_priority")) {
+        mHasContextPriority = true;
+    }
+}
+
+char const* GLExtensions::getEGLVersion() const {
+    return mEGLVersion.string();
+}
+
+char const* GLExtensions::getEGLExtensions() const {
+    return mEGLExtensions.string();
+}
+
+bool GLExtensions::hasEGLExtension(char const* extension) const {
+    const String8 s(extension);
+    return mEGLExtensionList.indexOf(s) >= 0;
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.h b/services/surfaceflinger/RenderEngine/GLExtensions.h
index ee7b446..0d8c10b 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.h
+++ b/services/surfaceflinger/RenderEngine/GLExtensions.h
@@ -35,7 +35,13 @@
 class GLExtensions : public Singleton<GLExtensions> {
     friend class Singleton<GLExtensions>;
 
-    bool mHaveFramebufferObject : 1;
+    bool mHasNoConfigContext = false;
+    bool mHasNativeFenceSync = false;
+    bool mHasFenceSync = false;
+    bool mHasWaitSync = false;
+    bool mHasImageCrop = false;
+    bool mHasProtectedContent = false;
+    bool mHasContextPriority = false;
 
     String8 mVendor;
     String8 mRenderer;
@@ -43,24 +49,39 @@
     String8 mExtensions;
     SortedVector<String8> mExtensionList;
 
+    String8 mEGLVersion;
+    String8 mEGLExtensions;
+    SortedVector<String8> mEGLExtensionList;
+
+    static SortedVector<String8> parseExtensionString(char const* extensions);
+
     GLExtensions(const GLExtensions&);
     GLExtensions& operator=(const GLExtensions&);
 
 protected:
-    GLExtensions();
+    GLExtensions() = default;
 
 public:
-    inline bool haveFramebufferObject() const { return mHaveFramebufferObject; }
+    bool hasNoConfigContext() const { return mHasNoConfigContext; }
+    bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
+    bool hasFenceSync() const { return mHasFenceSync; }
+    bool hasWaitSync() const { return mHasWaitSync; }
+    bool hasImageCrop() const { return mHasImageCrop; }
+    bool hasProtectedContent() const { return mHasProtectedContent; }
+    bool hasContextPriority() const { return mHasContextPriority; }
 
     void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
                            GLubyte const* extensions);
-
     char const* getVendor() const;
     char const* getRenderer() const;
     char const* getVersion() const;
-    char const* getExtension() const;
-
+    char const* getExtensions() const;
     bool hasExtension(char const* extension) const;
+
+    void initWithEGLStrings(char const* eglVersion, char const* eglExtensions);
+    char const* getEGLVersion() const;
+    char const* getEGLExtensions() const;
+    bool hasEGLExtension(char const* extension) const;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/Image.cpp b/services/surfaceflinger/RenderEngine/Image.cpp
new file mode 100644
index 0000000..1f8e75a
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/Image.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 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 "Image.h"
+
+#include <vector>
+
+#include <log/log.h>
+
+#include "GLExtensions.h"
+#include "RenderEngine.h"
+
+namespace android {
+namespace RE {
+
+Image::Image(const RenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
+
+Image::~Image() {
+    setNativeWindowBuffer(nullptr, false, 0, 0);
+}
+
+static std::vector<EGLint> buildAttributeList(bool isProtected, int32_t cropWidth,
+                                              int32_t cropHeight) {
+    std::vector<EGLint> attrs;
+    attrs.reserve(16);
+
+    attrs.push_back(EGL_IMAGE_PRESERVED_KHR);
+    attrs.push_back(EGL_TRUE);
+
+    if (isProtected && GLExtensions::getInstance().hasProtectedContent()) {
+        attrs.push_back(EGL_PROTECTED_CONTENT_EXT);
+        attrs.push_back(EGL_TRUE);
+    }
+
+    if (cropWidth > 0 && cropHeight > 0) {
+        attrs.push_back(EGL_IMAGE_CROP_LEFT_ANDROID);
+        attrs.push_back(0);
+        attrs.push_back(EGL_IMAGE_CROP_TOP_ANDROID);
+        attrs.push_back(0);
+        attrs.push_back(EGL_IMAGE_CROP_RIGHT_ANDROID);
+        attrs.push_back(cropWidth);
+        attrs.push_back(EGL_IMAGE_CROP_BOTTOM_ANDROID);
+        attrs.push_back(cropHeight);
+    }
+
+    attrs.push_back(EGL_NONE);
+
+    return attrs;
+}
+
+bool Image::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth,
+                                  int32_t cropHeight) {
+    if (mEGLImage != EGL_NO_IMAGE_KHR) {
+        if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
+            ALOGE("failed to destroy image: %#x", eglGetError());
+        }
+        mEGLImage = EGL_NO_IMAGE_KHR;
+    }
+
+    if (buffer) {
+        std::vector<EGLint> attrs = buildAttributeList(isProtected, cropWidth, cropHeight);
+        mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+                                      static_cast<EGLClientBuffer>(buffer), attrs.data());
+        if (mEGLImage == EGL_NO_IMAGE_KHR) {
+            ALOGE("failed to create EGLImage: %#x", eglGetError());
+            return false;
+        }
+    }
+
+    return true;
+}
+
+} // namespace RE
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Image.h b/services/surfaceflinger/RenderEngine/Image.h
new file mode 100644
index 0000000..f55aa59
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/Image.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+
+class RenderEngine;
+
+namespace RE {
+
+class Image {
+public:
+    Image(const RenderEngine& engine);
+    ~Image();
+
+    Image(const Image&) = delete;
+    Image& operator=(const Image&) = delete;
+
+    bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth,
+                               int32_t cropHeight);
+
+private:
+    // methods internal to RenderEngine
+    friend class android::RenderEngine;
+    EGLSurface getEGLImage() const { return mEGLImage; }
+
+    EGLDisplay mEGLDisplay;
+    EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
+};
+
+} // namespace RE
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/services/surfaceflinger/RenderEngine/Program.cpp
index baf92eb..225bcf0 100644
--- a/services/surfaceflinger/RenderEngine/Program.cpp
+++ b/services/surfaceflinger/RenderEngine/Program.cpp
@@ -115,7 +115,7 @@
     GLint l;
     glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &l);
     char* src = new char[l];
-    glGetShaderSource(shader, l, NULL, src);
+    glGetShaderSource(shader, l, nullptr, src);
     result.append(src);
     delete[] src;
     return result;
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 4f138dc..3b8ac0e 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -98,7 +98,7 @@
             continue;
         }
         Program* program = mCache.valueFor(shaderKey);
-        if (program == NULL) {
+        if (program == nullptr) {
             program = generateProgram(shaderKey);
             mCache.add(shaderKey, program);
             shaderCount++;
@@ -273,7 +273,7 @@
 
     // look-up the program in the cache
     Program* program = mCache.valueFor(needs);
-    if (program == NULL) {
+    if (program == nullptr) {
         // we didn't find our program, so generate one...
         nsecs_t time = -systemTime();
         program = generateProgram(needs);
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 883ae26..22016ed 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -20,54 +20,40 @@
 
 #include "GLES20RenderEngine.h"
 #include "GLExtensions.h"
+#include "Image.h"
 #include "Mesh.h"
 #include "RenderEngine.h"
 
 #include <SurfaceFlinger.h>
 #include <vector>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
 extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
 
-static bool findExtension(const char* exts, const char* name) {
-    if (!exts) return false;
-    size_t len = strlen(name);
-
-    const char* pos = exts;
-    while ((pos = strstr(pos, name)) != NULL) {
-        if (pos[len] == '\0' || pos[len] == ' ') return true;
-        pos += len;
-    }
-
-    return false;
-}
-
 std::unique_ptr<RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) {
     // initialize EGL for the default display
     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (!eglInitialize(display, NULL, NULL)) {
+    if (!eglInitialize(display, nullptr, nullptr)) {
         LOG_ALWAYS_FATAL("failed to initialize EGL");
     }
 
-    // EGL_ANDROIDX_no_config_context is an experimental extension with no
-    // written specification. It will be replaced by something more formal.
-    // SurfaceFlinger is using it to allow a single EGLContext to render to
-    // both a 16-bit primary display framebuffer and a 32-bit virtual display
-    // framebuffer.
-    //
-    // EGL_KHR_no_config_context is official extension to allow creating a
-    // context that works with any surface of a display.
-    //
+    GLExtensions& extensions = GLExtensions::getInstance();
+    extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION),
+                                  eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS));
+
     // The code assumes that ES2 or later is available if this extension is
     // supported.
     EGLConfig config = EGL_NO_CONFIG;
-    if (!findExtension(eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS),
-                       "EGL_ANDROIDX_no_config_context") &&
-        !findExtension(eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS),
-                       "EGL_KHR_no_config_context")) {
+    if (!extensions.hasNoConfigContext()) {
         config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
     }
 
@@ -90,16 +76,14 @@
     contextAttributes.reserve(6);
     contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
     contextAttributes.push_back(contextClientVersion);
-#ifdef EGL_IMG_context_priority
-    if (SurfaceFlinger::useContextPriority) {
+    bool useContextPriority = overrideUseContextPriorityFromConfig(extensions.hasContextPriority());
+    if (useContextPriority) {
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
     }
-#endif
-    contextAttributes.push_back(EGL_NONE);
     contextAttributes.push_back(EGL_NONE);
 
-    EGLContext ctxt = eglCreateContext(display, config, NULL, contextAttributes.data());
+    EGLContext ctxt = eglCreateContext(display, config, nullptr, contextAttributes.data());
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -117,7 +101,6 @@
     EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
     LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
 
-    GLExtensions& extensions(GLExtensions::getInstance());
     extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
                                  glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
 
@@ -142,7 +125,7 @@
     ALOGI("vendor    : %s", extensions.getVendor());
     ALOGI("renderer  : %s", extensions.getRenderer());
     ALOGI("version   : %s", extensions.getVersion());
-    ALOGI("extensions: %s", extensions.getExtension());
+    ALOGI("extensions: %s", extensions.getExtensions());
     ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
     ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
 
@@ -152,8 +135,20 @@
     return engine;
 }
 
+bool RenderEngine::overrideUseContextPriorityFromConfig(bool useContextPriority) {
+    OptionalBool ret;
+    ISurfaceFlingerConfigs::getService()->useContextPriority([&ret](OptionalBool b) {
+        ret = b;
+    });
+    if (ret.specified) {
+        return ret.value;
+    } else {
+        return useContextPriority;
+    }
+}
+
 RenderEngine::RenderEngine()
-      : mEGLDisplay(EGL_NO_DISPLAY), mEGLConfig(NULL), mEGLContext(EGL_NO_CONTEXT) {}
+      : mEGLDisplay(EGL_NO_DISPLAY), mEGLConfig(nullptr), mEGLContext(EGL_NO_CONTEXT) {}
 
 RenderEngine::~RenderEngine() {
     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -174,6 +169,14 @@
     return mEGLConfig;
 }
 
+bool RenderEngine::supportsImageCrop() const {
+    return GLExtensions::getInstance().hasImageCrop();
+}
+
+bool RenderEngine::isCurrent() const {
+    return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
+}
+
 bool RenderEngine::setCurrentSurface(const RE::Surface& surface) {
     bool success = true;
     EGLSurface eglSurface = surface.getEGLSurface();
@@ -191,6 +194,88 @@
     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 }
 
+base::unique_fd RenderEngine::flush() {
+    if (!GLExtensions::getInstance().hasNativeFenceSync()) {
+        return base::unique_fd();
+    }
+
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+        return base::unique_fd();
+    }
+
+    // native fence fd will not be populated until flush() is done.
+    glFlush();
+
+    // get the fence fd
+    base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+        ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+    }
+
+    return fenceFd;
+}
+
+bool RenderEngine::finish() {
+    if (!GLExtensions::getInstance().hasFenceSync()) {
+        ALOGW("no synchronization support");
+        return false;
+    }
+
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGW("failed to create EGL fence sync: %#x", eglGetError());
+        return false;
+    }
+
+    EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+                                         2000000000 /*2 sec*/);
+    EGLint error = eglGetError();
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (result != EGL_CONDITION_SATISFIED_KHR) {
+        if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+            ALOGW("fence wait timed out");
+        } else {
+            ALOGW("error waiting on EGL fence: %#x", error);
+        }
+        return false;
+    }
+
+    return true;
+}
+
+bool RenderEngine::waitFence(base::unique_fd fenceFd) {
+    if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+        !GLExtensions::getInstance().hasWaitSync()) {
+        return false;
+    }
+
+    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+        return false;
+    }
+
+    // fenceFd is now owned by EGLSync
+    (void)fenceFd.release();
+
+    // XXX: The spec draft is inconsistent as to whether this should return an
+    // EGLint or void.  Ignore the return value for now, as it's not strictly
+    // needed.
+    eglWaitSyncKHR(mEGLDisplay, sync, 0);
+    EGLint error = eglGetError();
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (error != EGL_SUCCESS) {
+        ALOGE("failed to wait for EGL native fence sync: %#x", error);
+        return false;
+    }
+
+    return true;
+}
+
 void RenderEngine::checkErrors() const {
     do {
         // there could be more than one error flag
@@ -242,52 +327,6 @@
     drawMesh(mesh);
 }
 
-int RenderEngine::flush(bool wait) {
-    // Attempt to create a sync khr object that can produce a sync point. If that
-    // isn't available, create a non-dupable sync object in the fallback path and
-    // wait on it directly.
-    EGLSyncKHR sync;
-    if (!wait) {
-        EGLint syncFd = EGL_NO_NATIVE_FENCE_FD_ANDROID;
-
-        sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
-        if (sync != EGL_NO_SYNC_KHR) {
-            // native fence fd will not be populated until flush() is done.
-            glFlush();
-
-            // get the sync fd
-            syncFd = eglDupNativeFenceFDANDROID(mEGLDisplay, sync);
-            if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
-                ALOGW("failed to dup sync khr object");
-            }
-
-            eglDestroySyncKHR(mEGLDisplay, sync);
-        }
-
-        if (syncFd != EGL_NO_NATIVE_FENCE_FD_ANDROID) {
-            return syncFd;
-        }
-    }
-
-    // fallback or explicit wait
-    sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
-    if (sync != EGL_NO_SYNC_KHR) {
-        EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
-                                             2000000000 /*2 sec*/);
-        EGLint eglErr = eglGetError();
-        if (result == EGL_TIMEOUT_EXPIRED_KHR) {
-            ALOGW("fence wait timed out");
-        } else {
-            ALOGW_IF(eglErr != EGL_SUCCESS, "error waiting on EGL fence: %#x", eglErr);
-        }
-        eglDestroySyncKHR(mEGLDisplay, sync);
-    } else {
-        ALOGW("error creating EGL fence: %#x", eglGetError());
-    }
-
-    return -1;
-}
-
 void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
     glClearColor(red, green, blue, alpha);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -310,19 +349,28 @@
     glDeleteTextures(count, names);
 }
 
+void RenderEngine::bindExternalTextureImage(uint32_t texName, const RE::Image& image) {
+    const GLenum target = GL_TEXTURE_EXTERNAL_OES;
+
+    glBindTexture(target, texName);
+    if (image.getEGLImage() != EGL_NO_IMAGE_KHR) {
+        glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(image.getEGLImage()));
+    }
+}
+
 void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) {
     glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
 }
 
 void RenderEngine::dump(String8& result) {
-    result.appendFormat("EGL implementation : %s\n",
-                        eglQueryStringImplementationANDROID(mEGLDisplay, EGL_VERSION));
-    result.appendFormat("%s\n", eglQueryStringImplementationANDROID(mEGLDisplay, EGL_EXTENSIONS));
+    const GLExtensions& extensions = GLExtensions::getInstance();
 
-    const GLExtensions& extensions(GLExtensions::getInstance());
+    result.appendFormat("EGL implementation : %s\n", extensions.getEGLVersion());
+    result.appendFormat("%s\n", extensions.getEGLExtensions());
+
     result.appendFormat("GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
                         extensions.getVersion());
-    result.appendFormat("%s\n", extensions.getExtension());
+    result.appendFormat("%s\n", extensions.getExtensions());
 }
 
 // ---------------------------------------------------------------------------
@@ -331,7 +379,7 @@
         RenderEngine& engine, ANativeWindowBuffer* buffer)
       : mEngine(engine) {
     mImage = eglCreateImageKHR(mEngine.mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
-                               buffer, NULL);
+                               buffer, nullptr);
     if (mImage == EGL_NO_IMAGE_KHR) {
         mStatus = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
         return;
@@ -362,7 +410,7 @@
 static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
                                          EGLint wanted, EGLConfig* outConfig) {
     EGLint numConfigs = -1, n = 0;
-    eglGetConfigs(dpy, NULL, 0, &numConfigs);
+    eglGetConfigs(dpy, nullptr, 0, &numConfigs);
     EGLConfig* const configs = new EGLConfig[numConfigs];
     eglChooseConfig(dpy, attrs, configs, numConfigs, &n);
 
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 3847347..737b1dd 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -25,6 +25,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <Transform.h>
+#include <android-base/unique_fd.h>
 #include <gui/SurfaceControl.h>
 #include <math/mat4.h>
 
@@ -43,8 +44,9 @@
 class Texture;
 
 namespace RE {
+class Image;
 class Surface;
-}
+} // namespace RE
 
 class RenderEngine {
     enum GlesVersion {
@@ -64,6 +66,8 @@
                                         uint32_t* status) = 0;
     virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName) = 0;
 
+    static bool overrideUseContextPriorityFromConfig(bool useContextPriority);
+
 protected:
     RenderEngine();
 
@@ -82,9 +86,26 @@
     // dump the extension strings. always call the base class.
     virtual void dump(String8& result);
 
+    bool supportsImageCrop() const;
+
+    bool isCurrent() const;
+    bool setCurrentSurface(const RE::Surface& surface);
+    void resetCurrentSurface();
+
+    // synchronization
+
+    // flush submits RenderEngine command stream for execution and returns a
+    // native fence fd that is signaled when the execution has completed.  It
+    // returns -1 on errors.
+    base::unique_fd flush();
+    // finish waits until RenderEngine command stream has been executed.  It
+    // returns false on errors.
+    bool finish();
+    // waitFence inserts a wait on an external fence fd to RenderEngine
+    // command stream.  It returns false on errors.
+    bool waitFence(base::unique_fd fenceFd);
+
     // helpers
-    // flush returns -1 or a valid native fence fd owned by the caller
-    int flush(bool wait);
     void clearWithColor(float red, float green, float blue, float alpha);
     void fillRegionWithColor(const Region& region, uint32_t height, float red, float green,
                              float blue, float alpha);
@@ -94,6 +115,7 @@
     void disableScissor();
     void genTextures(size_t count, uint32_t* names);
     void deleteTextures(size_t count, uint32_t const* names);
+    void bindExternalTextureImage(uint32_t texName, const RE::Image& image);
     void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels);
 
     class BindNativeBufferAsFramebuffer {
@@ -108,9 +130,6 @@
         int getStatus() const;
     };
 
-    bool setCurrentSurface(const RE::Surface& surface);
-    void resetCurrentSurface();
-
     // set-up
     virtual void checkErrors() const;
     virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 44d5ce4..a91525d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -129,13 +129,13 @@
 // ---------------------------------------------------------------------------
 int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
 int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
-bool SurfaceFlinger::useContextPriority;
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
 bool SurfaceFlinger::useHwcForRgbToYuv;
 uint64_t SurfaceFlinger::maxVirtualDisplaySize;
 bool SurfaceFlinger::hasSyncFramework;
 bool SurfaceFlinger::useVrFlinger;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+// TODO(courtneygo): Rename hasWideColorDisplay to clarify its actual meaning.
 bool SurfaceFlinger::hasWideColorDisplay;
 
 
@@ -206,9 +206,6 @@
     hasSyncFramework = getBool< ISurfaceFlingerConfigs,
             &ISurfaceFlingerConfigs::hasSyncFramework>(true);
 
-    useContextPriority = getBool< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::useContextPriority>(false);
-
     dispSyncPresentTimeOffset = getInt64< ISurfaceFlingerConfigs,
             &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0);
 
@@ -368,21 +365,10 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) {
-    ALOGV("createBuiltinDisplayLocked(%d)", type);
-    ALOGW_IF(mBuiltinDisplays[type],
-            "Overwriting display token for display type %d", type);
-    mBuiltinDisplays[type] = new BBinder();
-    // All non-virtual displays are currently considered secure.
-    DisplayDeviceState info(type, true);
-    mCurrentState.displays.add(mBuiltinDisplays[type], info);
-    mInterceptor.saveDisplayCreation(info);
-}
-
 sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
     if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
         ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id);
-        return NULL;
+        return nullptr;
     }
     return mBuiltinDisplays[id];
 }
@@ -531,7 +517,7 @@
             }
         }
 
-        if (callback != NULL) {
+        if (callback != nullptr) {
             callback->onVSyncEvent(when);
         }
     }
@@ -618,6 +604,14 @@
             "Starting with vr flinger active is not currently supported.");
     getBE().mHwc.reset(new HWComposer(getBE().mHwcServiceName));
     getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId);
+    // Process any initial hotplug and resulting display changes.
+    processDisplayHotplugEventsLocked();
+    LOG_ALWAYS_FATAL_IF(!getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY),
+            "Registered composer callback but didn't create the default primary display");
+
+    // make the default display GLContext current so that we can create textures
+    // when creating Layers (which may happens before we render something)
+    getDefaultDisplayDeviceLocked()->makeCurrent();
 
     if (useVrFlinger) {
         auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) {
@@ -737,7 +731,7 @@
 
 status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
         Vector<DisplayInfo>* configs) {
-    if ((configs == NULL) || (display.get() == NULL)) {
+    if (configs == nullptr || display.get() == nullptr) {
         return BAD_VALUE;
     }
 
@@ -761,7 +755,7 @@
         static int getDensityFromProperty(char const* propName) {
             char property[PROPERTY_VALUE_MAX];
             int density = 0;
-            if (property_get(propName, property, NULL) > 0) {
+            if (property_get(propName, property, nullptr) > 0) {
                 density = atoi(property);
             }
             return density;
@@ -801,7 +795,7 @@
 
             // TODO: this needs to go away (currently needed only by webkit)
             sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
-            info.orientation = hw->getOrientation();
+            info.orientation = hw ? hw->getOrientation() : 0;
         } else {
             // TODO: where should this value come from?
             static const int TV_DENSITY = 213;
@@ -842,7 +836,7 @@
 
 status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */,
         DisplayStatInfo* stats) {
-    if (stats == NULL) {
+    if (stats == nullptr) {
         return BAD_VALUE;
     }
 
@@ -854,13 +848,13 @@
 }
 
 int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
-    if (display == NULL) {
-        ALOGE("%s : display is NULL", __func__);
+    if (display == nullptr) {
+        ALOGE("%s : display is nullptr", __func__);
         return BAD_VALUE;
     }
 
     sp<const DisplayDevice> device(getDisplayDevice(display));
-    if (device != NULL) {
+    if (device != nullptr) {
         return device->getActiveConfig();
     }
 
@@ -905,7 +899,7 @@
                 return true;
             }
             sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
-            if (hw == NULL) {
+            if (hw == nullptr) {
                 ALOGE("Attempt to set active config = %d for null display %p",
                         mMode, mDisplay.get());
             } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
@@ -1266,94 +1260,25 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
-void SurfaceFlinger::createDefaultDisplayDevice() {
-    const DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_PRIMARY;
-    wp<IBinder> token = mBuiltinDisplays[type];
+void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
+                                       HWC2::Connection connection) {
+    ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s)", sequenceId, display,
+          connection == HWC2::Connection::Connected ? "connected" : "disconnected");
 
-    // All non-virtual displays are currently considered secure.
-    const bool isSecure = true;
-
-    sp<IGraphicBufferProducer> producer;
-    sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer);
-
-    sp<FramebufferSurface> fbs = new FramebufferSurface(*getBE().mHwc, type, consumer);
-
-    bool hasWideColorModes = false;
-    std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(type);
-    for (android_color_mode_t colorMode : modes) {
-        switch (colorMode) {
-            case HAL_COLOR_MODE_DISPLAY_P3:
-            case HAL_COLOR_MODE_ADOBE_RGB:
-            case HAL_COLOR_MODE_DCI_P3:
-                hasWideColorModes = true;
-                break;
-            default:
-                break;
-        }
+    // Ignore events that do not have the right sequenceId.
+    if (sequenceId != getBE().mComposerSequenceId) {
+        return;
     }
-    bool useWideColorMode = hasWideColorModes && hasWideColorDisplay && !mForceNativeColorMode;
-    sp<DisplayDevice> hw = new DisplayDevice(this, DisplayDevice::DISPLAY_PRIMARY, type, isSecure,
-                                             token, fbs, producer, useWideColorMode);
-    mDisplays.add(token, hw);
-    android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
-    if (useWideColorMode) {
-        defaultColorMode = HAL_COLOR_MODE_SRGB;
-    }
-    setActiveColorModeInternal(hw, defaultColorMode);
-    hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN);
-
-    // Add the primary display token to mDrawingState so we don't try to
-    // recreate the DisplayDevice for the primary display.
-    mDrawingState.displays.add(token, DisplayDeviceState(type, true));
-
-    // make the GLContext current so that we can create textures when creating
-    // Layers (which may happens before we render something)
-    hw->makeCurrent();
-}
-
-void SurfaceFlinger::onHotplugReceived(int32_t sequenceId,
-        hwc2_display_t display, HWC2::Connection connection,
-        bool primaryDisplay) {
-    ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s, %s)",
-          sequenceId, display,
-          connection == HWC2::Connection::Connected ?
-                  "connected" : "disconnected",
-          primaryDisplay ? "primary" : "external");
 
     // Only lock if we're not on the main thread. This function is normally
     // called on a hwbinder thread, but for the primary display it's called on
     // the main thread with the state lock already held, so don't attempt to
     // acquire it here.
-    ConditionalLock lock(mStateLock,
-            std::this_thread::get_id() != mMainThreadId);
+    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
 
-    if (primaryDisplay) {
-        getBE().mHwc->onHotplug(display, connection);
-        if (!mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY].get()) {
-            createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY);
-        }
-        createDefaultDisplayDevice();
-    } else {
-        if (sequenceId != getBE().mComposerSequenceId) {
-            return;
-        }
-        if (getBE().mHwc->isUsingVrComposer()) {
-            ALOGE("External displays are not supported by the vr hardware composer.");
-            return;
-        }
-        getBE().mHwc->onHotplug(display, connection);
-        auto type = DisplayDevice::DISPLAY_EXTERNAL;
-        if (connection == HWC2::Connection::Connected) {
-            createBuiltinDisplayLocked(type);
-        } else {
-            mCurrentState.displays.removeItem(mBuiltinDisplays[type]);
-            mBuiltinDisplays[type].clear();
-        }
-        setTransactionFlags(eDisplayTransactionNeeded);
+    mPendingHotplugEvents.emplace_back(HotplugEvent{display, connection});
 
-        // Defer EventThread notification until SF has updated mDisplays.
-    }
+    setTransactionFlags(eDisplayTransactionNeeded);
 }
 
 void SurfaceFlinger::onRefreshReceived(int sequenceId,
@@ -1668,7 +1593,7 @@
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    if (getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
+    if (hw && getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
         glCompositionDoneFenceTime =
                 std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
         getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
@@ -1713,7 +1638,7 @@
     }
 
     if (!hasSyncFramework) {
-        if (hw->isDisplayOn()) {
+        if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) && hw->isDisplayOn()) {
             enableHardwareVsync();
         }
     }
@@ -1724,7 +1649,7 @@
         if (presentFenceTime->isValid()) {
             mAnimFrameTracker.setActualPresentFence(
                     std::move(presentFenceTime));
-        } else {
+        } else if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
             nsecs_t presentTime =
@@ -1734,7 +1659,8 @@
         mAnimFrameTracker.advanceFrame();
     }
 
-    if (hw->getPowerMode() == HWC_POWER_MODE_OFF) {
+    if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) &&
+            hw->getPowerMode() == HWC_POWER_MODE_OFF) {
         return;
     }
 
@@ -2077,9 +2003,11 @@
     mDebugInSwapBuffers = 0;
 
     // |mStateLock| not needed as we are on the main thread
-    uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
-    if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
-        logFrameStats();
+    if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
+        uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
+        if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
+            logFrameStats();
+        }
     }
 }
 
@@ -2112,6 +2040,245 @@
     // here the transaction has been committed
 }
 
+DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t display,
+        HWC2::Connection connection) const {
+    // Figure out whether the event is for the primary display or an
+    // external display by matching the Hwc display id against one for a
+    // connected display. If we did not find a match, we then check what
+    // displays are not already connected to determine the type. If we don't
+    // have a connected primary display, we assume the new display is meant to
+    // be the primary display, and then if we don't have an external display,
+    // we assume it is that.
+    const auto primaryDisplayId =
+            getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_PRIMARY);
+    const auto externalDisplayId =
+            getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_EXTERNAL);
+    if (primaryDisplayId && primaryDisplayId == display) {
+        return DisplayDevice::DISPLAY_PRIMARY;
+    } else if (externalDisplayId && externalDisplayId == display) {
+        return  DisplayDevice::DISPLAY_EXTERNAL;
+    } else if (connection == HWC2::Connection::Connected && !primaryDisplayId) {
+        return DisplayDevice::DISPLAY_PRIMARY;
+    } else if (connection == HWC2::Connection::Connected && !externalDisplayId) {
+        return DisplayDevice::DISPLAY_EXTERNAL;
+    }
+
+    return DisplayDevice::DISPLAY_ID_INVALID;
+}
+
+void SurfaceFlinger::processDisplayHotplugEventsLocked() {
+    for (const auto& event : mPendingHotplugEvents) {
+        auto displayType = determineDisplayType(event.display, event.connection);
+        if (displayType == DisplayDevice::DISPLAY_ID_INVALID) {
+            ALOGW("Unable to determine the display type for display %" PRIu64, event.display);
+            continue;
+        }
+
+        if (getBE().mHwc->isUsingVrComposer() && displayType == DisplayDevice::DISPLAY_EXTERNAL) {
+            ALOGE("External displays are not supported by the vr hardware composer.");
+            continue;
+        }
+
+        getBE().mHwc->onHotplug(event.display, displayType, event.connection);
+
+        if (event.connection == HWC2::Connection::Connected) {
+            ALOGV("Creating built in display %d", displayType);
+            ALOGW_IF(mBuiltinDisplays[displayType],
+                    "Overwriting display token for display type %d", displayType);
+            mBuiltinDisplays[displayType] = new BBinder();
+            // All non-virtual displays are currently considered secure.
+            DisplayDeviceState info(displayType, true);
+            info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ?
+                    "Built-in Screen" : "External Screen";
+            mCurrentState.displays.add(mBuiltinDisplays[displayType], info);
+            mInterceptor.saveDisplayCreation(info);
+        } else {
+            ALOGV("Removing built in display %d", displayType);
+
+            ssize_t idx = mCurrentState.displays.indexOfKey(mBuiltinDisplays[displayType]);
+            if (idx >= 0) {
+                const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
+                mInterceptor.saveDisplayDeletion(info.displayId);
+                mCurrentState.displays.removeItemsAt(idx);
+            }
+            mBuiltinDisplays[displayType].clear();
+        }
+
+        processDisplayChangesLocked();
+    }
+
+    mPendingHotplugEvents.clear();
+}
+
+void SurfaceFlinger::processDisplayChangesLocked() {
+    // here we take advantage of Vector's copy-on-write semantics to
+    // improve performance by skipping the transaction entirely when
+    // know that the lists are identical
+    const KeyedVector<wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
+    const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
+    if (!curr.isIdenticalTo(draw)) {
+        mVisibleRegionsDirty = true;
+        const size_t cc = curr.size();
+        size_t dc = draw.size();
+
+        // find the displays that were removed
+        // (ie: in drawing state but not in current state)
+        // also handle displays that changed
+        // (ie: displays that are in both lists)
+        for (size_t i = 0; i < dc;) {
+            const ssize_t j = curr.indexOfKey(draw.keyAt(i));
+            if (j < 0) {
+                // in drawing state but not in current state
+                // Call makeCurrent() on the primary display so we can
+                // be sure that nothing associated with this display
+                // is current.
+                const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
+                if (defaultDisplay != nullptr) defaultDisplay->makeCurrent();
+                sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
+                if (hw != nullptr) hw->disconnect(getHwComposer());
+                if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
+                    mEventThread->onHotplugReceived(draw[i].type, false);
+                mDisplays.removeItem(draw.keyAt(i));
+            } else {
+                // this display is in both lists. see if something changed.
+                const DisplayDeviceState& state(curr[j]);
+                const wp<IBinder>& display(curr.keyAt(j));
+                const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
+                const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
+                if (state_binder != draw_binder) {
+                    // changing the surface is like destroying and
+                    // recreating the DisplayDevice, so we just remove it
+                    // from the drawing state, so that it get re-added
+                    // below.
+                    sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
+                    if (hw != nullptr) hw->disconnect(getHwComposer());
+                    mDisplays.removeItem(display);
+                    mDrawingState.displays.removeItemsAt(i);
+                    dc--;
+                    // at this point we must loop to the next item
+                    continue;
+                }
+
+                const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
+                if (disp != nullptr) {
+                    if (state.layerStack != draw[i].layerStack) {
+                        disp->setLayerStack(state.layerStack);
+                    }
+                    if ((state.orientation != draw[i].orientation) ||
+                        (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
+                        disp->setProjection(state.orientation, state.viewport, state.frame);
+                    }
+                    if (state.width != draw[i].width || state.height != draw[i].height) {
+                        disp->setDisplaySize(state.width, state.height);
+                    }
+                }
+            }
+            ++i;
+        }
+
+        // find displays that were added
+        // (ie: in current state but not in drawing state)
+        for (size_t i = 0; i < cc; i++) {
+            if (draw.indexOfKey(curr.keyAt(i)) < 0) {
+                const DisplayDeviceState& state(curr[i]);
+
+                sp<DisplaySurface> dispSurface;
+                sp<IGraphicBufferProducer> producer;
+                sp<IGraphicBufferProducer> bqProducer;
+                sp<IGraphicBufferConsumer> bqConsumer;
+                BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
+
+                int32_t hwcId = -1;
+                if (state.isVirtualDisplay()) {
+                    // Virtual displays without a surface are dormant:
+                    // they have external state (layer stack, projection,
+                    // etc.) but no internal state (i.e. a DisplayDevice).
+                    if (state.surface != nullptr) {
+                        // Allow VR composer to use virtual displays.
+                        if (mUseHwcVirtualDisplays || getBE().mHwc->isUsingVrComposer()) {
+                            int width = 0;
+                            int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
+                            ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
+                            int height = 0;
+                            status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
+                            ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
+                            int intFormat = 0;
+                            status = state.surface->query(NATIVE_WINDOW_FORMAT, &intFormat);
+                            ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
+                            auto format = static_cast<android_pixel_format_t>(intFormat);
+
+                            getBE().mHwc->allocateVirtualDisplay(width, height, &format, &hwcId);
+                        }
+
+                        // TODO: Plumb requested format back up to consumer
+
+                        sp<VirtualDisplaySurface> vds =
+                                new VirtualDisplaySurface(*getBE().mHwc, hwcId, state.surface,
+                                                          bqProducer, bqConsumer,
+                                                          state.displayName);
+
+                        dispSurface = vds;
+                        producer = vds;
+                    }
+                } else {
+                    ALOGE_IF(state.surface != nullptr,
+                             "adding a supported display, but rendering "
+                             "surface is provided (%p), ignoring it",
+                             state.surface.get());
+
+                    hwcId = state.type;
+                    dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer);
+                    producer = bqProducer;
+                }
+
+                const wp<IBinder>& display(curr.keyAt(i));
+
+                if (dispSurface != nullptr) {
+                    bool useWideColorMode = hasWideColorDisplay;
+                    if (!mForceNativeColorMode) {
+                        bool hasWideColorModes = false;
+                        std::vector<android_color_mode_t> modes =
+                                getHwComposer().getColorModes(state.type);
+                        for (android_color_mode_t colorMode : modes) {
+                            switch (colorMode) {
+                                case HAL_COLOR_MODE_DISPLAY_P3:
+                                case HAL_COLOR_MODE_ADOBE_RGB:
+                                case HAL_COLOR_MODE_DCI_P3:
+                                    hasWideColorModes = true;
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                        useWideColorMode = hasWideColorModes && hasWideColorDisplay;
+                    }
+
+                    sp<DisplayDevice> hw =
+                            new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
+                                              dispSurface, producer, useWideColorMode);
+
+                    android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
+                    if (useWideColorMode) {
+                        defaultColorMode = HAL_COLOR_MODE_SRGB;
+                    }
+                    setActiveColorModeInternal(hw, defaultColorMode);
+                    hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN);
+                    hw->setLayerStack(state.layerStack);
+                    hw->setProjection(state.orientation, state.viewport, state.frame);
+                    hw->setDisplayName(state.displayName);
+
+                    mDisplays.add(display, hw);
+                    if (!state.isVirtualDisplay()) {
+                        mEventThread->onHotplugReceived(state.type, true);
+                    }
+                }
+            }
+        }
+    }
+
+    mDrawingState.displays = mCurrentState.displays;
+}
+
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
 {
     // Notify all layers of available frames
@@ -2140,161 +2307,8 @@
      */
 
     if (transactionFlags & eDisplayTransactionNeeded) {
-        // here we take advantage of Vector's copy-on-write semantics to
-        // improve performance by skipping the transaction entirely when
-        // know that the lists are identical
-        const KeyedVector<  wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
-        const KeyedVector<  wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
-        if (!curr.isIdenticalTo(draw)) {
-            mVisibleRegionsDirty = true;
-            const size_t cc = curr.size();
-                  size_t dc = draw.size();
-
-            // find the displays that were removed
-            // (ie: in drawing state but not in current state)
-            // also handle displays that changed
-            // (ie: displays that are in both lists)
-            for (size_t i=0 ; i<dc ;) {
-                const ssize_t j = curr.indexOfKey(draw.keyAt(i));
-                if (j < 0) {
-                    // in drawing state but not in current state
-                    if (!draw[i].isMainDisplay()) {
-                        // Call makeCurrent() on the primary display so we can
-                        // be sure that nothing associated with this display
-                        // is current.
-                        const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
-                        defaultDisplay->makeCurrent();
-                        sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
-                        if (hw != NULL)
-                            hw->disconnect(getHwComposer());
-                        if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
-                            mEventThread->onHotplugReceived(draw[i].type, false);
-                        mDisplays.removeItem(draw.keyAt(i));
-                    } else {
-                        ALOGW("trying to remove the main display");
-                    }
-                } else {
-                    // this display is in both lists. see if something changed.
-                    const DisplayDeviceState& state(curr[j]);
-                    const wp<IBinder>& display(curr.keyAt(j));
-                    const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
-                    const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
-                    if (state_binder != draw_binder) {
-                        // changing the surface is like destroying and
-                        // recreating the DisplayDevice, so we just remove it
-                        // from the drawing state, so that it get re-added
-                        // below.
-                        sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
-                        if (hw != NULL)
-                            hw->disconnect(getHwComposer());
-                        mDisplays.removeItem(display);
-                        mDrawingState.displays.removeItemsAt(i);
-                        dc--;
-                        // at this point we must loop to the next item
-                        continue;
-                    }
-
-                    const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
-                    if (disp != NULL) {
-                        if (state.layerStack != draw[i].layerStack) {
-                            disp->setLayerStack(state.layerStack);
-                        }
-                        if ((state.orientation != draw[i].orientation)
-                                || (state.viewport != draw[i].viewport)
-                                || (state.frame != draw[i].frame))
-                        {
-                            disp->setProjection(state.orientation,
-                                    state.viewport, state.frame);
-                        }
-                        if (state.width != draw[i].width || state.height != draw[i].height) {
-                            disp->setDisplaySize(state.width, state.height);
-                        }
-                    }
-                }
-                ++i;
-            }
-
-            // find displays that were added
-            // (ie: in current state but not in drawing state)
-            for (size_t i=0 ; i<cc ; i++) {
-                if (draw.indexOfKey(curr.keyAt(i)) < 0) {
-                    const DisplayDeviceState& state(curr[i]);
-
-                    sp<DisplaySurface> dispSurface;
-                    sp<IGraphicBufferProducer> producer;
-                    sp<IGraphicBufferProducer> bqProducer;
-                    sp<IGraphicBufferConsumer> bqConsumer;
-                    BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
-
-                    int32_t hwcId = -1;
-                    if (state.isVirtualDisplay()) {
-                        // Virtual displays without a surface are dormant:
-                        // they have external state (layer stack, projection,
-                        // etc.) but no internal state (i.e. a DisplayDevice).
-                        if (state.surface != NULL) {
-
-                            // Allow VR composer to use virtual displays.
-                            if (mUseHwcVirtualDisplays || getBE().mHwc->isUsingVrComposer()) {
-                                int width = 0;
-                                int status = state.surface->query(
-                                        NATIVE_WINDOW_WIDTH, &width);
-                                ALOGE_IF(status != NO_ERROR,
-                                        "Unable to query width (%d)", status);
-                                int height = 0;
-                                status = state.surface->query(
-                                        NATIVE_WINDOW_HEIGHT, &height);
-                                ALOGE_IF(status != NO_ERROR,
-                                        "Unable to query height (%d)", status);
-                                int intFormat = 0;
-                                status = state.surface->query(
-                                        NATIVE_WINDOW_FORMAT, &intFormat);
-                                ALOGE_IF(status != NO_ERROR,
-                                        "Unable to query format (%d)", status);
-                                auto format = static_cast<android_pixel_format_t>(
-                                        intFormat);
-
-                                getBE().mHwc->allocateVirtualDisplay(width, height, &format,
-                                        &hwcId);
-                            }
-
-                            // TODO: Plumb requested format back up to consumer
-
-                            sp<VirtualDisplaySurface> vds =
-                                    new VirtualDisplaySurface(*getBE().mHwc,
-                                            hwcId, state.surface, bqProducer,
-                                            bqConsumer, state.displayName);
-
-                            dispSurface = vds;
-                            producer = vds;
-                        }
-                    } else {
-                        ALOGE_IF(state.surface!=NULL,
-                                "adding a supported display, but rendering "
-                                "surface is provided (%p), ignoring it",
-                                state.surface.get());
-
-                        hwcId = state.type;
-                        dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer);
-                        producer = bqProducer;
-                    }
-
-                    const wp<IBinder>& display(curr.keyAt(i));
-                    if (dispSurface != NULL) {
-                        sp<DisplayDevice> hw =
-                                new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
-                                                  dispSurface, producer, hasWideColorDisplay);
-                        hw->setLayerStack(state.layerStack);
-                        hw->setProjection(state.orientation,
-                                state.viewport, state.frame);
-                        hw->setDisplayName(state.displayName);
-                        mDisplays.add(display, hw);
-                        if (!state.isVirtualDisplay()) {
-                            mEventThread->onHotplugReceived(state.type, true);
-                        }
-                    }
-                }
-            }
-        }
+        processDisplayChangesLocked();
+        processDisplayHotplugEventsLocked();
     }
 
     if (transactionFlags & (eTraversalNeeded|eDisplayTransactionNeeded)) {
@@ -2334,25 +2348,31 @@
                 for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
                     sp<const DisplayDevice> hw(mDisplays[dpy]);
                     if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) {
-                        if (disp == NULL) {
+                        if (disp == nullptr) {
                             disp = std::move(hw);
                         } else {
-                            disp = NULL;
+                            disp = nullptr;
                             break;
                         }
                     }
                 }
             }
-            if (disp == NULL) {
-                // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
-                // redraw after transform hint changes. See bug 8508397.
 
-                // could be null when this layer is using a layerStack
-                // that is not visible on any display. Also can occur at
-                // screen off/on times.
-                disp = getDefaultDisplayDeviceLocked();
+            if (transactionFlags & eDisplayTransactionNeeded) {
+                if (disp == nullptr) {
+                    // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
+                    // redraw after transform hint changes. See bug 8508397.
+
+                    // could be null when this layer is using a layerStack
+                    // that is not visible on any display. Also can occur at
+                    // screen off/on times.
+                    disp = getDefaultDisplayDeviceLocked();
+                }
+                layer->updateTransformHint(disp);
             }
-            layer->updateTransformHint(disp);
+            if (disp != nullptr) {
+                layer->updateTransformHint(disp);
+            }
 
             first = false;
         });
@@ -2510,6 +2530,11 @@
             }
         }
 
+        if (visibleRegion.isEmpty()) {
+            layer->clearVisibilityRegions();
+            return;
+        }
+
         // Clip the covered region to the visible region
         coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
 
@@ -2938,16 +2963,16 @@
     for (size_t i=0 ; i<count ; i++) {
         const ComposerState& s(state[i]);
         // Here we need to check that the interface we're given is indeed
-        // one of our own. A malicious client could give us a NULL
+        // one of our own. A malicious client could give us a nullptr
         // IInterface, or one of its own or even one of our own but a
         // different type. All these situations would cause us to crash.
         //
         // NOTE: it would be better to use RTTI as we could directly check
         // that we have a Client*. however, RTTI is disabled in Android.
-        if (s.client != NULL) {
+        if (s.client != nullptr) {
             sp<IBinder> binder = IInterface::asBinder(s.client);
-            if (binder != NULL) {
-                if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) != NULL) {
+            if (binder != nullptr) {
+                if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) != nullptr) {
                     sp<Client> client( static_cast<Client *>(s.client.get()) );
                     transactionFlags |= setClientStateLocked(client, s.state);
                 }
@@ -3321,7 +3346,7 @@
     // called by a client when it wants to remove a Layer
     status_t err = NO_ERROR;
     sp<Layer> l(client->getLayerUser(handle));
-    if (l != NULL) {
+    if (l != nullptr) {
         mInterceptor.saveSurfaceDeletion(l);
         err = removeLayer(l);
         ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
@@ -3486,7 +3511,7 @@
                     mDisplay(disp) { mMode = mode; }
         virtual bool handler() {
             sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
-            if (hw == NULL) {
+            if (hw == nullptr) {
                 ALOGE("Attempt to set power mode = %d for null display %p",
                         mMode, mDisplay.get());
             } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
@@ -3663,7 +3688,6 @@
 void SurfaceFlinger::appendSfConfigString(String8& result) const
 {
     result.append(" [sf");
-    result.appendFormat(" HAS_CONTEXT_PRIORITY=%d", useContextPriority);
 
     if (isLayerTripleBufferingDisabled())
         result.append(" DISABLE_TRIPLE_BUFFERING");
@@ -3888,9 +3912,11 @@
 
     getBE().mRenderEngine->dump(result);
 
-    hw->undefinedRegion.dump(result, "undefinedRegion");
-    result.appendFormat("  orientation=%d, isDisplayOn=%d\n",
-            hw->getOrientation(), hw->isDisplayOn());
+    if (hw) {
+        hw->undefinedRegion.dump(result, "undefinedRegion");
+        result.appendFormat("  orientation=%d, isDisplayOn=%d\n",
+                hw->getOrientation(), hw->isDisplayOn());
+    }
     result.appendFormat(
             "  last eglSwapBuffers() time: %f us\n"
             "  last transaction time     : %f us\n"
@@ -3975,7 +4001,7 @@
             break;
         }
     }
-    if (dpy == NULL) {
+    if (dpy == nullptr) {
         ALOGE("getLayerSortedByZForHwcDisplay: invalid hwc display id %d", id);
         // Just use the primary display so we have something to return
         dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY);
@@ -4242,7 +4268,7 @@
                 reply->writeBool(hasWideColorDisplay);
                 return NO_ERROR;
             }
-            case 1025: { // tracing
+            case 1025: { // Set layer tracing
                 n = data.readInt32();
                 if (n) {
                     ALOGV("LayerTracing enabled");
@@ -4256,6 +4282,10 @@
                 }
                 return NO_ERROR;
             }
+            case 1026: { // Get layer tracing status
+                reply->writeBool(mTracing.isEnabled());
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -4303,7 +4333,7 @@
 }
 
 status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder,
-                                       sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, 
+                                       sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
                                        float frameScale) {
     ATRACE_CALL();
 
@@ -4397,6 +4427,9 @@
     int syncFd = -1;
     std::optional<status_t> captureResult;
 
+    const int uid = IPCThreadState::self()->getCallingUid();
+    const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
+
     sp<LambdaMessage> message = new LambdaMessage([&]() {
         // If there is a refresh pending, bug out early and tell the binder thread to try again
         // after the refresh.
@@ -4413,7 +4446,7 @@
         {
             Mutex::Autolock _l(mStateLock);
             result = captureScreenImplLocked(renderArea, traverseLayers, (*outBuffer).get(),
-                                             useIdentityTransform, &fd);
+                                             useIdentityTransform, forSystem, &fd);
         }
 
         {
@@ -4510,6 +4543,7 @@
                                                  TraverseLayersFunction traverseLayers,
                                                  ANativeWindowBuffer* buffer,
                                                  bool useIdentityTransform,
+                                                 bool forSystem,
                                                  int* outSyncFd) {
     ATRACE_CALL();
 
@@ -4519,7 +4553,10 @@
         secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() && layer->isSecure());
     });
 
-    if (secureLayerIsVisible) {
+    // We allow the system server to take screenshots of secure layers for
+    // use in situations like the Screen-rotation animation and place
+    // the impetus on WindowManager to not persist them.
+    if (secureLayerIsVisible && !forSystem) {
         ALOGW("FB is protected: PERMISSION_DENIED");
         return PERMISSION_DENIED;
     }
@@ -4538,9 +4575,10 @@
     // dependent on the context's EGLConfig.
     renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform);
 
-    *outSyncFd = getRenderEngine().flush(DEBUG_SCREENSHOTS);
-
     if (DEBUG_SCREENSHOTS) {
+        getRenderEngine().finish();
+        *outSyncFd = -1;
+
         const auto reqWidth = renderArea.getReqWidth();
         const auto reqHeight = renderArea.getReqHeight();
 
@@ -4548,6 +4586,12 @@
         getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
         checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, traverseLayers);
         delete [] pixels;
+    } else {
+        base::unique_fd syncFd = getRenderEngine().flush();
+        if (syncFd < 0) {
+            getRenderEngine().finish();
+        }
+        *outSyncFd = syncFd.release();
     }
 
     return NO_ERROR;
@@ -4601,6 +4645,9 @@
             continue;
         }
         layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+            if (!layer->belongsToDisplay(hw->getLayerStack(), false)) {
+                return;
+            }
             if (!layer->isVisible()) {
                 return;
             }
@@ -4618,4 +4665,4 @@
 
 #if defined(__gl2_h_)
 #error "don't include gl2/gl2.h in this file"
-#endif
\ No newline at end of file
+#endif
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 2477bdc..4da0803 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -142,14 +142,14 @@
     std::unique_ptr<RenderEngine> mRenderEngine;
     EGLContext mEGLContext;
     EGLDisplay mEGLDisplay;
-    
+
     FenceTimeline mGlCompositionDoneTimeline;
     FenceTimeline mDisplayTimeline;
 
     // protected by mCompositorTimingLock;
     mutable std::mutex mCompositorTimingLock;
     CompositorTiming mCompositorTiming;
-    
+
     // Only accessed from the main thread.
     struct CompositePresentTime {
         nsecs_t composite { -1 };
@@ -227,9 +227,6 @@
     // If fences from sync Framework are supported.
     static bool hasSyncFramework;
 
-    // Instruct the Render Engine to use EGL_IMG_context_priority is available.
-    static bool useContextPriority;
-
     // The offset in nanoseconds to use when DispSync timestamps present fence
     // signaling time.
     static int64_t dispSyncPresentTimeOffset;
@@ -417,8 +414,7 @@
     void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
                          int64_t timestamp) override;
     void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
-                           HWC2::Connection connection,
-                           bool primaryDisplay) override;
+                           HWC2::Connection connection) override;
     void onRefreshReceived(int32_t sequenceId, hwc2_display_t display) override;
 
     /* ------------------------------------------------------------------------
@@ -524,7 +520,7 @@
     status_t captureScreenImplLocked(const RenderArea& renderArea,
                                      TraverseLayersFunction traverseLayers,
                                      ANativeWindowBuffer* buffer, bool useIdentityTransform,
-                                     int* outSyncFd);
+                                     bool forSystem, int* outSyncFd);
     void traverseLayersInDisplay(const sp<const DisplayDevice>& display, int32_t minLayerZ,
                                  int32_t maxLayerZ, const LayerVector::Visitor& visitor);
 
@@ -547,10 +543,6 @@
     // called when starting, or restarting after system_server death
     void initializeDisplays();
 
-    // Create an IBinder for a builtin display and add it to current state
-    void createBuiltinDisplayLocked(DisplayDevice::DisplayType type);
-
-
     sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) const {
       Mutex::Autolock _l(mStateLock);
       return getDisplayDeviceLocked(dpy);
@@ -575,8 +567,6 @@
         return getDisplayDeviceLocked(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]);
     }
 
-    void createDefaultDisplayDevice();
-
     int32_t getDisplayType(const sp<IBinder>& display) {
         if (!display.get()) return NAME_NOT_FOUND;
         for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) {
@@ -637,6 +627,10 @@
     /* ------------------------------------------------------------------------
      * Display management
      */
+    DisplayDevice::DisplayType determineDisplayType(hwc2_display_t display,
+            HWC2::Connection connection) const;
+    void processDisplayChangesLocked();
+    void processDisplayHotplugEventsLocked();
 
     /* ------------------------------------------------------------------------
      * VSync
@@ -738,6 +732,13 @@
     sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
     bool mHadClientComposition = false;
 
+    struct HotplugEvent {
+        hwc2_display_t display;
+        HWC2::Connection connection = HWC2::Connection::Invalid;
+    };
+    // protected by mStateLock
+    std::vector<HotplugEvent> mPendingHotplugEvents;
+
     // this may only be written from the main thread with mStateLock held
     // it may be read from other threads with mStateLock held
     DefaultKeyedVector< wp<IBinder>, sp<DisplayDevice> > mDisplays;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
deleted file mode 100644
index d5ba09d..0000000
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include "SurfaceFlingerConsumer.h"
-#include "Layer.h"
-
-#include <private/gui/SyncFeatures.h>
-
-#include <gui/BufferItem.h>
-#include <gui/BufferQueue.h>
-
-#include <utils/Errors.h>
-#include <utils/NativeHandle.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
-        const DispSync& dispSync, bool* autoRefresh, bool* queuedBuffer,
-        uint64_t maxFrameNumber)
-{
-    ATRACE_CALL();
-    ALOGV("updateTexImage");
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ALOGE("updateTexImage: GLConsumer is abandoned!");
-        return NO_INIT;
-    }
-
-    // Make sure the EGL state is the same as in previous calls.
-    status_t err = checkAndUpdateEglStateLocked();
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    BufferItem item;
-
-    // Acquire the next buffer.
-    // In asynchronous mode the list is guaranteed to be one buffer
-    // deep, while in synchronous mode we use the oldest buffer.
-    err = acquireBufferLocked(&item, computeExpectedPresent(dispSync),
-            maxFrameNumber);
-    if (err != NO_ERROR) {
-        if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
-            err = NO_ERROR;
-        } else if (err == BufferQueue::PRESENT_LATER) {
-            // return the error, without logging
-        } else {
-            ALOGE("updateTexImage: acquire failed: %s (%d)",
-                strerror(-err), err);
-        }
-        return err;
-    }
-
-    if (autoRefresh) {
-        *autoRefresh = item.mAutoRefresh;
-    }
-
-    if (queuedBuffer) {
-        *queuedBuffer = item.mQueuedBuffer;
-    }
-
-    // We call the rejecter here, in case the caller has a reason to
-    // not accept this buffer.  This is used by SurfaceFlinger to
-    // reject buffers which have the wrong size
-    int slot = item.mSlot;
-    if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) {
-        releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, EGL_NO_SYNC_KHR);
-        return BUFFER_REJECTED;
-    }
-
-    // Release the previous buffer.
-    err = updateAndReleaseLocked(item, &mPendingRelease);
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    if (!SyncFeatures::getInstance().useNativeFenceSync()) {
-        // Bind the new buffer to the GL texture.
-        //
-        // Older devices require the "implicit" synchronization provided
-        // by glEGLImageTargetTexture2DOES, which this method calls.  Newer
-        // devices will either call this in Layer::onDraw, or (if it's not
-        // a GL-composited layer) not at all.
-        err = bindTextureImageLocked();
-    }
-
-    return err;
-}
-
-status_t SurfaceFlingerConsumer::bindTextureImage()
-{
-    Mutex::Autolock lock(mMutex);
-
-    return bindTextureImageLocked();
-}
-
-status_t SurfaceFlingerConsumer::acquireBufferLocked(BufferItem* item,
-        nsecs_t presentWhen, uint64_t maxFrameNumber) {
-    status_t result = GLConsumer::acquireBufferLocked(item, presentWhen,
-            maxFrameNumber);
-    if (result == NO_ERROR) {
-        mTransformToDisplayInverse = item->mTransformToDisplayInverse;
-        mSurfaceDamage = item->mSurfaceDamage;
-    }
-    return result;
-}
-
-bool SurfaceFlingerConsumer::getTransformToDisplayInverse() const {
-    Mutex::Autolock lock(mMutex);
-    return mTransformToDisplayInverse;
-}
-
-const Region& SurfaceFlingerConsumer::getSurfaceDamage() const {
-    return mSurfaceDamage;
-}
-
-// We need to determine the time when a buffer acquired now will be
-// displayed.  This can be calculated:
-//   time when previous buffer's actual-present fence was signaled
-//    + current display refresh rate * HWC latency
-//    + a little extra padding
-//
-// Buffer producers are expected to set their desired presentation time
-// based on choreographer time stamps, which (coming from vsync events)
-// will be slightly later then the actual-present timing.  If we get a
-// desired-present time that is unintentionally a hair after the next
-// vsync, we'll hold the frame when we really want to display it.  We
-// need to take the offset between actual-present and reported-vsync
-// into account.
-//
-// If the system is configured without a DispSync phase offset for the app,
-// we also want to throw in a bit of padding to avoid edge cases where we
-// just barely miss.  We want to do it here, not in every app.  A major
-// source of trouble is the app's use of the display's ideal refresh time
-// (via Display.getRefreshRate()), which could be off of the actual refresh
-// by a few percent, with the error multiplied by the number of frames
-// between now and when the buffer should be displayed.
-//
-// If the refresh reported to the app has a phase offset, we shouldn't need
-// to tweak anything here.
-nsecs_t SurfaceFlingerConsumer::computeExpectedPresent(const DispSync& dispSync)
-{
-    // The HWC doesn't currently have a way to report additional latency.
-    // Assume that whatever we submit now will appear right after the flip.
-    // For a smart panel this might be 1.  This is expressed in frames,
-    // rather than time, because we expect to have a constant frame delay
-    // regardless of the refresh rate.
-    const uint32_t hwcLatency = 0;
-
-    // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
-    const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency);
-
-    // The DispSync time is already adjusted for the difference between
-    // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so
-    // we don't need to factor that in here.  Pad a little to avoid
-    // weird effects if apps might be requesting times right on the edge.
-    nsecs_t extraPadding = 0;
-    if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
-        extraPadding = 1000000;        // 1ms (6% of 60Hz)
-    }
-
-    return nextRefresh + extraPadding;
-}
-
-sp<Fence> SurfaceFlingerConsumer::getPrevFinalReleaseFence() const {
-    Mutex::Autolock lock(mMutex);
-    return ConsumerBase::mPrevFinalReleaseFence;
-}
-
-void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence)
-{
-    if (!mPendingRelease.isPending) {
-        GLConsumer::setReleaseFence(fence);
-        return;
-    }
-    auto currentTexture = mPendingRelease.currentTexture;
-    if (fence->isValid() &&
-            currentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
-        status_t result = addReleaseFence(currentTexture,
-                mPendingRelease.graphicBuffer, fence);
-        ALOGE_IF(result != NO_ERROR, "setReleaseFence: failed to add the"
-                " fence: %s (%d)", strerror(-result), result);
-    }
-}
-
-bool SurfaceFlingerConsumer::releasePendingBuffer()
-{
-    if (!mPendingRelease.isPending) {
-        ALOGV("Pending buffer already released");
-        return false;
-    }
-    ALOGV("Releasing pending buffer");
-    Mutex::Autolock lock(mMutex);
-    status_t result = releaseBufferLocked(mPendingRelease.currentTexture,
-            mPendingRelease.graphicBuffer, mPendingRelease.display,
-            mPendingRelease.fence);
-    ALOGE_IF(result < NO_ERROR, "releasePendingBuffer failed: %s (%d)",
-            strerror(-result), result);
-    mPendingRelease = PendingRelease();
-    return true;
-}
-
-void SurfaceFlingerConsumer::setContentsChangedListener(
-        const wp<ContentsChangedListener>& listener) {
-    setFrameAvailableListener(listener);
-    Mutex::Autolock lock(mMutex);
-    mContentsChangedListener = listener;
-}
-
-void SurfaceFlingerConsumer::onSidebandStreamChanged() {
-    FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
-    {
-        Mutex::Autolock lock(mFrameAvailableMutex);
-        unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
-    }
-    sp<ContentsChangedListener> listener;
-    {   // scope for the lock
-        Mutex::Autolock lock(mMutex);
-        ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
-        listener = mContentsChangedListener.promote();
-    }
-
-    if (listener != NULL) {
-        listener->onSidebandStreamChanged();
-    }
-}
-
-void SurfaceFlingerConsumer::onDisconnect() {
-    sp<Layer> l = mLayer.promote();
-    if (l.get()) {
-        l->onDisconnect();
-    }
-}
-
-void SurfaceFlingerConsumer::addAndGetFrameTimestamps(
-        const NewFrameEventsEntry* newTimestamps,
-        FrameEventHistoryDelta *outDelta) {
-    sp<Layer> l = mLayer.promote();
-    if (l.get()) {
-        l->addAndGetFrameTimestamps(newTimestamps, outDelta);
-    }
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
deleted file mode 100644
index 7397f2d..0000000
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2012 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_SURFACEFLINGERCONSUMER_H
-#define ANDROID_SURFACEFLINGERCONSUMER_H
-
-#include "DispSync.h"
-
-#include <ui/Region.h>
-#include <gui/GLConsumer.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Layer;
-
-/*
- * This is a thin wrapper around GLConsumer.
- */
-class SurfaceFlingerConsumer : public GLConsumer {
-public:
-    static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
-
-    struct ContentsChangedListener: public FrameAvailableListener {
-        virtual void onSidebandStreamChanged() = 0;
-    };
-
-    SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
-            uint32_t tex, Layer* layer)
-        : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
-          mTransformToDisplayInverse(false), mSurfaceDamage(), mLayer(layer)
-    {}
-
-    class BufferRejecter {
-        friend class SurfaceFlingerConsumer;
-        virtual bool reject(const sp<GraphicBuffer>& buf,
-                const BufferItem& item) = 0;
-
-    protected:
-        virtual ~BufferRejecter() { }
-    };
-
-    virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen,
-            uint64_t maxFrameNumber = 0) override;
-
-    // This version of updateTexImage() takes a functor that may be used to
-    // reject the newly acquired buffer.  Unlike the GLConsumer version,
-    // this does not guarantee that the buffer has been bound to the GL
-    // texture.
-    status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
-            bool* autoRefresh, bool* queuedBuffer,
-            uint64_t maxFrameNumber);
-
-    // See GLConsumer::bindTextureImageLocked().
-    status_t bindTextureImage();
-
-    bool getTransformToDisplayInverse() const;
-
-    // must be called from SF main thread
-    const Region& getSurfaceDamage() const;
-
-    // Sets the contents changed listener. This should be used instead of
-    // ConsumerBase::setFrameAvailableListener().
-    void setContentsChangedListener(const wp<ContentsChangedListener>& listener);
-
-    nsecs_t computeExpectedPresent(const DispSync& dispSync);
-
-    sp<Fence> getPrevFinalReleaseFence() const;
-    virtual void setReleaseFence(const sp<Fence>& fence) override;
-    bool releasePendingBuffer();
-
-    void onDisconnect() override;
-    void addAndGetFrameTimestamps(
-            const NewFrameEventsEntry* newTimestamps,
-            FrameEventHistoryDelta* outDelta) override;
-
-private:
-    virtual void onSidebandStreamChanged();
-
-    wp<ContentsChangedListener> mContentsChangedListener;
-
-    // Indicates this buffer must be transformed by the inverse transform of the screen
-    // it is displayed onto. This is applied after GLConsumer::mCurrentTransform.
-    // This must be set/read from SurfaceFlinger's main thread.
-    bool mTransformToDisplayInverse;
-
-    // The portion of this surface that has changed since the previous frame
-    Region mSurfaceDamage;
-
-    // A release that is pending on the receipt of a new release fence from
-    // presentDisplay
-    PendingRelease mPendingRelease;
-
-    // The layer for this SurfaceFlingerConsumer
-    const wp<Layer> mLayer;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SURFACEFLINGERCONSUMER_H
diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
index 37925a1..e05ed53 100644
--- a/services/surfaceflinger/Transform.cpp
+++ b/services/surfaceflinger/Transform.cpp
@@ -224,6 +224,27 @@
     return r;
 }
 
+FloatRect Transform::transform(const FloatRect& bounds) const
+{
+    vec2 lt(bounds.left, bounds.top);
+    vec2 rt(bounds.right, bounds.top);
+    vec2 lb(bounds.left, bounds.bottom);
+    vec2 rb(bounds.right, bounds.bottom);
+
+    lt = transform(lt);
+    rt = transform(rt);
+    lb = transform(lb);
+    rb = transform(rb);
+
+    FloatRect r;
+    r.left = min(lt[0], rt[0], lb[0], rb[0]);
+    r.top = min(lt[1], rt[1], lb[1], rb[1]);
+    r.right = max(lt[0], rt[0], lb[0], rb[0]);
+    r.bottom = max(lt[1], rt[1], lb[1], rb[1]);
+
+    return r;
+}
+
 Region Transform::transform(const Region& reg) const
 {
     Region out;
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index bfc66ec..b11d057 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -84,6 +84,7 @@
             Region  transform(const Region& reg) const;
             Rect    transform(const Rect& bounds,
                     bool roundOutwards = false) const;
+            FloatRect transform(const FloatRect& bounds) const;
             Transform operator * (const Transform& rhs) const;
             // assumes the last row is < 0 , 0 , 1 >
             vec2 transform(const vec2& v) const;
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index ed806b8..de78c3f 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -148,7 +148,7 @@
         mBGSurfaceControl = mComposerClient->createSurface(
                 String8("BG Interceptor Test Surface"), displayWidth, displayHeight,
                 PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mBGSurfaceControl != NULL);
+        ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
         mBGLayerId = getSurfaceId("BG Interceptor Test Surface");
 
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index ff81dc9..ac8a2ad 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -148,8 +148,8 @@
                              bool unlock = true) {
     ANativeWindow_Buffer outBuffer;
     sp<Surface> s = sc->getSurface();
-    ASSERT_TRUE(s != NULL);
-    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, NULL));
+    ASSERT_TRUE(s != nullptr);
+    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
     uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
     for (int y = 0; y < outBuffer.height; y++) {
         for (int x = 0; x < outBuffer.width; x++) {
@@ -279,7 +279,7 @@
 
 private:
     sp<GraphicBuffer> mOutBuffer;
-    uint8_t* mPixels = NULL;
+    uint8_t* mPixels = nullptr;
 };
 
 class LayerTransactionTest : public ::testing::Test {
@@ -1483,14 +1483,14 @@
         mBGSurfaceControl =
                 mComposerClient->createSurface(String8("BG Test Surface"), displayWidth,
                                                displayHeight, PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mBGSurfaceControl != NULL);
+        ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
         fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
 
         // Foreground surface
         mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
                                                            PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mFGSurfaceControl != NULL);
+        ASSERT_TRUE(mFGSurfaceControl != nullptr);
         ASSERT_TRUE(mFGSurfaceControl->isValid());
 
         fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
@@ -1498,7 +1498,7 @@
         // Synchronization surface
         mSyncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
                                                              PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mSyncSurfaceControl != NULL);
+        ASSERT_TRUE(mSyncSurfaceControl != nullptr);
         ASSERT_TRUE(mSyncSurfaceControl->isValid());
 
         fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
@@ -2018,7 +2018,7 @@
             mNewComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
                                               PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
 
-    ASSERT_TRUE(mChildNewClient != NULL);
+    ASSERT_TRUE(mChildNewClient != nullptr);
     ASSERT_TRUE(mChildNewClient->isValid());
 
     fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
@@ -2210,7 +2210,7 @@
 TEST_F(ChildLayerTest, ReparentFromNoParent) {
     sp<SurfaceControl> newSurface = mComposerClient->createSurface(String8("New Surface"), 10, 10,
                                                                    PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(newSurface != NULL);
+    ASSERT_TRUE(newSurface != nullptr);
     ASSERT_TRUE(newSurface->isValid());
 
     fillSurfaceRGBA8(newSurface, 63, 195, 63);
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index d949d48..e16e7ec 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -156,6 +156,10 @@
 
 FakeComposerClient::~FakeComposerClient() {}
 
+bool FakeComposerClient::hasCapability(hwc2_capability_t /*capability*/) {
+    return false;
+}
+
 void FakeComposerClient::removeClient() {
     ALOGV("removeClient");
     // TODO: Ahooga! Only thing current lifetime management choices in
@@ -177,6 +181,12 @@
     }
 }
 
+void FakeComposerClient::refreshDisplay(Display display) {
+    if (mCallbacksOn) {
+        mClient->onRefresh(display);
+    }
+}
+
 uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
     ALOGV("getMaxVirtualDisplayCount");
     return 1;
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index b944182..cef7f5b 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -59,6 +59,8 @@
     FakeComposerClient();
     virtual ~FakeComposerClient();
 
+    bool hasCapability(hwc2_capability_t capability) override;
+
     void removeClient() override;
     void enableCallback(bool enable) override;
     uint32_t getMaxVirtualDisplayCount() override;
@@ -140,6 +142,7 @@
     Layer getLayer(size_t index) const;
 
     void hotplugDisplay(Display display, IComposerCallback::Connection state);
+    void refreshDisplay(Display display);
 
 private:
     LayerImpl& getLayerImpl(Layer handle);
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 8a97ea4..1873832 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -22,25 +22,22 @@
 #include "FakeComposerService.h"
 #include "FakeComposerUtils.h"
 
+#include <gui/DisplayEventReceiver.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 
-#include <private/gui/ComposerService.h>
-
-#include <ui/DisplayInfo.h>
-
-#include <android/native_window.h>
-
 #include <android/hidl/manager/1.0/IServiceManager.h>
-
-#include <hwbinder/ProcessState.h>
-
+#include <android/looper.h>
+#include <android/native_window.h>
 #include <binder/ProcessState.h>
-
+#include <hwbinder/ProcessState.h>
 #include <log/log.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayInfo.h>
+#include <utils/Looper.h>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -142,13 +139,22 @@
     };
 
 protected:
+    static int processDisplayEvents(int fd, int events, void* data);
+
     void SetUp() override;
     void TearDown() override;
 
+    void waitForDisplayTransaction();
+    bool waitForHotplugEvent(uint32_t id, bool connected);
+
     sp<IComposer> mFakeService;
     sp<SurfaceComposerClient> mComposerClient;
 
     MockComposerClient* mMockComposer;
+
+    std::unique_ptr<DisplayEventReceiver> mReceiver;
+    sp<Looper> mLooper;;
+    std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
 };
 
 void DisplayTest::SetUp() {
@@ -188,9 +194,16 @@
 
     mComposerClient = new SurfaceComposerClient;
     ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+    mReceiver.reset(new DisplayEventReceiver());
+    mLooper = new Looper(false);
+    mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
 }
 
 void DisplayTest::TearDown() {
+    mLooper = nullptr;
+    mReceiver = nullptr;
+
     mComposerClient->dispose();
     mComposerClient = nullptr;
 
@@ -204,6 +217,55 @@
     mMockComposer = nullptr;
 }
 
+
+int DisplayTest::processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
+    auto self = static_cast<DisplayTest*>(data);
+
+    ssize_t n;
+    DisplayEventReceiver::Event buffer[1];
+
+    while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
+        for (int i=0 ; i<n ; i++) {
+            self->mReceivedDisplayEvents.push_back(buffer[i]);
+        }
+    }
+    ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
+    return 1;
+}
+
+void DisplayTest::waitForDisplayTransaction() {
+    // Both a refresh and a vsync event are needed to apply pending display
+    // transactions.
+    mMockComposer->refreshDisplay(EXTERNAL_DISPLAY);
+    mMockComposer->runVSyncAndWait();
+
+    // Extra vsync and wait to avoid a 10% flake due to a race.
+    mMockComposer->runVSyncAndWait();
+}
+
+bool DisplayTest::waitForHotplugEvent(uint32_t id, bool connected) {
+    int waitCount = 20;
+    while (waitCount--) {
+        while (!mReceivedDisplayEvents.empty()) {
+            auto event = mReceivedDisplayEvents.front();
+            mReceivedDisplayEvents.pop_front();
+
+            ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
+                    "event hotplug: id %d, connected %d\t", event.header.id,
+                    event.hotplug.connected);
+
+            if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
+                event.header.id == id && event.hotplug.connected == connected) {
+                return true;
+            }
+        }
+
+        mLooper->pollOnce(1);
+    }
+
+    return false;
+}
+
 TEST_F(DisplayTest, Hotplug) {
     ALOGD("DisplayTest::Hotplug");
 
@@ -215,7 +277,7 @@
     EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
             .Times(2 * 3)
             .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-    // ... and then special handling for dimensions. Specifying this
+    // ... and then special handling for dimensions. Specifying these
     // rules later means that gmock will try them first, i.e.,
     // ordering of width/height vs. the default implementation for
     // other queries is significant.
@@ -229,11 +291,12 @@
             .Times(2)
             .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
 
-    // TODO: Width and height queries are not actually called. Display
-    // info returns dimensions 0x0 in display info. Why?
-
     mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
 
+    waitForDisplayTransaction();
+
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+
     {
         sp<android::IBinder> display(
                 SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
@@ -264,6 +327,11 @@
 
     mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
 
+    waitForDisplayTransaction();
+
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, false));
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+
     {
         sp<android::IBinder> display(
                 SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
@@ -290,6 +358,64 @@
     mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
 }
 
+TEST_F(DisplayTest, HotplugPrimaryDisplay) {
+    ALOGD("DisplayTest::HotplugPrimaryDisplay");
+
+    mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+
+    waitForDisplayTransaction();
+
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, false));
+
+    {
+        sp<android::IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+        DisplayInfo info;
+        auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+        EXPECT_NE(NO_ERROR, result);
+    }
+
+    mMockComposer->clearFrames();
+
+    EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+                                  Return(Error::NONE)));
+    // The attribute queries will get done twice. This is for defaults
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
+            .Times(2 * 3)
+            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+    // ... and then special handling for dimensions. Specifying these
+    // rules later means that gmock will try them first, i.e.,
+    // ordering of width/height vs. the default implementation for
+    // other queries is significant.
+    EXPECT_CALL(*mMockComposer,
+                getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+
+    EXPECT_CALL(*mMockComposer,
+                getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+
+    mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+
+    waitForDisplayTransaction();
+
+    EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, true));
+
+    {
+        sp<android::IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+        DisplayInfo info;
+        auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+        EXPECT_EQ(NO_ERROR, result);
+        ASSERT_EQ(400u, info.w);
+        ASSERT_EQ(200u, info.h);
+    }
+}
+
 ////////////////////////////////////////////////
 
 class TransactionTest : public ::testing::Test {
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index fd271d0..d5664d5 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -232,7 +232,7 @@
 
 VrHwc::~VrHwc() {}
 
-bool VrHwc::hasCapability(Capability /* capability */) const { return false; }
+bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
 
 void VrHwc::removeClient() {
   std::lock_guard<std::mutex> guard(mutex_);
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
index fce9a06..eff721b 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -196,8 +196,6 @@
   VrHwc();
   ~VrHwc() override;
 
-  bool hasCapability(Capability capability) const;
-
   Error setLayerInfo(Display display, Layer layer, uint32_t type,
                      uint32_t appId);
   Error setClientTargetMetadata(
@@ -207,6 +205,8 @@
       const IVrComposerClient::BufferMetadata& metadata);
 
   // ComposerBase
+  bool hasCapability(hwc2_capability_t capability) override;
+
   void removeClient() override;
   void enableCallback(bool enable) override;
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index ade0bde..26f60fb 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -19,9 +19,9 @@
 #include <string.h>
 #include <sys/prctl.h>
 
+#include <dlfcn.h>
 #include <algorithm>
 #include <array>
-#include <dlfcn.h>
 #include <new>
 
 #include <log/log.h>
@@ -1047,17 +1047,54 @@
     VkInstance instance,
     uint32_t* pPhysicalDeviceGroupCount,
     VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
+    VkResult result = VK_SUCCESS;
     const auto& data = GetData(instance);
 
-    VkResult result = data.driver.EnumeratePhysicalDeviceGroups(
-        instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
-    if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
-        *pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
-        for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
-            for (uint32_t j = 0;
-                 j < pPhysicalDeviceGroupProperties->physicalDeviceCount; j++) {
-                SetData(pPhysicalDeviceGroupProperties->physicalDevices[j],
+    if (!data.driver.EnumeratePhysicalDeviceGroups) {
+        uint32_t device_count = 0;
+        result = EnumeratePhysicalDevices(instance, &device_count, nullptr);
+        if (result < 0)
+            return result;
+        if (!pPhysicalDeviceGroupProperties) {
+            *pPhysicalDeviceGroupCount = device_count;
+            return result;
+        }
+
+        device_count = std::min(device_count, *pPhysicalDeviceGroupCount);
+        if (!device_count) {
+            *pPhysicalDeviceGroupCount = 0;
+            return result;
+        }
+
+        android::Vector<VkPhysicalDevice> devices;
+        devices.resize(device_count);
+
+        result = EnumeratePhysicalDevices(instance, &device_count,
+                                          devices.editArray());
+        if (result < 0)
+            return result;
+
+        devices.resize(device_count);
+        *pPhysicalDeviceGroupCount = device_count;
+        for (uint32_t i = 0; i < device_count; ++i) {
+            pPhysicalDeviceGroupProperties[i].physicalDeviceCount = 1;
+            pPhysicalDeviceGroupProperties[i].physicalDevices[0] = devices[i];
+            pPhysicalDeviceGroupProperties[i].subsetAllocation = 0;
+        }
+    } else {
+        result = data.driver.EnumeratePhysicalDeviceGroups(
+            instance, pPhysicalDeviceGroupCount,
+            pPhysicalDeviceGroupProperties);
+        if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
+            *pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
+            for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
+                for (uint32_t j = 0;
+                     j < pPhysicalDeviceGroupProperties[i].physicalDeviceCount;
+                     j++) {
+                    SetData(
+                        pPhysicalDeviceGroupProperties[i].physicalDevices[j],
                         data);
+                }
             }
         }
     }
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index 1745925..d1f6241 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -1,12 +1,15 @@
 LIBVULKAN {
   global:
+    vkAcquireNextImage2KHR; # introduced=28
     vkAcquireNextImageKHR;
     vkAllocateCommandBuffers;
     vkAllocateDescriptorSets;
     vkAllocateMemory;
     vkBeginCommandBuffer;
     vkBindBufferMemory;
+    vkBindBufferMemory2; # introduced=28
     vkBindImageMemory;
+    vkBindImageMemory2; # introduced=28
     vkCmdBeginQuery;
     vkCmdBeginRenderPass;
     vkCmdBindDescriptorSets;
@@ -23,6 +26,7 @@
     vkCmdCopyImageToBuffer;
     vkCmdCopyQueryPoolResults;
     vkCmdDispatch;
+    vkCmdDispatchBase; # introduced=28
     vkCmdDispatchIndirect;
     vkCmdDraw;
     vkCmdDrawIndexed;
@@ -41,6 +45,7 @@
     vkCmdSetBlendConstants;
     vkCmdSetDepthBias;
     vkCmdSetDepthBounds;
+    vkCmdSetDeviceMask; # introduced=28
     vkCmdSetEvent;
     vkCmdSetLineWidth;
     vkCmdSetScissor;
@@ -58,6 +63,7 @@
     vkCreateComputePipelines;
     vkCreateDescriptorPool;
     vkCreateDescriptorSetLayout;
+    vkCreateDescriptorUpdateTemplate; # introduced=28
     vkCreateDevice;
     vkCreateEvent;
     vkCreateFence;
@@ -71,6 +77,7 @@
     vkCreateQueryPool;
     vkCreateRenderPass;
     vkCreateSampler;
+    vkCreateSamplerYcbcrConversion; # introduced=28
     vkCreateSemaphore;
     vkCreateShaderModule;
     vkCreateSwapchainKHR;
@@ -79,6 +86,7 @@
     vkDestroyCommandPool;
     vkDestroyDescriptorPool;
     vkDestroyDescriptorSetLayout;
+    vkDestroyDescriptorUpdateTemplate; # introduced=28
     vkDestroyDevice;
     vkDestroyEvent;
     vkDestroyFence;
@@ -92,6 +100,7 @@
     vkDestroyQueryPool;
     vkDestroyRenderPass;
     vkDestroySampler;
+    vkDestroySamplerYcbcrConversion; # introduced=28
     vkDestroySemaphore;
     vkDestroyShaderModule;
     vkDestroySurfaceKHR;
@@ -102,28 +111,49 @@
     vkEnumerateDeviceLayerProperties;
     vkEnumerateInstanceExtensionProperties;
     vkEnumerateInstanceLayerProperties;
+    vkEnumerateInstanceVersion; # introduced=28
+    vkEnumeratePhysicalDeviceGroups; # introduced=28
     vkEnumeratePhysicalDevices;
     vkFlushMappedMemoryRanges;
     vkFreeCommandBuffers;
     vkFreeDescriptorSets;
     vkFreeMemory;
     vkGetBufferMemoryRequirements;
+    vkGetBufferMemoryRequirements2; # introduced=28
+    vkGetDescriptorSetLayoutSupport; # introduced=28
+    vkGetDeviceGroupPeerMemoryFeatures; # introduced=28
+    vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28
+    vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
     vkGetDeviceMemoryCommitment;
     vkGetDeviceProcAddr;
     vkGetDeviceQueue;
+    vkGetDeviceQueue2; # introduced=28
     vkGetEventStatus;
     vkGetFenceStatus;
     vkGetImageMemoryRequirements;
+    vkGetImageMemoryRequirements2; # introduced=28
     vkGetImageSparseMemoryRequirements;
+    vkGetImageSparseMemoryRequirements2; # introduced=28
     vkGetImageSubresourceLayout;
     vkGetInstanceProcAddr;
+    vkGetPhysicalDeviceExternalBufferProperties; # introduced=28
+    vkGetPhysicalDeviceExternalFenceProperties; # introduced=28
+    vkGetPhysicalDeviceExternalSemaphoreProperties; # introduced=28
     vkGetPhysicalDeviceFeatures;
+    vkGetPhysicalDeviceFeatures2; # introduced=28
     vkGetPhysicalDeviceFormatProperties;
+    vkGetPhysicalDeviceFormatProperties2; # introduced=28
     vkGetPhysicalDeviceImageFormatProperties;
+    vkGetPhysicalDeviceImageFormatProperties2; # introduced=28
     vkGetPhysicalDeviceMemoryProperties;
+    vkGetPhysicalDeviceMemoryProperties2; # introduced=28
+    vkGetPhysicalDevicePresentRectanglesKHR; # introduced=28
     vkGetPhysicalDeviceProperties;
+    vkGetPhysicalDeviceProperties2; # introduced=28
     vkGetPhysicalDeviceQueueFamilyProperties;
+    vkGetPhysicalDeviceQueueFamilyProperties2; # introduced=28
     vkGetPhysicalDeviceSparseImageFormatProperties;
+    vkGetPhysicalDeviceSparseImageFormatProperties2; # introduced=28
     vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
     vkGetPhysicalDeviceSurfaceFormatsKHR;
     vkGetPhysicalDeviceSurfacePresentModesKHR;
@@ -145,8 +175,10 @@
     vkResetEvent;
     vkResetFences;
     vkSetEvent;
+    vkTrimCommandPool; # introduced=28
     vkUnmapMemory;
     vkUpdateDescriptorSets;
+    vkUpdateDescriptorSetWithTemplate; # introduced=28
     vkWaitForFences;
   local:
     *;