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(×tamp, &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(×tamp, &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 ¤t,
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:
*;