Merge "Make dataspace accessible to surface flinger"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index b1338f8..4be0432 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
- #define LOG_TAG "atrace"
+#define LOG_TAG "atrace"
#include <errno.h>
#include <fcntl.h>
@@ -112,81 +112,81 @@
{ "adb", "ADB", ATRACE_TAG_ADB, { } },
{ k_coreServiceCategory, "Core services", 0, { } },
{ "sched", "CPU Scheduling", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable" },
+ { REQ, "events/sched/sched_switch/enable" },
+ { REQ, "events/sched/sched_wakeup/enable" },
+ { OPT, "events/sched/sched_blocked_reason/enable" },
+ { OPT, "events/sched/sched_cpu_hotplug/enable" },
} },
{ "irq", "IRQ Events", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/irq/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/ipi/enable" },
+ { REQ, "events/irq/enable" },
+ { OPT, "events/ipi/enable" },
} },
{ "i2c", "I2C Events", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/i2c/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_read/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_write/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_result/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_reply/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/i2c/smbus_read/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/i2c/smbus_write/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/i2c/smbus_result/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/i2c/smbus_reply/enable" },
+ { REQ, "events/i2c/enable" },
+ { REQ, "events/i2c/i2c_read/enable" },
+ { REQ, "events/i2c/i2c_write/enable" },
+ { REQ, "events/i2c/i2c_result/enable" },
+ { REQ, "events/i2c/i2c_reply/enable" },
+ { OPT, "events/i2c/smbus_read/enable" },
+ { OPT, "events/i2c/smbus_write/enable" },
+ { OPT, "events/i2c/smbus_result/enable" },
+ { OPT, "events/i2c/smbus_reply/enable" },
} },
{ "freq", "CPU Frequency", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable" },
+ { REQ, "events/power/cpu_frequency/enable" },
+ { OPT, "events/power/clock_set_rate/enable" },
+ { OPT, "events/power/cpu_frequency_limits/enable" },
} },
{ "membus", "Memory Bus Utilization", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/memory_bus/enable" },
+ { REQ, "events/memory_bus/enable" },
} },
{ "idle", "CPU Idle", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" },
+ { REQ, "events/power/cpu_idle/enable" },
} },
{ "disk", "Disk I/O", 0, {
- { OPT, "/sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" },
+ { OPT, "events/f2fs/f2fs_sync_file_enter/enable" },
+ { OPT, "events/f2fs/f2fs_sync_file_exit/enable" },
+ { OPT, "events/f2fs/f2fs_write_begin/enable" },
+ { OPT, "events/f2fs/f2fs_write_end/enable" },
+ { OPT, "events/ext4/ext4_da_write_begin/enable" },
+ { OPT, "events/ext4/ext4_da_write_end/enable" },
+ { OPT, "events/ext4/ext4_sync_file_enter/enable" },
+ { OPT, "events/ext4/ext4_sync_file_exit/enable" },
+ { REQ, "events/block/block_rq_issue/enable" },
+ { REQ, "events/block/block_rq_complete/enable" },
} },
{ "mmc", "eMMC commands", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/mmc/enable" },
+ { REQ, "events/mmc/enable" },
} },
{ "load", "CPU Load", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" },
+ { REQ, "events/cpufreq_interactive/enable" },
} },
{ "sync", "Synchronization", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/sync/enable" },
+ { REQ, "events/sync/enable" },
} },
{ "workq", "Kernel Workqueues", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/workqueue/enable" },
+ { REQ, "events/workqueue/enable" },
} },
{ "memreclaim", "Kernel Memory Reclaim", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable" },
+ { REQ, "events/vmscan/mm_vmscan_direct_reclaim_begin/enable" },
+ { REQ, "events/vmscan/mm_vmscan_direct_reclaim_end/enable" },
+ { REQ, "events/vmscan/mm_vmscan_kswapd_wake/enable" },
+ { REQ, "events/vmscan/mm_vmscan_kswapd_sleep/enable" },
} },
{ "regulators", "Voltage and Current Regulators", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/regulator/enable" },
+ { REQ, "events/regulator/enable" },
} },
{ "binder_driver", "Binder Kernel driver", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/binder/binder_transaction/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable" },
+ { REQ, "events/binder/binder_transaction/enable" },
+ { REQ, "events/binder/binder_transaction_received/enable" },
} },
{ "binder_lock", "Binder global lock trace", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/binder/binder_lock/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/binder/binder_locked/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/binder/binder_unlock/enable" },
+ { REQ, "events/binder/binder_lock/enable" },
+ { REQ, "events/binder/binder_locked/enable" },
+ { REQ, "events/binder/binder_unlock/enable" },
} },
{ "pagecache", "Page cache", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/filemap/enable" },
+ { REQ, "events/filemap/enable" },
} },
};
@@ -205,61 +205,62 @@
/* Global state */
static bool g_traceAborted = false;
static bool g_categoryEnables[NELEM(k_categories)] = {};
+static std::string g_traceFolder;
/* Sys file paths */
static const char* k_traceClockPath =
- "/sys/kernel/debug/tracing/trace_clock";
+ "trace_clock";
static const char* k_traceBufferSizePath =
- "/sys/kernel/debug/tracing/buffer_size_kb";
+ "buffer_size_kb";
static const char* k_tracingOverwriteEnablePath =
- "/sys/kernel/debug/tracing/options/overwrite";
+ "options/overwrite";
static const char* k_currentTracerPath =
- "/sys/kernel/debug/tracing/current_tracer";
+ "current_tracer";
static const char* k_printTgidPath =
- "/sys/kernel/debug/tracing/options/print-tgid";
+ "options/print-tgid";
static const char* k_funcgraphAbsTimePath =
- "/sys/kernel/debug/tracing/options/funcgraph-abstime";
+ "options/funcgraph-abstime";
static const char* k_funcgraphCpuPath =
- "/sys/kernel/debug/tracing/options/funcgraph-cpu";
+ "options/funcgraph-cpu";
static const char* k_funcgraphProcPath =
- "/sys/kernel/debug/tracing/options/funcgraph-proc";
+ "options/funcgraph-proc";
static const char* k_funcgraphFlatPath =
- "/sys/kernel/debug/tracing/options/funcgraph-flat";
+ "options/funcgraph-flat";
static const char* k_funcgraphDurationPath =
- "/sys/kernel/debug/tracing/options/funcgraph-duration";
+ "options/funcgraph-duration";
static const char* k_ftraceFilterPath =
- "/sys/kernel/debug/tracing/set_ftrace_filter";
+ "set_ftrace_filter";
static const char* k_tracingOnPath =
- "/sys/kernel/debug/tracing/tracing_on";
+ "tracing_on";
static const char* k_tracePath =
- "/sys/kernel/debug/tracing/trace";
+ "trace";
static const char* k_traceStreamPath =
- "/sys/kernel/debug/tracing/trace_pipe";
+ "trace_pipe";
static const char* k_traceMarkerPath =
- "/sys/kernel/debug/tracing/trace_marker";
+ "trace_marker";
// Check whether a file exists.
static bool fileExists(const char* filename) {
- return access(filename, F_OK) != -1;
+ return access((g_traceFolder + filename).c_str(), F_OK) != -1;
}
// Check whether a file is writable.
static bool fileIsWritable(const char* filename) {
- return access(filename, W_OK) != -1;
+ return access((g_traceFolder + filename).c_str(), W_OK) != -1;
}
// Truncate a file.
@@ -268,9 +269,9 @@
// This uses creat rather than truncate because some of the debug kernel
// device nodes (e.g. k_ftraceFilterPath) currently aren't changed by
// calls to truncate, but they are cleared by calls to creat.
- int traceFD = creat(path, 0);
+ int traceFD = creat((g_traceFolder + path).c_str(), 0);
if (traceFD == -1) {
- fprintf(stderr, "error truncating %s: %s (%d)\n", path,
+ fprintf(stderr, "error truncating %s: %s (%d)\n", (g_traceFolder + path).c_str(),
strerror(errno), errno);
return false;
}
@@ -282,9 +283,10 @@
static bool _writeStr(const char* filename, const char* str, int flags)
{
- int fd = open(filename, flags);
+ std::string fullFilename = g_traceFolder + filename;
+ int fd = open(fullFilename.c_str(), flags);
if (fd == -1) {
- fprintf(stderr, "error opening %s: %s (%d)\n", filename,
+ fprintf(stderr, "error opening %s: %s (%d)\n", fullFilename.c_str(),
strerror(errno), errno);
return false;
}
@@ -292,7 +294,7 @@
bool ok = true;
ssize_t len = strlen(str);
if (write(fd, str, len) != len) {
- fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
+ fprintf(stderr, "error writing to %s: %s (%d)\n", fullFilename.c_str(),
strerror(errno), errno);
ok = false;
}
@@ -318,7 +320,7 @@
{
char buffer[128];
int len = 0;
- int fd = open(k_traceMarkerPath, O_WRONLY);
+ int fd = open((g_traceFolder + k_traceMarkerPath).c_str(), O_WRONLY);
if (fd == -1) {
fprintf(stderr, "error opening %s: %s (%d)\n", k_traceMarkerPath,
strerror(errno), errno);
@@ -439,7 +441,7 @@
// local [global] counter uptime perf
static bool isTraceClock(const char *mode)
{
- int fd = open(k_traceClockPath, O_RDONLY);
+ int fd = open((g_traceFolder + k_traceClockPath).c_str(), O_RDONLY);
if (fd == -1) {
fprintf(stderr, "error opening %s: %s (%d)\n", k_traceClockPath,
strerror(errno), errno);
@@ -562,7 +564,8 @@
}
});
if (!listRet.isOk()) {
- fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str());
+ // TODO(b/34242478) fix this when we determine the correct ACL
+ //fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str());
}
}
@@ -651,7 +654,7 @@
static bool verifyKernelTraceFuncs(const char* funcs)
{
std::string buf;
- if (!android::base::ReadFileToString(k_ftraceFilterPath, &buf)) {
+ if (!android::base::ReadFileToString(g_traceFolder + k_ftraceFilterPath, &buf)) {
fprintf(stderr, "error opening %s: %s (%d)\n", k_ftraceFilterPath,
strerror(errno), errno);
return false;
@@ -872,7 +875,7 @@
static void streamTrace()
{
char trace_data[4096];
- int traceFD = open(k_traceStreamPath, O_RDWR);
+ int traceFD = open((g_traceFolder + k_traceStreamPath).c_str(), O_RDWR);
if (traceFD == -1) {
fprintf(stderr, "error opening %s: %s (%d)\n", k_traceStreamPath,
strerror(errno), errno);
@@ -897,7 +900,7 @@
static void dumpTrace(int outFd)
{
ALOGI("Dumping trace");
- int traceFD = open(k_tracePath, O_RDWR);
+ int traceFD = open((g_traceFolder + k_tracePath).c_str(), O_RDWR);
if (traceFD == -1) {
fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
strerror(errno), errno);
@@ -1052,6 +1055,29 @@
);
}
+bool findTraceFiles()
+{
+ static const std::string debugfs_path = "/sys/kernel/debug/tracing/";
+ static const std::string tracefs_path = "/sys/kernel/tracing/";
+ static const std::string trace_file = "trace_marker";
+
+ bool tracefs = access((tracefs_path + trace_file).c_str(), F_OK) != -1;
+ bool debugfs = access((debugfs_path + trace_file).c_str(), F_OK) != -1;
+
+ if (!tracefs && !debugfs) {
+ fprintf(stderr, "Error: Did not find trace folder\n");
+ return false;
+ }
+
+ if (tracefs) {
+ g_traceFolder = tracefs_path;
+ } else {
+ g_traceFolder = debugfs_path;
+ }
+
+ return true;
+}
+
int main(int argc, char **argv)
{
bool async = false;
@@ -1065,6 +1091,11 @@
exit(0);
}
+ if (!findTraceFiles()) {
+ fprintf(stderr, "No trace folder found\n");
+ exit(-1);
+ }
+
for (;;) {
int ret;
int option_index = 0;
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 54ba5ca..cef41be 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -4,72 +4,131 @@
# Allow writing to the kernel trace log.
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/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/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/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/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/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
# 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
on property:persist.debug.atrace.boottrace=1
start boottrace
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index af87ffa..efc050b 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -20,14 +20,13 @@
libbase \
libbinder \
libcutils \
+ libdebuggerd_client \
libdumpstateaidl \
- libhardware_legacy \
+ libdumpstateutil \
liblog \
libselinux \
libutils \
$(COMMON_ZIP_LIBRARIES)
-COMMON_STATIC_LIBRARIES := \
- libdumpstateutil
# ====================#
# libdumpstateutil #
@@ -43,9 +42,10 @@
DumpstateInternal.cpp \
DumpstateUtil.cpp
LOCAL_SHARED_LIBRARIES := \
- libbase
+ libbase \
+ liblog \
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_SHARED_LIBRARY)
# ====================#
# libdumpstateheaders #
@@ -108,8 +108,6 @@
LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES)
-LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
-
LOCAL_CFLAGS += $(COMMON_LOCAL_CFLAGS)
LOCAL_INIT_RC := dumpstate.rc
@@ -182,14 +180,3 @@
LOCAL_PICKUP_FILES := $(dumpstate_tests_intermediates)
include $(BUILD_NATIVE_TEST)
-
-# =======================#
-# libdumpstate.default #
-# =======================#
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := libdumpstate_default.cpp
-LOCAL_MODULE := libdumpstate.default
-
-LOCAL_STATIC_LIBRARIES := libdumpstateheaders
-include $(BUILD_STATIC_LIBRARY)
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 2d74377..0343277 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -52,8 +52,8 @@
return false;
}
- gid_t groups[] = {AID_LOG, AID_SDCARD_R, AID_SDCARD_RW, AID_MOUNT, AID_INET,
- AID_NET_BW_STATS, AID_READPROC, AID_WAKELOCK, AID_BLUETOOTH};
+ gid_t groups[] = {AID_LOG, AID_SDCARD_R, AID_SDCARD_RW, AID_MOUNT,
+ AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_BLUETOOTH};
if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
return false;
@@ -74,10 +74,8 @@
capheader.version = _LINUX_CAPABILITY_VERSION_3;
capheader.pid = 0;
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted =
- (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective =
- (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
capdata[0].inheritable = 0;
capdata[1].inheritable = 0;
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 9faa63e..26702c4 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -18,6 +18,7 @@
#include "DumpstateUtil.h"
+#include <dirent.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
@@ -25,15 +26,22 @@
#include <vector>
+#include <android-base/file.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/log.h>
#include "DumpstateInternal.h"
-// TODO: move to unnamed namespace
+namespace android {
+namespace os {
+namespace dumpstate {
+
+namespace {
+
static constexpr const char* kSuPath = "/system/xbin/su";
-// TODO: move to unnamed namespace
static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
sigset_t child_mask, old_mask;
sigemptyset(&child_mask);
@@ -77,10 +85,10 @@
}
return true;
}
+} // unnamed namespace
CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build();
-CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
}
@@ -342,3 +350,35 @@
return status;
}
+
+int GetPidByName(const std::string& ps_name) {
+ DIR* proc_dir;
+ struct dirent* ps;
+ unsigned int pid;
+ std::string cmdline;
+
+ if (!(proc_dir = opendir("/proc"))) {
+ MYLOGE("Can't open /proc\n");
+ return -1;
+ }
+
+ while ((ps = readdir(proc_dir))) {
+ if (!(pid = atoi(ps->d_name))) {
+ continue;
+ }
+ android::base::ReadFileToString("/proc/" + std::string(ps->d_name) + "/cmdline", &cmdline);
+ if (cmdline.find(ps_name) == std::string::npos) {
+ continue;
+ } else {
+ closedir(proc_dir);
+ return pid;
+ }
+ }
+ MYLOGE("can't find the pid\n");
+ closedir(proc_dir);
+ return -1;
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index 8bda6f2..5a8ce5b 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
-#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
+#ifndef ANDROID_OS_DUMPSTATE_UTIL_H_
+#define ANDROID_OS_DUMPSTATE_UTIL_H_
#include <cstdint>
#include <string>
-// TODO: use android::os::dumpstate (must wait until device code is refactored)
+namespace android {
+namespace os {
+namespace dumpstate {
/*
* Defines the Linux account that should be executing a command.
@@ -120,9 +122,6 @@
// Common options.
static CommandOptions DEFAULT;
static CommandOptions AS_ROOT;
-
- // TODO: temporary, until device implementations use AS_ROOT
- static CommandOptions AS_ROOT_5;
};
/*
@@ -174,4 +173,14 @@
*/
int DumpFileToFd(int fd, const std::string& title, const std::string& path);
-#endif // FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
+/*
+ * Finds the process id by process name.
+ * |ps_name| the process name we want to search for
+ */
+int GetPidByName(const std::string& ps_name);
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif // ANDROID_OS_DUMPSTATE_UTIL_H_
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
new file mode 100644
index 0000000..0302ea5
--- /dev/null
+++ b/cmds/dumpstate/README.md
@@ -0,0 +1,103 @@
+# `dumpstate` development tips
+
+## To build `dumpstate`
+
+Do a full build first:
+
+```
+m -j dumpstate
+```
+
+Then incremental ones:
+
+```
+mmm -j frameworks/native/cmds/dumpstate
+```
+
+If you're working on device-specific code, you might need to build them as well. Example:
+
+```
+mmm -j frameworks/native/cmds/dumpstate device/acme/secret_device/dumpstate/ hardware/interfaces/dumpstate
+```
+
+## To build, deploy, and take a bugreport
+
+```
+mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb shell am bug-report
+```
+
+## To build, deploy, and run unit tests
+
+First create `/data/nativetest`:
+
+```
+adb shell mkdir /data/nativetest
+```
+
+Then run:
+
+```
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test
+```
+
+And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`):
+
+```
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs
+```
+
+## To take quick bugreports
+
+```
+adb shell setprop dumpstate.dry_run true
+```
+
+## To change the `dumpstate` version
+
+```
+adb shell setprop dumpstate.version VERSION_NAME
+```
+
+Example:
+
+```
+adb shell setprop dumpstate.version split-dumpsys && adb shell dumpstate -v
+```
+
+
+Then to restore the default version:
+
+```
+adb shell setprop dumpstate.version default
+```
+
+## Code style and formatting
+
+Use the style defined at the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
+and make sure to run the following command prior to `repo upload`:
+
+```
+git clang-format --style=file HEAD~
+```
+
+## Useful Bash tricks
+
+```
+export BR_DIR=/bugreports
+
+alias br='adb shell cmd activity bug-report'
+alias ls_bugs='adb shell ls -l ${BR_DIR}/'
+
+unzip_bug() {
+ adb pull ${BR_DIR}/$1 && emacs $1 && mv $1 /tmp
+}
+
+less_bug() {
+ adb pull ${BR_DIR}/$1 && less $1 && mv $1 /tmp
+}
+
+rm_bugs() {
+ if [ -z "${BR_DIR}" ] ; then echo "Variable BR_DIR not set"; else adb shell rm -rf ${BR_DIR}/*; fi
+}
+
+```
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1e33439..dca0695 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -24,7 +24,6 @@
#include <memory>
#include <regex>
#include <set>
-#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -46,7 +45,6 @@
#include <android/hardware/vibrator/1.0/IVibrator.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
-#include <hardware_legacy/power.h>
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -59,6 +57,12 @@
using ::android::hardware::vibrator::V1_0::IVibrator;
using VibratorStatus = ::android::hardware::vibrator::V1_0::Status;
+// TODO: remove once moved to namespace
+using android::os::dumpstate::CommandOptions;
+using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::GetPidByName;
+
/* read before root is shed */
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = NULL;
@@ -83,7 +87,6 @@
#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4)
#define NUM_TOMBSTONES 10
#define WLUTIL "/vendor/xbin/wlutil"
-#define WAKE_LOCK_NAME "dumpstate_wakelock"
typedef struct {
char name[TOMBSTONE_MAX_LEN];
@@ -110,6 +113,9 @@
// Relative directory (inside the zip) for all files copied as-is into the bugreport.
static const std::string ZIP_ROOT_DIR = "FS";
+// Must be hardcoded because dumpstate HAL implementation need SELinux access to it
+static const std::string kDumpstateBoardPath = "/bugreports/dumpstate_board.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";
@@ -192,34 +198,7 @@
closedir(d);
}
-// return pid of a userspace process. If not found or error, return 0.
-static unsigned int pid_of_process(const char* ps_name) {
- DIR *proc_dir;
- struct dirent *ps;
- unsigned int pid;
- std::string cmdline;
- if (!(proc_dir = opendir("/proc"))) {
- MYLOGE("Can't open /proc\n");
- return 0;
- }
-
- while ((ps = readdir(proc_dir))) {
- if (!(pid = atoi(ps->d_name))) {
- continue;
- }
- android::base::ReadFileToString("/proc/"
- + std::string(ps->d_name) + "/cmdline", &cmdline);
- if (cmdline.find(ps_name) == std::string::npos) {
- continue;
- } else {
- closedir(proc_dir);
- return pid;
- }
- }
- closedir(proc_dir);
- return 0;
-}
// dump anrd's trace and add to the zip file.
// 1. check if anrd is running on this device.
@@ -242,7 +221,7 @@
}
// find anrd's pid if it is running.
- pid = pid_of_process("/system/xbin/anrd");
+ pid = GetPidByName("/system/xbin/anrd");
if (pid > 0) {
if (stat(trace_path, &st) == 0) {
@@ -816,7 +795,52 @@
return true;
}
-static void dump_iptables() {
+static void DoKmsg() {
+ struct stat st;
+ if (!stat(PSTORE_LAST_KMSG, &st)) {
+ /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
+ DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
+ } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
+ DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG);
+ } else {
+ /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
+ DumpFile("LAST KMSG", "/proc/last_kmsg");
+ }
+}
+
+static void DoLogcat() {
+ unsigned long timeout;
+ // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
+ // calculate timeout
+ timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
+ if (timeout < 20000) {
+ timeout = 20000;
+ }
+ RunCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
+ CommandOptions::WithTimeout(timeout / 1000).Build());
+ timeout = logcat_timeout("events");
+ if (timeout < 20000) {
+ timeout = 20000;
+ }
+ RunCommand("EVENT LOG",
+ {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
+ CommandOptions::WithTimeout(timeout / 1000).Build());
+ timeout = logcat_timeout("radio");
+ if (timeout < 20000) {
+ timeout = 20000;
+ }
+ RunCommand("RADIO LOG",
+ {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
+ CommandOptions::WithTimeout(timeout / 1000).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", "-d", "*:v"});
+}
+
+static void DumpIpTables() {
RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
@@ -910,7 +934,6 @@
static void dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
- unsigned long timeout;
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
RunCommand("UPTIME", {"uptime"});
@@ -960,30 +983,7 @@
ds.TakeScreenshot();
}
- // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
- // calculate timeout
- timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
- if (timeout < 20000) {
- timeout = 20000;
- }
- RunCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
- CommandOptions::WithTimeout(timeout / 1000).Build());
- timeout = logcat_timeout("events");
- if (timeout < 20000) {
- timeout = 20000;
- }
- RunCommand("EVENT LOG",
- {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
- CommandOptions::WithTimeout(timeout / 1000).Build());
- timeout = logcat_timeout("radio");
- if (timeout < 20000) {
- timeout = 20000;
- }
- RunCommand("RADIO LOG",
- {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
- CommandOptions::WithTimeout(timeout / 1000).Build());
-
- RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
+ DoLogcat();
AddAnrTraceFiles();
@@ -1014,20 +1014,7 @@
DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
- struct stat st;
- if (!stat(PSTORE_LAST_KMSG, &st)) {
- /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
- DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
- } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
- DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG);
- } else {
- /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
- DumpFile("LAST KMSG", "/proc/last_kmsg");
- }
-
- /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
- RunCommand("LAST LOGCAT",
- {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-d", "*:v"});
+ DoKmsg();
/* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
@@ -1048,11 +1035,11 @@
CommandOptions::WithTimeout(20).Build());
#ifdef FWDUMP_bcmdhd
- RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT_5);
+ RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT);
RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, AS_ROOT_20);
- RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
+ RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT);
#endif
DumpFile("INTERRUPTS (1)", "/proc/interrupts");
@@ -1065,7 +1052,7 @@
RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, AS_ROOT_20);
- RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
+ RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT);
#endif
DumpFile("INTERRUPTS (2)", "/proc/interrupts");
@@ -1074,6 +1061,8 @@
RunCommand("VOLD DUMP", {"vdc", "dump"});
RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
+ RunCommand("STORAGED TASKIOINFO", {"storaged", "-d"}, CommandOptions::WithTimeout(10).Build());
+
RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
@@ -1100,7 +1089,7 @@
ds.DumpstateBoard();
- /* Migrate the ril_dumpstate to a dumpstate_board()? */
+ /* Migrate the ril_dumpstate to a device specific dumpstate? */
int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
if (rilDumpstateTimeout > 0) {
// su does not exist on user builds, so try running without it.
@@ -1169,21 +1158,18 @@
printf("== Board\n");
printf("========================================================\n");
- ::android::sp<IDumpstateDevice> dumpstate_device(
- IDumpstateDevice::getService("DumpstateDevice"));
+ ::android::sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService("dumpstate"));
if (dumpstate_device == nullptr) {
- // TODO: temporary workaround until devices on master implement it
- MYLOGE("no IDumpstateDevice implementation; using legacy dumpstate_board()\n");
- dumpstate_board();
+ MYLOGE("No IDumpstateDevice implementation\n");
return;
}
if (!IsZipping()) {
- MYLOGE("Not dumping board info because it's not a zipped bugreport\n");
+ MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
return;
}
- std::string path = ds.GetPath("-dumpstate-board.txt");
+ std::string path = kDumpstateBoardPath;
MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path.c_str());
int fd =
@@ -1202,7 +1188,13 @@
handle->data[0] = fd;
// TODO: need a timeout mechanism so dumpstate does not hang on device implementation call.
- dumpstate_device->dumpstateBoard(handle);
+ android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle);
+ if (!status.isOk()) {
+ MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ return;
+ }
AddZipEntry("dumpstate-board.txt", path);
printf("*** See dumpstate-board.txt entry ***\n");
@@ -1243,16 +1235,7 @@
ShowUsageAndExit();
}
-static void wake_lock_releaser() {
- if (release_wake_lock(WAKE_LOCK_NAME) < 0) {
- MYLOGE("Failed to release wake lock: %s \n", strerror(errno));
- } else {
- MYLOGD("Wake lock released.\n");
- }
-}
-
-static void sig_handler(int signo __attribute__((unused))) {
- wake_lock_releaser();
+static void sig_handler(int) {
_exit(EXIT_FAILURE);
}
@@ -1362,6 +1345,7 @@
int is_remote_mode = 0;
bool show_header_only = false;
bool do_start_service = false;
+ bool telephony_only = false;
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
@@ -1429,6 +1413,8 @@
do_fb = 0;
} else if (ds.extra_options_ == "bugreportwear") {
ds.update_progress_ = true;
+ } else if (ds.extra_options_ == "bugreporttelephony") {
+ telephony_only = true;
} else {
MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
}
@@ -1484,13 +1470,7 @@
MYLOGI("begin\n");
- if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
- MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
- } else {
- MYLOGD("Wake lock acquired.\n");
- atexit(wake_lock_releaser);
- register_sig_handler();
- }
+ register_sig_handler();
if (do_start_service) {
MYLOGI("Starting 'dumpstate' service\n");
@@ -1525,7 +1505,10 @@
if (is_redirecting) {
ds.bugreport_dir_ = dirname(use_outfile);
- ds.base_name_ = basename(use_outfile);
+ std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
+ std::string device_name = android::base::GetProperty("ro.product.device", "UNKNOWN_DEVICE");
+ ds.base_name_ = android::base::StringPrintf("%s-%s-%s", basename(use_outfile),
+ device_name.c_str(), build_id.c_str());
if (do_add_date) {
char date[80];
strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
@@ -1533,8 +1516,11 @@
} else {
ds.name_ = "undated";
}
- std::string buildId = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
- ds.base_name_ += "-" + buildId;
+
+ if (telephony_only) {
+ ds.base_name_ += "-telephony";
+ }
+
if (do_fb) {
ds.screenshot_path_ = ds.GetPath(".png");
}
@@ -1568,8 +1554,12 @@
if (ds.update_progress_) {
if (do_broadcast) {
// clang-format off
+
+ // NOTE: flag must be kept in sync when the value of
+ // FLAG_RECEIVER_INCLUDE_BACKGROUND is changed.
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
+ "-f", "0x01000000",
"--es", "android.intent.extra.NAME", ds.name_,
"--ei", "android.intent.extra.ID", std::to_string(ds.id_),
"--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
@@ -1593,7 +1583,7 @@
::android::sp<IVibrator> vibrator = nullptr;
if (do_vibrate) {
- vibrator = IVibrator::getService("vibrator");
+ vibrator = IVibrator::getService();
if (vibrator != nullptr) {
// cancel previous vibration if any
@@ -1650,51 +1640,64 @@
// duration is logged into MYLOG instead.
ds.PrintHeader();
- // Dumps systrace right away, otherwise it will be filled with unnecessary events.
- // First try to dump anrd trace if the daemon is running. Otherwise, dump
- // the raw trace.
- if (!dump_anrd_trace()) {
- dump_systrace();
+ if (telephony_only) {
+ DumpIpTables();
+ if (!DropRootUser()) {
+ return -1;
+ }
+ do_dmesg();
+ DoLogcat();
+ DoKmsg();
+ ds.DumpstateBoard();
+ DumpModemLogs();
+ } else {
+ // Dumps systrace right away, otherwise it will be filled with unnecessary events.
+ // First try to dump anrd trace if the daemon is running. Otherwise, dump
+ // the raw trace.
+ if (!dump_anrd_trace()) {
+ dump_systrace();
+ }
+
+ // Invoking the following dumpsys calls before dump_traces() to try and
+ // keep the system stats as close to its initial state as possible.
+ RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
+ CommandOptions::WithTimeout(90).DropRoot().Build());
+ RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"},
+ CommandOptions::WithTimeout(10).DropRoot().Build());
+
+ // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
+ dump_raft();
+
+ /* collect stack traces from Dalvik and native processes (needs root) */
+ dump_traces_path = dump_traces();
+
+ /* Run some operations that require root. */
+ get_tombstone_fds(tombstone_data);
+ ds.AddDir(RECOVERY_DIR, true);
+ ds.AddDir(RECOVERY_DATA_DIR, true);
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+ if (!PropertiesHelper::IsUserBuild()) {
+ ds.AddDir(PROFILE_DATA_DIR_CUR, true);
+ ds.AddDir(PROFILE_DATA_DIR_REF, true);
+ }
+ add_mountinfo();
+ DumpIpTables();
+
+ // Capture any IPSec policies in play. No keys are exposed here.
+ RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
+ CommandOptions::WithTimeout(10).Build());
+
+ // Run ss as root so we can see socket marks.
+ RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"},
+ CommandOptions::WithTimeout(10).Build());
+
+ if (!DropRootUser()) {
+ return -1;
+ }
+
+ dumpstate();
}
- // Invoking the following dumpsys calls before dump_traces() to try and
- // keep the system stats as close to its initial state as possible.
- RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
- CommandOptions::WithTimeout(90).DropRoot().Build());
- RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"},
- CommandOptions::WithTimeout(10).DropRoot().Build());
-
- // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
- dump_raft();
-
- /* collect stack traces from Dalvik and native processes (needs root) */
- dump_traces_path = dump_traces();
-
- /* Run some operations that require root. */
- get_tombstone_fds(tombstone_data);
- ds.AddDir(RECOVERY_DIR, true);
- ds.AddDir(RECOVERY_DATA_DIR, true);
- ds.AddDir(LOGPERSIST_DATA_DIR, false);
- if (!PropertiesHelper::IsUserBuild()) {
- ds.AddDir(PROFILE_DATA_DIR_CUR, true);
- ds.AddDir(PROFILE_DATA_DIR_REF, true);
- }
- add_mountinfo();
- dump_iptables();
-
- // Capture any IPSec policies in play. No keys are exposed here.
- RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
- CommandOptions::WithTimeout(10).Build());
-
- // Run ss as root so we can see socket marks.
- RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build());
-
- if (!DropRootUser()) {
- return -1;
- }
-
- dumpstate();
-
/* close output if needed */
if (is_redirecting) {
fclose(stdout);
@@ -1795,8 +1798,12 @@
if (!ds.path_.empty()) {
MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
// clang-format off
+
+ // NOTE: flag must be kept in sync when the value of
+ // FLAG_RECEIVER_INCLUDE_BACKGROUND is changed.
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
+ "-f", "0x01000000",
"--ei", "android.intent.extra.ID", std::to_string(ds.id_),
"--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
"--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 2c11c0c..b2cd241 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -36,6 +36,19 @@
// TODO: remove once not used
#define MAX_ARGS_ARRAY_SIZE 1000
+// TODO: move everything under this namespace
+// TODO: and then remove explicitly android::os::dumpstate:: prefixes
+namespace android {
+namespace os {
+namespace dumpstate {
+
+class DumpstateTest;
+class ProgressTest;
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
// TODO: remove once moved to HAL
#ifdef __cplusplus
extern "C" {
@@ -74,8 +87,8 @@
*
*/
class Progress {
- friend class ProgressTest;
- friend class DumpstateTest;
+ friend class android::os::dumpstate::ProgressTest;
+ friend class android::os::dumpstate::DumpstateTest;
public:
/*
@@ -152,13 +165,10 @@
friend class DumpstateTest;
public:
- static CommandOptions DEFAULT_DUMPSYS;
+ static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
static Dumpstate& GetInstance();
- // TODO: temporary function until device code uses PropertiesHelper::IsUserBuild()
- bool IsUserBuild();
-
/* Checkes whether dumpstate is generating a zipped bugreport. */
bool IsZipping() const;
@@ -172,7 +182,8 @@
* |options| optional argument defining the command's behavior.
*/
int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
- const CommandOptions& options = CommandOptions::DEFAULT);
+ const android::os::dumpstate::CommandOptions& options =
+ android::os::dumpstate::CommandOptions::DEFAULT);
/*
* Runs `dumpsys` with the given arguments, automatically setting its timeout
@@ -187,7 +198,8 @@
* timeout from `options`)
*/
void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
- const CommandOptions& options = DEFAULT_DUMPSYS, long dumpsys_timeout = 0);
+ const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
+ long dumpsys_timeout = 0);
/*
* Prints the contents of a file.
@@ -399,9 +411,6 @@
/* Play a sound via Stagefright */
void play_sound(const char *path);
-/* Implemented by libdumpstate_board to dump board-specific info */
-void dumpstate_board();
-
/* Checks if a given path is a directory. */
bool is_dir(const char* pathname);
diff --git a/cmds/dumpstate/libdumpstate_default.cpp b/cmds/dumpstate/libdumpstate_default.cpp
deleted file mode 100644
index f3997ab..0000000
--- a/cmds/dumpstate/libdumpstate_default.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2013 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"
-
-void dumpstate_board(void)
-{
-}
-
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 2e35112..1c19268 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -37,7 +37,9 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-using namespace android;
+namespace android {
+namespace os {
+namespace dumpstate {
using ::testing::EndsWith;
using ::testing::HasSubstr;
@@ -52,14 +54,6 @@
using ::testing::internal::GetCapturedStderr;
using ::testing::internal::GetCapturedStdout;
-using os::DumpstateService;
-using os::IDumpstateListener;
-using os::IDumpstateToken;
-
-// Not used on test cases yet...
-void dumpstate_board(void) {
-}
-
class DumpstateListenerMock : public IDumpstateListener {
public:
MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
@@ -845,6 +839,14 @@
return status;
}
+ // Find out the pid of the process_name
+ int FindPidOfProcess(const std::string& process_name) {
+ CaptureStderr();
+ int status = GetPidByName(process_name);
+ err = GetCapturedStderr();
+ return status;
+ }
+
int fd;
// 'fd` output and `stderr` from the last command ran.
@@ -1137,3 +1139,19 @@
out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
}
+
+TEST_F(DumpstateUtilTest, FindingPidWithExistingProcess) {
+ // init process always has pid 1.
+ EXPECT_EQ(1, FindPidOfProcess("init"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, FindingPidWithNotExistingProcess) {
+ // find the process with abnormal name.
+ EXPECT_EQ(-1, FindPidOfProcess("abcdef12345-543"));
+ EXPECT_THAT(err, StrEq("can't find the pid\n"));
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 0f50fc9..c6dfa37 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -41,18 +41,23 @@
#include <string>
#include <vector>
-#include <android/log.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <cutils/debugger.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
+#include <debuggerd/client.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include "DumpstateInternal.h"
+// TODO: remove once moved to namespace
+using android::os::dumpstate::CommandOptions;
+using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::PropertiesHelper;
+
static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
/* Most simple commands have 10 as timeout, so 5 is a good estimate */
@@ -64,9 +69,6 @@
const CommandOptions& options = CommandOptions::DEFAULT) {
return ds.RunCommand(title, full_command, options);
}
-bool Dumpstate::IsUserBuild() {
- return PropertiesHelper::IsUserBuild();
-}
/* list of native processes to include in the native dumps */
// This matches the /proc/pid/exe link instead of /proc/pid/cmdline.
@@ -862,9 +864,9 @@
}
/* create a new, empty traces.txt file to receive stack dumps */
- int fd = TEMP_FAILURE_RETRY(open(traces_path.c_str(),
- O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
- 0666)); /* -rw-rw-rw- */
+ int fd = TEMP_FAILURE_RETRY(
+ open(traces_path.c_str(), O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
+ 0666)); /* -rw-rw-rw- */
if (fd < 0) {
MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno));
return nullptr;
diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp
index 3476964..c5ae9d2 100644
--- a/cmds/dumpsys/Android.bp
+++ b/cmds/dumpsys/Android.bp
@@ -15,6 +15,8 @@
"libutils",
"liblog",
"libbinder",
+ "android.hidl.manager@1.0",
+ "libhidlbase"
],
clang: true,
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index f0e7200..860b7b4 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -45,8 +45,7 @@
using android::base::unique_fd;
using android::base::WriteFully;
-static int sort_func(const String16* lhs, const String16* rhs)
-{
+static int sort_func(const String16* lhs, const String16* rhs) {
return lhs->compare(*rhs);
}
@@ -55,10 +54,11 @@
"usage: dumpsys\n"
" To dump all services.\n"
"or:\n"
- " dumpsys [-t TIMEOUT] [--help | -l | --skip SERVICES | SERVICE [ARGS]]\n"
+ " dumpsys [-t TIMEOUT] [--help | --hw | -l | --skip SERVICES | SERVICE [ARGS]]\n"
" --help: shows this help\n"
" -l: only list services, do not dump them\n"
" -t TIMEOUT: TIMEOUT to use in seconds instead of default 10 seconds\n"
+ " --hw: list all hw services running on the device\n"
" --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
" SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
}
@@ -72,16 +72,42 @@
return false;
}
+static void ListHardwareServices(android::hidl::manager::V1_0::IServiceManager* hm) {
+ using android::hardware::hidl_vec;
+ using android::hardware::hidl_string;
+ using android::hardware::Return;
+ using android::sp;
+
+ if (hm == nullptr) {
+ ALOGE("Unable to get hardware service manager!");
+ aerr << "Failed to get hardware service manager!";
+ return;
+ }
+
+ Return<void> ret = hm->list([](const hidl_vec<hidl_string> ®istered){
+ aout << "Currently running hardware services:" << endl;
+ for (const auto &service : registered) {
+ aout << " " << service << endl;
+ }
+ });
+
+ if (!ret.isOk()) {
+ aerr << "Failed to list hardware services: " << ret.description();
+ }
+}
+
int Dumpsys::main(int argc, char* const argv[]) {
Vector<String16> services;
Vector<String16> args;
Vector<String16> skippedServices;
bool showListOnly = false;
+ bool listHwOnly = false;
bool skipServices = false;
int timeoutArg = 10;
static struct option longOptions[] = {
{"skip", no_argument, 0, 0 },
{"help", no_argument, 0, 0 },
+ {"hw", no_argument, 0, 0 },
{ 0, 0, 0, 0 }
};
@@ -105,6 +131,8 @@
} else if (!strcmp(longOptions[optionIndex].name, "help")) {
usage();
return 0;
+ } else if (!strcmp(longOptions[optionIndex].name, "hw")) {
+ listHwOnly = true;
}
break;
@@ -143,11 +171,17 @@
}
if ((skipServices && skippedServices.empty()) ||
- (showListOnly && (!services.empty() || !skippedServices.empty()))) {
+ (showListOnly && (!services.empty() || !skippedServices.empty())) ||
+ (listHwOnly && (skipServices || services.size() > 0 || showListOnly))) {
usage();
return -1;
}
+ if (listHwOnly) {
+ ListHardwareServices(hm_);
+ return 0;
+ }
+
if (services.empty() || showListOnly) {
// gets all services
services = sm_->listServices();
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 2534dde..20d515d 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -17,18 +17,21 @@
#ifndef FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
#define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
+#include <android/hidl/manager/1.0/IServiceManager.h>
#include <binder/IServiceManager.h>
namespace android {
class Dumpsys {
public:
- Dumpsys(android::IServiceManager* sm) : sm_(sm) {
+ Dumpsys(android::IServiceManager* sm,
+ android::hidl::manager::V1_0::IServiceManager* hm) : sm_(sm), hm_(hm) {
}
int main(int argc, char* const argv[]);
private:
android::IServiceManager* sm_;
+ android::hidl::manager::V1_0::IServiceManager* hm_;
};
}
diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp
index 8ba0eba..b180c98 100644
--- a/cmds/dumpsys/main.cpp
+++ b/cmds/dumpsys/main.cpp
@@ -27,6 +27,7 @@
#include <stdio.h>
using namespace android;
+using HServiceManager = android::hidl::manager::V1_0::IServiceManager;
int main(int argc, char* const argv[]) {
signal(SIGPIPE, SIG_IGN);
@@ -38,6 +39,8 @@
return 20;
}
- Dumpsys dumpsys(sm.get());
+ sp<HServiceManager> hm = HServiceManager::getService("manager");
+
+ Dumpsys dumpsys(sm.get(), hm.get());
return dumpsys.main(argc, argv);
}
diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp
index 7698ed5..e00444f 100644
--- a/cmds/dumpsys/tests/Android.bp
+++ b/cmds/dumpsys/tests/Android.bp
@@ -5,8 +5,12 @@
srcs: ["dumpsys_test.cpp"],
shared_libs: [
+ "android.hidl.manager@1.0",
"libbase",
"libbinder",
+ "liblog",
+ "libhidlbase",
+ "libhidltransport",
"libutils",
],
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index a61cb00..a66685d 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -44,6 +44,12 @@
using ::testing::internal::GetCapturedStderr;
using ::testing::internal::GetCapturedStdout;
+using android::hardware::hidl_vec;
+using android::hardware::hidl_string;
+using android::hardware::Void;
+using HServiceManager = android::hidl::manager::V1_0::IServiceManager;
+using IServiceNotification = android::hidl::manager::V1_0::IServiceNotification;
+
class ServiceManagerMock : public IServiceManager {
public:
MOCK_CONST_METHOD1(getService, sp<IBinder>(const String16&));
@@ -55,6 +61,26 @@
MOCK_METHOD0(onAsBinder, IBinder*());
};
+class HardwareServiceManagerMock : public HServiceManager {
+ public:
+ template<typename T>
+ using R = android::hardware::Return<T>; // conflicts with ::testing::Return
+
+ MOCK_METHOD2(get, R<sp<IBase>>(const hidl_string&, const hidl_string&));
+ MOCK_METHOD3(add,
+ R<bool>(const hidl_vec<hidl_string>&,
+ const hidl_string&,
+ const sp<IBase>&));
+ MOCK_METHOD1(list, R<void>(list_cb));
+ MOCK_METHOD2(listByInterface,
+ R<void>(const hidl_string&, listByInterface_cb));
+ MOCK_METHOD3(registerForNotifications,
+ R<bool>(const hidl_string&,
+ const hidl_string&,
+ const sp<IServiceNotification>&));
+
+};
+
class BinderMock : public BBinder {
public:
BinderMock() {
@@ -84,6 +110,26 @@
return MakeAction(new WriteOnFdAction(output));
}
+// gmock black magic to provide a WithArg<0>(List(services)) matcher
+typedef void HardwareListFunction(HServiceManager::list_cb);
+
+class HardwareListAction : public ActionInterface<HardwareListFunction> {
+ public:
+ explicit HardwareListAction(const hidl_vec<hidl_string> &services) : services_(services) {
+ }
+ virtual Result Perform(const ArgumentTuple& args) {
+ auto cb = ::std::tr1::get<0>(args);
+ cb(services_);
+ }
+
+ private:
+ hidl_vec<hidl_string> services_;
+};
+
+Action<HardwareListFunction> HardwareList(const hidl_vec<hidl_string> &services) {
+ return MakeAction(new HardwareListAction(services));
+}
+
// Matcher for args using Android's Vector<String16> format
// TODO: move it to some common testing library
MATCHER_P(AndroidElementsAre, expected, "") {
@@ -121,7 +167,7 @@
class DumpsysTest : public Test {
public:
- DumpsysTest() : sm_(), dump_(&sm_), stdout_(), stderr_() {
+ DumpsysTest() : sm_(), hm_(), dump_(&sm_, &hm_), stdout_(), stderr_() {
}
void ExpectListServices(std::vector<std::string> services) {
@@ -129,9 +175,22 @@
for (auto& service : services) {
services16.add(String16(service.c_str()));
}
+
EXPECT_CALL(sm_, listServices()).WillRepeatedly(Return(services16));
}
+ void ExpectListHardwareServices(std::vector<std::string> services) {
+ hidl_vec<hidl_string> hidl_services;
+ hidl_services.resize(services.size());
+ for (size_t i = 0; i < services.size(); i++) {
+ hidl_services[i] = services[i];
+ }
+
+ EXPECT_CALL(hm_, list(_)).WillRepeatedly(DoAll(
+ WithArg<0>(HardwareList(hidl_services)),
+ Return(Void())));
+ }
+
sp<BinderMock> ExpectCheckService(const char* name, bool running = true) {
sp<BinderMock> binder_mock;
if (running) {
@@ -175,8 +234,10 @@
EXPECT_THAT(status, Eq(0));
}
- void AssertRunningServices(const std::vector<std::string>& services) {
- std::string expected("Currently running services:\n");
+ void AssertRunningServices(const std::vector<std::string>& services,
+ const std::string &message = "Currently running services:") {
+ std::string expected(message);
+ expected.append("\n");
for (const std::string& service : services) {
expected.append(" ").append(service).append("\n");
}
@@ -204,12 +265,21 @@
}
ServiceManagerMock sm_;
+ HardwareServiceManagerMock hm_;
Dumpsys dump_;
private:
std::string stdout_, stderr_;
};
+TEST_F(DumpsysTest, ListHwServices) {
+ ExpectListHardwareServices({"Locksmith", "Valet"});
+
+ CallMain({"--hw"});
+
+ AssertRunningServices({"Locksmith", "Valet"}, "Currently running hardware services:");
+}
+
// Tests 'dumpsys -l' when all services are running
TEST_F(DumpsysTest, ListAllServices) {
ExpectListServices({"Locksmith", "Valet"});
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 93174bf..33db6db 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -6,6 +6,8 @@
"-Werror",
],
srcs: [
+ "CacheItem.cpp",
+ "CacheTracker.cpp",
"InstalldNativeService.cpp",
"dexopt.cpp",
"globals.cpp",
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp
new file mode 100644
index 0000000..d1bdded
--- /dev/null
+++ b/cmds/installd/CacheItem.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 "CacheItem.h"
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CacheItem::CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p) : mParent(parent) {
+ level = p->fts_level;
+ directory = S_ISDIR(p->fts_statp->st_mode);
+ size = p->fts_statp->st_blocks * 512;
+ modified = p->fts_statp->st_mtime;
+ mName = p->fts_path;
+}
+
+CacheItem::~CacheItem() {
+}
+
+std::string CacheItem::toString() {
+ return StringPrintf("%s size=%" PRId64 " mod=%ld", buildPath().c_str(), size, modified);
+}
+
+std::string CacheItem::buildPath() {
+ std::string res = mName;
+ std::shared_ptr<CacheItem> parent = mParent;
+ while (parent) {
+ res.insert(0, parent->mName);
+ parent = parent->mParent;
+ }
+ return res;
+}
+
+int CacheItem::purge() {
+ auto path = buildPath();
+ if (directory) {
+ return delete_dir_contents_and_dir(path, true);
+ } else {
+ int res = unlink(path.c_str());
+ if (res != 0) {
+ PLOG(WARNING) << "Failed to unlink " << path;
+ }
+ return res;
+ }
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/CacheItem.h b/cmds/installd/CacheItem.h
new file mode 100644
index 0000000..bec8bc8
--- /dev/null
+++ b/cmds/installd/CacheItem.h
@@ -0,0 +1,62 @@
+/*
+ * 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_INSTALLD_CACHE_ITEM_H
+#define ANDROID_INSTALLD_CACHE_ITEM_H
+
+#include <memory>
+#include <string>
+
+#include <fts.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace installd {
+
+/**
+ * Single cache item that can be purged to free up space. This may be an
+ * isolated file, or an entire directory tree that should be atomically
+ * deleted.
+ */
+class CacheItem {
+public:
+ CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p);
+ ~CacheItem();
+
+ std::string toString();
+ std::string buildPath();
+
+ int purge();
+
+ short level;
+ bool directory;
+ int64_t size;
+ time_t modified;
+
+private:
+ std::shared_ptr<CacheItem> mParent;
+ std::string mName;
+
+ DISALLOW_COPY_AND_ASSIGN(CacheItem);
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_CACHE_ITEM_H
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
new file mode 100644
index 0000000..23c4330
--- /dev/null
+++ b/cmds/installd/CacheTracker.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
+#include "CacheTracker.h"
+
+#include <fts.h>
+#include <sys/quota.h>
+#include <utils/Trace.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CacheTracker::CacheTracker(userid_t userId, appid_t appId, const std::string& quotaDevice) :
+ cacheUsed(0), cacheQuota(0), mUserId(userId), mAppId(appId), mQuotaDevice(quotaDevice),
+ mItemsLoaded(false) {
+}
+
+CacheTracker::~CacheTracker() {
+}
+
+std::string CacheTracker::toString() {
+ return StringPrintf("UID=%d used=%" PRId64 " quota=%" PRId64 " ratio=%d",
+ multiuser_get_uid(mUserId, mAppId), cacheUsed, cacheQuota, getCacheRatio());
+}
+
+void CacheTracker::addDataPath(const std::string& dataPath) {
+ mDataPaths.push_back(dataPath);
+}
+
+void CacheTracker::loadStats() {
+ int cacheGid = multiuser_get_cache_gid(mUserId, mAppId);
+ if (cacheGid != -1 && !mQuotaDevice.empty()) {
+ ATRACE_BEGIN("loadStats quota");
+ struct dqblk dq;
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), cacheGid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ ATRACE_END();
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid;
+ }
+ } else {
+ cacheUsed = dq.dqb_curspace;
+ ATRACE_END();
+ return;
+ }
+ }
+
+ ATRACE_BEGIN("loadStats tree");
+ cacheUsed = 0;
+ for (auto path : mDataPaths) {
+ auto cachePath = read_path_inode(path, "cache", kXattrInodeCache);
+ auto codeCachePath = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
+ calculate_tree_size(cachePath, &cacheUsed);
+ calculate_tree_size(codeCachePath, &cacheUsed);
+ }
+ ATRACE_END();
+}
+
+void CacheTracker::loadItemsFrom(const std::string& path) {
+ FTS *fts;
+ FTSENT *p;
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+ PLOG(WARNING) << "Failed to fts_open " << path;
+ return;
+ }
+ // TODO: add support for "user.atomic" and "user.tombstone" xattrs
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_D:
+ // Track the newest mtime of anything inside so we consider
+ // deleting the directory last
+ p->fts_number = p->fts_statp->st_mtime;
+ break;
+ case FTS_DP:
+ p->fts_statp->st_mtime = p->fts_number;
+
+ // Ignore the actual top-level cache directories
+ if (p->fts_level == 0) break;
+ case FTS_DEFAULT:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ // TODO: optimize path memory footprint
+ items.push_back(std::shared_ptr<CacheItem>(new CacheItem(nullptr, p)));
+
+ // Track the newest modified item under this tree
+ p->fts_parent->fts_number =
+ std::max(p->fts_parent->fts_number, p->fts_statp->st_mtime);
+ break;
+ }
+ }
+ fts_close(fts);
+}
+
+void CacheTracker::loadItems() {
+ items.clear();
+
+ ATRACE_BEGIN("loadItems");
+ for (auto path : mDataPaths) {
+ loadItemsFrom(read_path_inode(path, "cache", kXattrInodeCache));
+ loadItemsFrom(read_path_inode(path, "code_cache", kXattrInodeCodeCache));
+ }
+ ATRACE_END();
+
+ ATRACE_BEGIN("sortItems");
+ auto cmp = [](std::shared_ptr<CacheItem> left, std::shared_ptr<CacheItem> right) {
+ // TODO: sort dotfiles last
+ // TODO: sort code_cache last
+ if (left->modified != right->modified) {
+ return (left->modified > right->modified);
+ }
+ if (left->level != right->level) {
+ return (left->level < right->level);
+ }
+ return left->directory;
+ };
+ std::sort(items.begin(), items.end(), cmp);
+ ATRACE_END();
+}
+
+void CacheTracker::ensureItems() {
+ if (mItemsLoaded) {
+ return;
+ } else {
+ loadItems();
+ mItemsLoaded = true;
+ }
+}
+
+int CacheTracker::getCacheRatio() {
+ if (cacheQuota == 0) {
+ return 0;
+ } else {
+ return (cacheUsed * 10000) / cacheQuota;
+ }
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/CacheTracker.h b/cmds/installd/CacheTracker.h
new file mode 100644
index 0000000..91692d7
--- /dev/null
+++ b/cmds/installd/CacheTracker.h
@@ -0,0 +1,77 @@
+/*
+ * 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_INSTALLD_CACHE_TRACKER_H
+#define ANDROID_INSTALLD_CACHE_TRACKER_H
+
+#include <memory>
+#include <string>
+#include <queue>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <android-base/macros.h>
+#include <cutils/multiuser.h>
+
+#include "CacheItem.h"
+
+namespace android {
+namespace installd {
+
+/**
+ * Cache tracker for a single UID. Each tracker is used in two modes: first
+ * for loading lightweight "stats", and then by loading detailed "items"
+ * which can then be purged to free up space.
+ */
+class CacheTracker {
+public:
+ CacheTracker(userid_t userId, appid_t appId, const std::string& quotaDevice);
+ ~CacheTracker();
+
+ std::string toString();
+
+ void addDataPath(const std::string& dataPath);
+
+ void loadStats();
+ void loadItems();
+
+ void ensureItems();
+
+ int getCacheRatio();
+
+ int64_t cacheUsed;
+ int64_t cacheQuota;
+
+ std::vector<std::shared_ptr<CacheItem>> items;
+
+private:
+ userid_t mUserId;
+ appid_t mAppId;
+ std::string mQuotaDevice;
+ bool mItemsLoaded;
+
+ std::vector<std::string> mDataPaths;
+
+ void loadItemsFrom(const std::string& path);
+
+ DISALLOW_COPY_AND_ASSIGN(CacheTracker);
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_CACHE_TRACKER_H
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index df0c6ca..ede31fc 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -16,6 +16,8 @@
#include "InstalldNativeService.h"
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
#include <errno.h>
#include <inttypes.h>
#include <fstream>
@@ -39,13 +41,14 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/fs.h>
-#include <cutils/log.h> // TODO: Move everything to base/logging.
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
+#include <log/log.h> // TODO: Move everything to base/logging.
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
#include <system/thread_defs.h>
+#include <utils/Trace.h>
#include "dexopt.h"
#include "globals.h"
@@ -53,13 +56,15 @@
#include "otapreopt_utils.h"
#include "utils.h"
+#include "CacheTracker.h"
+#include "MatchExtensionGen.h"
+
#ifndef LOG_TAG
#define LOG_TAG "installd"
#endif
-#define MEASURE_EXTERNAL 0
-
using android::base::StringPrintf;
+using std::endl;
namespace android {
namespace installd {
@@ -84,6 +89,8 @@
static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
static constexpr int FLAG_USE_QUOTA = 1 << 12;
+static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
+static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 14;
namespace {
@@ -189,15 +196,29 @@
}
status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) {
+ auto out = std::fstream(StringPrintf("/proc/self/fd/%d", fd));
const binder::Status dump_permission = checkPermission(kDump);
if (!dump_permission.isOk()) {
- const String8 msg(dump_permission.toString8());
- write(fd, msg.string(), msg.size());
+ out << dump_permission.toString8() << endl;
return PERMISSION_DENIED;
}
+ std::lock_guard<std::recursive_mutex> lock(mLock);
- std::string msg = "installd is happy\n";
- write(fd, msg.c_str(), strlen(msg.c_str()));
+ out << "installd is happy!" << endl;
+
+ out << endl << "Devices with quota support:" << endl;
+ for (const auto& n : mQuotaDevices) {
+ out << " " << n.first << " = " << n.second << endl;
+ }
+
+ out << endl << "Per-UID cache quotas:" << endl;
+ for (const auto& n : mCacheQuotas) {
+ out << " " << n.first << " = " << n.second << endl;
+ }
+
+ out << endl;
+ out.flush();
+
return NO_ERROR;
}
@@ -288,12 +309,13 @@
}
}
+ mode_t actual_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
if (st.st_uid != uid) {
// Mismatched UID is real trouble; we can't recover
LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid
<< " but expected " << uid;
return -1;
- } else if (st.st_gid == gid && st.st_mode == target_mode) {
+ } else if (st.st_gid == gid && actual_mode == target_mode) {
// Everything looks good!
return 0;
}
@@ -337,6 +359,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -344,10 +367,15 @@
// Assume invalid inode unless filled in below
if (_aidl_return != nullptr) *_aidl_return = -1;
- uid_t uid = multiuser_get_uid(userId, appId);
- gid_t cacheGid = multiuser_get_cache_gid(userId, appId);
+ int32_t uid = multiuser_get_uid(userId, appId);
+ int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
+ // If UID doesn't have a specific cache GID, use UID value
+ if (cacheGid == -1) {
+ cacheGid = uid;
+ }
+
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
bool existing = (access(path.c_str(), F_OK) == 0);
@@ -423,6 +451,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -465,6 +494,7 @@
binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* pkgname = packageName.c_str();
binder::Status res = ok();
@@ -482,6 +512,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -541,6 +572,7 @@
binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* pkgname = packageName.c_str();
binder::Status res = ok();
@@ -561,6 +593,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -594,6 +627,7 @@
CHECK_ARGUMENT_UUID(fromUuid);
CHECK_ARGUMENT_UUID(toUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr;
const char* to_uuid = toUuid ? toUuid->c_str() : nullptr;
@@ -728,6 +762,7 @@
int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
if (flags & FLAG_STORAGE_DE) {
@@ -744,6 +779,7 @@
int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
binder::Status res = ok();
@@ -757,7 +793,7 @@
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete " + path);
}
- path = create_data_user_profiles_path(userId);
+ path = create_data_user_profile_path(userId);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete " + path);
}
@@ -784,51 +820,172 @@
* when just reading from the cache, which is pretty awful.
*/
binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
- int64_t freeStorageSize) {
+ int64_t freeStorageSize, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ // TODO: remove this once framework is more robust
+ invalidateMounts();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
- cache_t* cache;
- int64_t avail;
-
auto data_path = create_data_path(uuid_);
+ auto device = findQuotaDeviceForUuid(uuid);
+ auto noop = (flags & FLAG_FREE_CACHE_NOOP);
- avail = data_disk_free(data_path);
- if (avail < 0) {
+ int64_t free = data_disk_free(data_path);
+ int64_t needed = freeStorageSize - free;
+ if (free < 0) {
return error("Failed to determine free space for " + data_path);
- }
-
- ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", freeStorageSize, avail);
- if (avail >= freeStorageSize) {
+ } else if (free >= freeStorageSize) {
return ok();
}
- cache = start_cache_collection();
+ LOG(DEBUG) << "Found " << data_path << " with " << free << " free; caller requested "
+ << freeStorageSize;
- auto users = get_known_users(uuid_);
- for (auto user : users) {
- add_cache_files(cache, create_data_user_ce_path(uuid_, user));
- add_cache_files(cache, create_data_user_de_path(uuid_, user));
- add_cache_files(cache,
- StringPrintf("%s/Android/data", create_data_media_path(uuid_, user).c_str()));
+ if (flags & FLAG_FREE_CACHE_V2) {
+ // This new cache strategy fairly removes files from UIDs by deleting
+ // files from the UIDs which are most over their allocated quota
+
+ // 1. Create trackers for every known UID
+ ATRACE_BEGIN("create");
+ std::unordered_map<uid_t, std::shared_ptr<CacheTracker>> trackers;
+ for (auto user : get_known_users(uuid_)) {
+ FTS *fts;
+ FTSENT *p;
+ char *argv[] = {
+ (char*) create_data_user_ce_path(uuid_, user).c_str(),
+ (char*) create_data_user_de_path(uuid_, user).c_str(),
+ nullptr
+ };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+ return error("Failed to fts_open");
+ }
+ while ((p = fts_read(fts)) != NULL) {
+ if (p->fts_info == FTS_D && p->fts_level == 1) {
+ uid_t uid = p->fts_statp->st_uid;
+ auto search = trackers.find(uid);
+ if (search != trackers.end()) {
+ search->second->addDataPath(p->fts_path);
+ } else {
+ auto tracker = std::shared_ptr<CacheTracker>(new CacheTracker(
+ multiuser_get_user_id(uid), multiuser_get_app_id(uid), device));
+ tracker->addDataPath(p->fts_path);
+ tracker->cacheQuota = mCacheQuotas[uid];
+ if (tracker->cacheQuota == 0) {
+ LOG(WARNING) << "UID " << uid << " has no cache quota; assuming 64MB";
+ tracker->cacheQuota = 67108864;
+ }
+ trackers[uid] = tracker;
+ }
+ fts_set(fts, p, FTS_SKIP);
+ }
+ }
+ fts_close(fts);
+ }
+ ATRACE_END();
+
+ // 2. Populate tracker stats and insert into priority queue
+ ATRACE_BEGIN("populate");
+ auto cmp = [](std::shared_ptr<CacheTracker> left, std::shared_ptr<CacheTracker> right) {
+ return (left->getCacheRatio() < right->getCacheRatio());
+ };
+ std::priority_queue<std::shared_ptr<CacheTracker>,
+ std::vector<std::shared_ptr<CacheTracker>>, decltype(cmp)> queue(cmp);
+ for (const auto& it : trackers) {
+ it.second->loadStats();
+ queue.push(it.second);
+ }
+ ATRACE_END();
+
+ // 3. Bounce across the queue, freeing items from whichever tracker is
+ // the most over their assigned quota
+ ATRACE_BEGIN("bounce");
+ std::shared_ptr<CacheTracker> active;
+ while (active || !queue.empty()) {
+ // Find the best tracker to work with; this might involve swapping
+ // if the active tracker is no longer the most over quota
+ bool nextBetter = active && !queue.empty()
+ && active->getCacheRatio() < queue.top()->getCacheRatio();
+ if (!active || nextBetter) {
+ if (active) {
+ // Current tracker still has items, so we'll consider it
+ // again later once it bubbles up to surface
+ queue.push(active);
+ }
+ active = queue.top(); queue.pop();
+ active->ensureItems();
+ continue;
+ }
+
+ // If no items remain, go find another tracker
+ if (active->items.empty()) {
+ active = nullptr;
+ continue;
+ } else {
+ auto item = active->items.back();
+ active->items.pop_back();
+
+ LOG(DEBUG) << "Purging " << item->toString() << " from " << active->toString();
+ if (!noop) {
+ item->purge();
+ }
+ active->cacheUsed -= item->size;
+ needed -= item->size;
+ }
+
+ // Verify that we're actually done before bailing, since sneaky
+ // apps might be using hardlinks
+ if (needed <= 0) {
+ free = data_disk_free(data_path);
+ needed = freeStorageSize - free;
+ if (needed <= 0) {
+ break;
+ } else {
+ LOG(WARNING) << "Expected to be done but still need " << needed;
+ }
+ }
+ }
+ ATRACE_END();
+
+ } else {
+ ATRACE_BEGIN("start");
+ cache_t* cache = start_cache_collection();
+ ATRACE_END();
+
+ ATRACE_BEGIN("add");
+ for (auto user : get_known_users(uuid_)) {
+ add_cache_files(cache, create_data_user_ce_path(uuid_, user));
+ add_cache_files(cache, create_data_user_de_path(uuid_, user));
+ add_cache_files(cache,
+ StringPrintf("%s/Android/data", create_data_media_path(uuid_, user).c_str()));
+ }
+ ATRACE_END();
+
+ ATRACE_BEGIN("clear");
+ clear_cache_files(data_path, cache, freeStorageSize);
+ ATRACE_END();
+
+ ATRACE_BEGIN("finish");
+ finish_cache_collection(cache);
+ ATRACE_END();
}
- clear_cache_files(data_path, cache, freeStorageSize);
- finish_cache_collection(cache);
-
- avail = data_disk_free(data_path);
- if (avail >= freeStorageSize) {
+ free = data_disk_free(data_path);
+ if (free >= freeStorageSize) {
return ok();
} else {
return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64,
- freeStorageSize, data_path.c_str(), avail));
+ freeStorageSize, data_path.c_str(), free));
}
}
binder::Status InstalldNativeService::rmdex(const std::string& codePath,
const std::string& instructionSet) {
ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
char dex_path[PKG_PATH_MAX];
const char* path = codePath.c_str();
@@ -850,54 +1007,32 @@
}
}
-static bool uuidEquals(const std::unique_ptr<std::string>& a,
- const std::unique_ptr<std::string>& b) {
- if (!a && !b) {
- return true;
- } else if (!a && b) {
- return false;
- } else if (a && !b) {
- return false;
- } else {
- return *a == *b;
- }
-}
-
struct stats {
int64_t codeSize;
int64_t dataSize;
int64_t cacheSize;
};
-static void collectQuotaStats(const std::unique_ptr<std::string>& uuid, int32_t userId,
- int32_t appId, struct stats* stats) {
- struct dqblk dq;
+#if MEASURE_DEBUG
+static std::string toString(std::vector<int64_t> values) {
+ std::stringstream res;
+ res << "[";
+ for (size_t i = 0; i < values.size(); i++) {
+ res << values[i];
+ if (i < values.size() - 1) {
+ res << ",";
+ }
+ }
+ res << "]";
+ return res.str();
+}
+#endif
- auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
- std::string device;
- {
- std::ifstream in("/proc/mounts");
- if (!in.is_open()) {
- PLOG(ERROR) << "Failed to read mounts";
- return;
- }
- std::string source;
- std::string target;
- while (!in.eof()) {
- std::getline(in, source, ' ');
- std::getline(in, target, ' ');
- if (target == path) {
- device = source;
- break;
- }
- // Skip to next line
- std::getline(in, source);
- }
- }
- if (device.empty()) {
- PLOG(ERROR) << "Failed to resolve block device for " << path;
- return;
- }
+static void collectQuotaStats(const std::string& device, int32_t userId,
+ int32_t appId, struct stats* stats, struct stats* extStats) {
+ if (device.empty()) return;
+
+ struct dqblk dq;
uid_t uid = multiuser_get_uid(userId, appId);
if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
@@ -906,6 +1041,9 @@
PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
}
} else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
stats->dataSize += dq.dqb_curspace;
}
@@ -917,11 +1055,29 @@
PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << cacheGid;
}
} else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for GID " << cacheGid << " " << dq.dqb_curspace;
+#endif
stats->cacheSize += dq.dqb_curspace;
}
}
- int sharedGid = multiuser_get_shared_app_gid(uid);
+ int extGid = multiuser_get_ext_gid(userId, appId);
+ if (extGid != -1) {
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), extGid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << extGid;
+ }
+ } else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for GID " << extGid << " " << dq.dqb_curspace;
+#endif
+ extStats->dataSize += dq.dqb_curspace;
+ }
+ }
+
+ int sharedGid = multiuser_get_shared_gid(userId, appId);
if (sharedGid != -1) {
if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), sharedGid,
reinterpret_cast<char*>(&dq)) != 0) {
@@ -929,12 +1085,15 @@
PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << sharedGid;
}
} else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for GID " << sharedGid << " " << dq.dqb_curspace;
+#endif
stats->codeSize += dq.dqb_curspace;
}
}
}
-static void collectManualStats(std::string& path, struct stats* stats) {
+static void collectManualStats(const std::string& path, struct stats* stats) {
DIR *d;
int dfd;
struct dirent *de;
@@ -984,90 +1143,471 @@
closedir(d);
}
+static void collectManualStatsForUser(const std::string& path, struct stats* stats,
+ bool exclude_apps = false) {
+ DIR *d;
+ int dfd;
+ struct dirent *de;
+ struct stat s;
+
+ d = opendir(path.c_str());
+ if (d == nullptr) {
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Failed to open " << path;
+ }
+ return;
+ }
+ dfd = dirfd(d);
+ while ((de = readdir(d))) {
+ if (de->d_type == DT_DIR) {
+ const char *name = de->d_name;
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) != 0) {
+ continue;
+ }
+ if (!strcmp(name, ".") || !strcmp(name, "..")) {
+ continue;
+ } else if (exclude_apps && (s.st_uid >= AID_APP_START && s.st_uid <= AID_APP_END)) {
+ continue;
+ } else {
+ collectManualStats(StringPrintf("%s/%s", path.c_str(), name), stats);
+ }
+ }
+ }
+ closedir(d);
+}
+
+static void collectManualExternalStatsForUser(const std::string& path, struct stats* stats) {
+ FTS *fts;
+ FTSENT *p;
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+ PLOG(ERROR) << "Failed to fts_open " << path;
+ return;
+ }
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_D:
+ if (p->fts_level == 4
+ && !strcmp(p->fts_name, "cache")
+ && !strcmp(p->fts_parent->fts_parent->fts_name, "data")
+ && !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) {
+ p->fts_number = 1;
+ }
+ p->fts_number = p->fts_parent->fts_number;
+ // Fall through to count the directory
+ case FTS_DEFAULT:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ int64_t size = (p->fts_statp->st_blocks * 512);
+ if (p->fts_number == 1) {
+ stats->cacheSize += size;
+ }
+ stats->dataSize += size;
+ break;
+ }
+ }
+ fts_close(fts);
+}
+
binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
- const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- int64_t ceDataInode, const std::string& codePath,
- const std::unique_ptr<std::string>& externalUuid, std::vector<int64_t>* _aidl_return) {
+ const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+ int32_t appId, const std::vector<int64_t>& ceDataInodes,
+ const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
- CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ for (auto packageName : packageNames) {
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ }
+ std::lock_guard<std::recursive_mutex> lock(mLock);
- const char* uuid_ = uuid ? uuid->c_str() : nullptr;
- const char* extuuid_ = externalUuid ? externalUuid->c_str() : nullptr;
- const char* pkgname = packageName.c_str();
+ // When modifying this logic, always verify using tests:
+ // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetAppSize
+
+#if MEASURE_DEBUG
+ LOG(INFO) << "Measuring user " << userId << " app " << appId;
+#endif
// Here's a summary of the common storage locations across the platform,
// and how they're each tagged:
//
// /data/app/com.example UID system
// /data/app/com.example/oat UID system
- // /data/user/0/com.example UID u0_a10 GID u0_a10
- // /data/user/0/com.example/cache UID u0_a10 GID u0_a10_cache
- // /data/media/0/Android/data/com.example UID u0_a10 GID u0_a10
- // /data/media/0/Android/data/com.example/cache UID u0_a10 GID u0_a10_cache
- // /data/media/0/Android/obb/com.example UID system
+ // /data/user/0/com.example UID u0_a10 GID u0_a10
+ // /data/user/0/com.example/cache UID u0_a10 GID u0_a10_cache
+ // /data/media/0/foo.txt UID u0_media_rw
+ // /data/media/0/bar.jpg UID u0_media_rw GID u0_media_image
+ // /data/media/0/Android/data/com.example UID u0_media_rw GID u0_a10_ext
+ // /data/media/0/Android/data/com.example/cache UID u0_media_rw GID u0_a10_ext_cache
+ // /data/media/obb/com.example UID system
struct stats stats;
+ struct stats extStats;
memset(&stats, 0, sizeof(stats));
+ memset(&extStats, 0, sizeof(extStats));
- auto obbCodePath = create_data_media_package_path(extuuid_, userId, pkgname, "obb");
- calculate_tree_size(obbCodePath, &stats.codeSize);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
- if (flags & FLAG_USE_QUOTA) {
- calculate_tree_size(codePath, &stats.codeSize,
- 0, multiuser_get_shared_gid(userId, appId));
+ auto device = findQuotaDeviceForUuid(uuid);
+ if (device.empty()) {
+ flags &= ~FLAG_USE_QUOTA;
+ }
- collectQuotaStats(uuid, userId, appId, &stats);
+ ATRACE_BEGIN("obb");
+ for (auto packageName : packageNames) {
+ auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
+ calculate_tree_size(obbCodePath, &extStats.codeSize);
+ }
+ ATRACE_END();
- // If external storage lives on a different storage device, also
- // collect quota stats from that block device
- if (!uuidEquals(uuid, externalUuid)) {
- collectQuotaStats(externalUuid, userId, appId, &stats);
+ if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
+ ATRACE_BEGIN("code");
+ for (auto codePath : codePaths) {
+ calculate_tree_size(codePath, &stats.codeSize, -1,
+ multiuser_get_shared_gid(userId, appId));
}
+ ATRACE_END();
+
+ ATRACE_BEGIN("quota");
+ collectQuotaStats(device, userId, appId, &stats, &extStats);
+ ATRACE_END();
+
} else {
- calculate_tree_size(codePath, &stats.codeSize);
+ ATRACE_BEGIN("code");
+ for (auto codePath : codePaths) {
+ calculate_tree_size(codePath, &stats.codeSize);
+ }
+ ATRACE_END();
- auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
- collectManualStats(cePath, &stats);
+ for (size_t i = 0; i < packageNames.size(); i++) {
+ const char* pkgname = packageNames[i].c_str();
- auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
- collectManualStats(dePath, &stats);
+ ATRACE_BEGIN("data");
+ auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]);
+ collectManualStats(cePath, &stats);
+ auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
+ collectManualStats(dePath, &stats);
+ ATRACE_END();
- auto userProfilePath = create_data_user_profile_package_path(userId, pkgname);
- calculate_tree_size(userProfilePath, &stats.dataSize);
+ ATRACE_BEGIN("profiles");
+ auto userProfilePath = create_data_user_profile_package_path(userId, pkgname);
+ calculate_tree_size(userProfilePath, &stats.dataSize);
+ auto refProfilePath = create_data_ref_profile_package_path(pkgname);
+ calculate_tree_size(refProfilePath, &stats.codeSize);
+ ATRACE_END();
- auto refProfilePath = create_data_ref_profile_package_path(pkgname);
- calculate_tree_size(refProfilePath, &stats.codeSize);
+ ATRACE_BEGIN("external");
+ auto extPath = create_data_media_package_path(uuid_, userId, pkgname, "data");
+ collectManualStats(extPath, &extStats);
+ auto mediaPath = create_data_media_package_path(uuid_, userId, pkgname, "media");
+ calculate_tree_size(mediaPath, &extStats.dataSize);
+ ATRACE_END();
+ }
- calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
- multiuser_get_shared_gid(userId, appId), 0);
-
+ ATRACE_BEGIN("dalvik");
+ int32_t sharedGid = multiuser_get_shared_gid(userId, appId);
+ if (sharedGid != -1) {
+ calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+ sharedGid, -1);
+ }
calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
- multiuser_get_uid(userId, appId), 0);
-
-#if MEASURE_EXTERNAL
- auto extPath = create_data_media_package_path(extuuid_, userId, pkgname, "data");
- collectManualStats(extPath, &stats);
-
- auto mediaPath = create_data_media_package_path(extuuid_, userId, pkgname, "media");
- calculate_tree_size(mediaPath, &stats.dataSize);
-#endif
+ multiuser_get_uid(userId, appId), -1);
+ ATRACE_END();
}
std::vector<int64_t> ret;
ret.push_back(stats.codeSize);
ret.push_back(stats.dataSize);
ret.push_back(stats.cacheSize);
+ ret.push_back(extStats.codeSize);
+ ret.push_back(extStats.dataSize);
+ ret.push_back(extStats.cacheSize);
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "Final result " << toString(ret);
+#endif
*_aidl_return = ret;
return ok();
}
+binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid,
+ int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+ std::vector<int64_t>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ // When modifying this logic, always verify using tests:
+ // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetUserSize
+
+#if MEASURE_DEBUG
+ LOG(INFO) << "Measuring user " << userId;
+#endif
+
+ struct stats stats;
+ struct stats extStats;
+ memset(&stats, 0, sizeof(stats));
+ memset(&extStats, 0, sizeof(extStats));
+
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ auto device = findQuotaDeviceForUuid(uuid);
+ if (device.empty()) {
+ flags &= ~FLAG_USE_QUOTA;
+ }
+
+ if (flags & FLAG_USE_QUOTA) {
+ struct dqblk dq;
+
+ ATRACE_BEGIN("obb");
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), AID_MEDIA_OBB,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << AID_MEDIA_OBB;
+ }
+ } else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for GID " << AID_MEDIA_OBB << " " << dq.dqb_curspace;
+#endif
+ extStats.codeSize += dq.dqb_curspace;
+ }
+ ATRACE_END();
+
+ ATRACE_BEGIN("code");
+ calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true);
+ ATRACE_END();
+
+ ATRACE_BEGIN("data");
+ auto cePath = create_data_user_ce_path(uuid_, userId);
+ collectManualStatsForUser(cePath, &stats, true);
+ auto dePath = create_data_user_de_path(uuid_, userId);
+ collectManualStatsForUser(dePath, &stats, true);
+ ATRACE_END();
+
+ ATRACE_BEGIN("profile");
+ auto userProfilePath = create_data_user_profile_path(userId);
+ calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true);
+ auto refProfilePath = create_data_ref_profile_path();
+ calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true);
+ ATRACE_END();
+
+ ATRACE_BEGIN("external");
+ uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
+ if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
+ }
+ } else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
+ extStats.dataSize += dq.dqb_curspace;
+ }
+ ATRACE_END();
+
+ ATRACE_BEGIN("dalvik");
+ calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+ -1, -1, true);
+ calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
+ -1, -1, true);
+ ATRACE_END();
+
+ ATRACE_BEGIN("quota");
+ for (auto appId : appIds) {
+ if (appId >= AID_APP_START) {
+ collectQuotaStats(device, userId, appId, &stats, &extStats);
+#if MEASURE_DEBUG
+ // Sleep to make sure we don't lose logs
+ usleep(1);
+#endif
+ }
+ }
+ ATRACE_END();
+ } else {
+ ATRACE_BEGIN("obb");
+ auto obbPath = create_data_path(uuid_) + "/media/obb";
+ calculate_tree_size(obbPath, &extStats.codeSize);
+ ATRACE_END();
+
+ ATRACE_BEGIN("code");
+ calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize);
+ ATRACE_END();
+
+ ATRACE_BEGIN("data");
+ auto cePath = create_data_user_ce_path(uuid_, userId);
+ collectManualStatsForUser(cePath, &stats);
+ auto dePath = create_data_user_de_path(uuid_, userId);
+ collectManualStatsForUser(dePath, &stats);
+ ATRACE_END();
+
+ ATRACE_BEGIN("profile");
+ auto userProfilePath = create_data_user_profile_path(userId);
+ calculate_tree_size(userProfilePath, &stats.dataSize);
+ auto refProfilePath = create_data_ref_profile_path();
+ calculate_tree_size(refProfilePath, &stats.codeSize);
+ ATRACE_END();
+
+ ATRACE_BEGIN("external");
+ auto dataMediaPath = create_data_media_path(uuid_, userId);
+ collectManualExternalStatsForUser(dataMediaPath, &extStats);
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "Measured external data " << extStats.dataSize << " cache "
+ << extStats.cacheSize;
+#endif
+ ATRACE_END();
+
+ ATRACE_BEGIN("dalvik");
+ calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize);
+ calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize);
+ ATRACE_END();
+ }
+
+ std::vector<int64_t> ret;
+ ret.push_back(stats.codeSize);
+ ret.push_back(stats.dataSize);
+ ret.push_back(stats.cacheSize);
+ ret.push_back(extStats.codeSize);
+ ret.push_back(extStats.dataSize);
+ ret.push_back(extStats.cacheSize);
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+ *_aidl_return = ret;
+ return ok();
+}
+
+binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid,
+ int32_t userId, int32_t flags, std::vector<int64_t>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ // When modifying this logic, always verify using tests:
+ // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetExternalSize
+
+#if MEASURE_DEBUG
+ LOG(INFO) << "Measuring external " << userId;
+#endif
+
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ int64_t totalSize = 0;
+ int64_t audioSize = 0;
+ int64_t videoSize = 0;
+ int64_t imageSize = 0;
+
+ auto device = findQuotaDeviceForUuid(uuid);
+ if (device.empty()) {
+ flags &= ~FLAG_USE_QUOTA;
+ }
+
+ if (flags & FLAG_USE_QUOTA) {
+ struct dqblk dq;
+
+ uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
+ if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
+ }
+ } else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
+ totalSize = dq.dqb_curspace;
+ }
+
+ gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO);
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), audioGid,
+ reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for GID " << audioGid << " " << dq.dqb_curspace;
+#endif
+ audioSize = dq.dqb_curspace;
+ }
+ gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO);
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), videoGid,
+ reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for GID " << videoGid << " " << dq.dqb_curspace;
+#endif
+ videoSize = dq.dqb_curspace;
+ }
+ gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE);
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), imageGid,
+ reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for GID " << imageGid << " " << dq.dqb_curspace;
+#endif
+ imageSize = dq.dqb_curspace;
+ }
+ } else {
+ FTS *fts;
+ FTSENT *p;
+ auto path = create_data_media_path(uuid_, userId);
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+ return error("Failed to fts_open " + path);
+ }
+ while ((p = fts_read(fts)) != NULL) {
+ char* ext;
+ int64_t size = (p->fts_statp->st_blocks * 512);
+ switch (p->fts_info) {
+ case FTS_F:
+ // Only categorize files not belonging to apps
+ if (p->fts_statp->st_gid < AID_APP_START) {
+ ext = strrchr(p->fts_name, '.');
+ if (ext != nullptr) {
+ switch (MatchExtension(++ext)) {
+ case AID_MEDIA_AUDIO: audioSize += size; break;
+ case AID_MEDIA_VIDEO: videoSize += size; break;
+ case AID_MEDIA_IMAGE: imageSize += size; break;
+ }
+ }
+ }
+ // Fall through to always count against total
+ case FTS_D:
+ case FTS_DEFAULT:
+ case FTS_SL:
+ case FTS_SLNONE:
+ totalSize += size;
+ break;
+ }
+ }
+ fts_close(fts);
+ }
+
+ std::vector<int64_t> ret;
+ ret.push_back(totalSize);
+ ret.push_back(audioSize);
+ ret.push_back(videoSize);
+ ret.push_back(imageSize);
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+ *_aidl_return = ret;
+ return ok();
+}
+
+binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
+ int32_t userId, int32_t appId, int64_t cacheQuota) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ int32_t uid = multiuser_get_uid(userId, appId);
+ mCacheQuotas[uid] = cacheQuota;
+
+ return ok();
+}
+
// Dumps the contents of a profile file, using pkgname's dex files for pretty
// printing the result.
binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::string& packageName,
const std::string& codePaths, bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* pkgname = packageName.c_str();
const char* code_paths = codePaths.c_str();
@@ -1081,6 +1621,7 @@
bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* pkgname = packageName.c_str();
*_aidl_return = analyse_profiles(uid, pkgname);
@@ -1097,6 +1638,7 @@
if (packageName && *packageName != "*") {
CHECK_ARGUMENT_PACKAGE_NAME(*packageName);
}
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* apk_path = apkPath.c_str();
const char* pkgname = packageName ? packageName->c_str() : "*";
@@ -1113,6 +1655,8 @@
binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
const char* instruction_set = instructionSet.c_str();
char boot_marker_path[PKG_PATH_MAX];
@@ -1156,6 +1700,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -1278,6 +1823,8 @@
binder::Status InstalldNativeService::idmap(const std::string& targetApkPath,
const std::string& overlayApkPath, int32_t uid) {
ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
const char* target_apk = targetApkPath.c_str();
const char* overlay_apk = overlayApkPath.c_str();
ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
@@ -1349,6 +1896,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
binder::Status res = ok();
@@ -1377,6 +1925,8 @@
binder::Status InstalldNativeService::createOatDir(const std::string& oatDir,
const std::string& instructionSet) {
ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
const char* oat_dir = oatDir.c_str();
const char* instruction_set = instructionSet.c_str();
char oat_instr_dir[PKG_PATH_MAX];
@@ -1399,6 +1949,8 @@
binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) {
ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
if (validate_apk_path(packageDir.c_str())) {
return error("Invalid path " + packageDir);
}
@@ -1411,6 +1963,8 @@
binder::Status InstalldNativeService::linkFile(const std::string& relativePath,
const std::string& fromBase, const std::string& toBase) {
ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
const char* relative_path = relativePath.c_str();
const char* from_base = fromBase.c_str();
const char* to_base = toBase.c_str();
@@ -1437,6 +1991,7 @@
binder::Status InstalldNativeService::moveAb(const std::string& apkPath,
const std::string& instructionSet, const std::string& outputPath) {
ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* apk_path = apkPath.c_str();
const char* instruction_set = instructionSet.c_str();
@@ -1449,6 +2004,7 @@
binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
const std::string& instructionSet, const std::string& outputPath) {
ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
const char* apk_path = apkPath.c_str();
const char* instruction_set = instructionSet.c_str();
@@ -1458,5 +2014,42 @@
return res ? ok() : error();
}
+binder::Status InstalldNativeService::invalidateMounts() {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ mQuotaDevices.clear();
+
+ std::ifstream in("/proc/mounts");
+ if (!in.is_open()) {
+ return error("Failed to read mounts");
+ }
+
+ std::string source;
+ std::string target;
+ std::string ignored;
+ struct dqblk dq;
+ while (!in.eof()) {
+ std::getline(in, source, ' ');
+ std::getline(in, target, ' ');
+ std::getline(in, ignored);
+
+ if (source.compare(0, 11, "/dev/block/") == 0) {
+ if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
+ reinterpret_cast<char*>(&dq)) == 0) {
+ LOG(DEBUG) << "Found " << source << " with quota";
+ mQuotaDevices[target] = source;
+ }
+ }
+ }
+ return ok();
+}
+
+std::string InstalldNativeService::findQuotaDeviceForUuid(
+ const std::unique_ptr<std::string>& uuid) {
+ auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
+ return mQuotaDevices[path];
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index cad9e43..0a9f12f 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -22,7 +22,9 @@
#include <unistd.h>
#include <vector>
+#include <unordered_map>
+#include <android-base/macros.h>
#include <binder/BinderService.h>
#include <cutils/multiuser.h>
@@ -55,10 +57,19 @@
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
+
binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
- const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- int64_t ceDataInode, const std::string& codePath,
- const std::unique_ptr<std::string>& externalUuid, std::vector<int64_t>* _aidl_return);
+ const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+ int32_t appId, const std::vector<int64_t>& ceDataInodes,
+ const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return);
+ binder::Status getUserSize(const std::unique_ptr<std::string>& uuid,
+ int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+ std::vector<int64_t>* _aidl_return);
+ binder::Status getExternalSize(const std::unique_ptr<std::string>& uuid,
+ int32_t userId, int32_t flags, std::vector<int64_t>* _aidl_return);
+
+ binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
+ int32_t userId, int32_t appId, int64_t cacheQuota);
binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
@@ -83,7 +94,8 @@
int32_t uid);
binder::Status rmPackageDir(const std::string& packageDir);
binder::Status markBootComplete(const std::string& instructionSet);
- binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t freeStorageSize);
+ binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t freeStorageSize,
+ int32_t flags);
binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
@@ -93,6 +105,18 @@
const std::string& outputPath);
binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
const std::string& outputPath);
+
+ binder::Status invalidateMounts();
+
+private:
+ std::recursive_mutex mLock;
+
+ /* Map from mount point to underlying device node */
+ std::unordered_map<std::string, std::string> mQuotaDevices;
+ /* Map from UID to cache quota size */
+ std::unordered_map<uid_t, int64_t> mCacheQuotas;
+
+ std::string findQuotaDeviceForUuid(const std::unique_ptr<std::string>& uuid);
};
} // namespace installd
diff --git a/cmds/installd/MatchExtensionGen.h b/cmds/installd/MatchExtensionGen.h
new file mode 100644
index 0000000..fded6b7
--- /dev/null
+++ b/cmds/installd/MatchExtensionGen.h
@@ -0,0 +1,628 @@
+/*
+ * 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.
+ */
+
+/******************************************************************
+ * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY *
+ ******************************************************************/
+
+#include <private/android_filesystem_config.h>
+
+int MatchExtension(const char* ext) {
+
+ switch (ext[0]) {
+ case '3':
+ switch (ext[1]) {
+ case 'g': case 'G':
+ switch (ext[2]) {
+ case '2':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ case 'p': case 'P':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ case 'p': case 'P':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ case '2':
+ switch (ext[5]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ }
+ }
+ }
+ case 'a': case 'A':
+ switch (ext[1]) {
+ case 'a': case 'A':
+ switch (ext[2]) {
+ case 'c': case 'C':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case 'i': case 'I':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ case 'c': case 'C':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case 'f': case 'F':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ }
+ case 'm': case 'M':
+ switch (ext[2]) {
+ case 'r': case 'R':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case 'r': case 'R':
+ switch (ext[2]) {
+ case 't': case 'T':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ case 'w': case 'W':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 's': case 'S':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ case 'x': case 'X':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'v': case 'V':
+ switch (ext[2]) {
+ case 'i': case 'I':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'w': case 'W':
+ switch (ext[2]) {
+ case 'b': case 'B':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ }
+ case 'b': case 'B':
+ switch (ext[1]) {
+ case 'm': case 'M':
+ switch (ext[2]) {
+ case 'p': case 'P':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 'c': case 'C':
+ switch (ext[1]) {
+ case 'r': case 'R':
+ switch (ext[2]) {
+ case '2':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 'd': case 'D':
+ switch (ext[1]) {
+ case 'i': case 'I':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'l': case 'L':
+ switch (ext[2]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ case 'n': case 'N':
+ switch (ext[2]) {
+ case 'g': case 'G':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'v': case 'V':
+ switch (ext[2]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'f': case 'F':
+ switch (ext[1]) {
+ case 'l': case 'L':
+ switch (ext[2]) {
+ case 'a': case 'A':
+ switch (ext[3]) {
+ case 'c': case 'C':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case 'i': case 'I':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ }
+ case 'g': case 'G':
+ switch (ext[1]) {
+ case 'i': case 'I':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 's': case 'S':
+ switch (ext[2]) {
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ }
+ case 'j': case 'J':
+ switch (ext[1]) {
+ case 'n': case 'N':
+ switch (ext[2]) {
+ case 'g': case 'G':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'p': case 'P':
+ switch (ext[2]) {
+ case 'e': case 'E':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ case 'g': case 'G':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'g': case 'G':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 'l': case 'L':
+ switch (ext[1]) {
+ case 's': case 'S':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ case 'x': case 'X':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ }
+ case 'm': case 'M':
+ switch (ext[1]) {
+ case '3':
+ switch (ext[2]) {
+ case 'u': case 'U':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case '4':
+ switch (ext[2]) {
+ case 'a': case 'A':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case 'v': case 'V':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'k': case 'K':
+ switch (ext[2]) {
+ case 'a': case 'A':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case 'v': case 'V':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'n': case 'N':
+ switch (ext[2]) {
+ case 'g': case 'G':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'o': case 'O':
+ switch (ext[2]) {
+ case 'v': case 'V':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ case 'i': case 'I':
+ switch (ext[4]) {
+ case 'e': case 'E':
+ switch (ext[5]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ }
+ }
+ case 'p': case 'P':
+ switch (ext[2]) {
+ case '2':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case '3':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case '4':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ case 'e': case 'E':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ case 'g': case 'G':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ case 'a': case 'A':
+ switch (ext[5]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ }
+ case 'g': case 'G':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ case 'a': case 'A':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ }
+ case 'x': case 'X':
+ switch (ext[2]) {
+ case 'u': case 'U':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ }
+ case 'n': case 'N':
+ switch (ext[1]) {
+ case 'e': case 'E':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'r': case 'R':
+ switch (ext[2]) {
+ case 'w': case 'W':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 'o': case 'O':
+ switch (ext[1]) {
+ case 'g': case 'G':
+ switch (ext[2]) {
+ case 'a': case 'A':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case 'g': case 'G':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case 'r': case 'R':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 'p': case 'P':
+ switch (ext[1]) {
+ case 'b': case 'B':
+ switch (ext[2]) {
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'c': case 'C':
+ switch (ext[2]) {
+ case 'x': case 'X':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'e': case 'E':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'g': case 'G':
+ switch (ext[2]) {
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'l': case 'L':
+ switch (ext[2]) {
+ case 's': case 'S':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case 'n': case 'N':
+ switch (ext[2]) {
+ case 'g': case 'G':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'p': case 'P':
+ switch (ext[2]) {
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 's': case 'S':
+ switch (ext[2]) {
+ case 'd': case 'D':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 'q': case 'Q':
+ switch (ext[1]) {
+ case 't': case 'T':
+ switch (ext[2]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'r': case 'R':
+ switch (ext[1]) {
+ case 'a': case 'A':
+ switch (ext[2]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case 's': case 'S':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'g': case 'G':
+ switch (ext[2]) {
+ case 'b': case 'B':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'm': case 'M':
+ switch (ext[2]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case 'w': case 'W':
+ switch (ext[2]) {
+ case '2':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 's': case 'S':
+ switch (ext[1]) {
+ case 'd': case 'D':
+ switch (ext[2]) {
+ case '2':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case 'n': case 'N':
+ switch (ext[2]) {
+ case 'd': case 'D':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case 'r': case 'R':
+ switch (ext[2]) {
+ case 'w': case 'W':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'v': case 'V':
+ switch (ext[2]) {
+ case 'g': case 'G':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ case 'z': case 'Z':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ }
+ case 't': case 'T':
+ switch (ext[1]) {
+ case 'i': case 'I':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ case 'f': case 'F':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 's': case 'S':
+ switch (ext[2]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'v': case 'V':
+ switch (ext[1]) {
+ case 'o': case 'O':
+ switch (ext[2]) {
+ case 'b': case 'B':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ }
+ case 'w': case 'W':
+ switch (ext[1]) {
+ case 'a': case 'A':
+ switch (ext[2]) {
+ case 'v': case 'V':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case 'x': case 'X':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ }
+ case 'b': case 'B':
+ switch (ext[2]) {
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case 'p': case 'P':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 'e': case 'E':
+ switch (ext[2]) {
+ case 'b': case 'B':
+ switch (ext[3]) {
+ case 'm': case 'M':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ case 'p': case 'P':
+ switch (ext[4]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ case 'm': case 'M':
+ switch (ext[2]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ case 'a': case 'A':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_AUDIO;
+ }
+ case 'v': case 'V':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ case 'x': case 'X':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'r': case 'R':
+ switch (ext[2]) {
+ case 'f': case 'F':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ case 'v': case 'V':
+ switch (ext[2]) {
+ case 'x': case 'X':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_VIDEO;
+ }
+ }
+ }
+ case 'x': case 'X':
+ switch (ext[1]) {
+ case 'b': case 'B':
+ switch (ext[2]) {
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'p': case 'P':
+ switch (ext[2]) {
+ case 'm': case 'M':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ case 'w': case 'W':
+ switch (ext[2]) {
+ case 'd': case 'D':
+ switch (ext[3]) {
+ case '\0': return AID_MEDIA_IMAGE;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 8c5d2f4..aa5e4f2 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -31,9 +31,14 @@
int userId, int flags, long ceDataInode);
void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, int flags, long ceDataInode);
- long[] getAppSize(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
- int userId, int flags, int appId, long ceDataInode, @utf8InCpp String codePath,
- @nullable @utf8InCpp String externalUuid);
+
+ long[] getAppSize(@nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+ int userId, int flags, int appId, in long[] ceDataInodes,
+ in @utf8InCpp String[] codePaths);
+ long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
+ long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags);
+
+ void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota);
void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
@utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
@@ -55,7 +60,7 @@
void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
void rmPackageDir(@utf8InCpp String packageDir);
void markBootComplete(@utf8InCpp String instructionSet);
- void freeCache(@nullable @utf8InCpp String uuid, long freeStorageSize);
+ void freeCache(@nullable @utf8InCpp String uuid, long freeStorageSize, int flags);
void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
@utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
@@ -65,4 +70,6 @@
@utf8InCpp String outputPath);
void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
@utf8InCpp String outputPath);
+
+ void invalidateMounts();
}
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 5025fde..1565d0d 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -27,13 +27,13 @@
#include <sys/wait.h>
#include <unistd.h>
-#include <android/log.h> // TODO: Move everything to base/logging.
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
+#include <log/log.h> // TODO: Move everything to base/logging.
#include <private/android_filesystem_config.h>
#include <system/thread_defs.h>
@@ -153,57 +153,6 @@
return count;
}
-static void run_patchoat(int input_oat_fd, int input_vdex_fd, int out_oat_fd, int out_vdex_fd,
- const char* input_oat_file_name, const char* input_vdex_file_name,
- const char* output_oat_file_name, const char* output_vdex_file_name,
- const char *pkgname ATTRIBUTE_UNUSED, const char *instruction_set)
-{
- static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig
- static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
-
- static const char* PATCHOAT_BIN = "/system/bin/patchoat";
- if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
- ALOGE("Instruction set %s longer than max length of %d",
- instruction_set, MAX_INSTRUCTION_SET_LEN);
- return;
- }
-
- /* input_file_name/input_fd should be the .odex/.oat file that is precompiled. I think*/
- char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
- char input_oat_fd_arg[strlen("--input-oat-fd=") + MAX_INT_LEN];
- char input_vdex_fd_arg[strlen("--input-vdex-fd=") + MAX_INT_LEN];
- char output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN];
- char output_vdex_fd_arg[strlen("--output-vdex-fd=") + MAX_INT_LEN];
- const char* patched_image_location_arg = "--patched-image-location=/system/framework/boot.art";
- // The caller has already gotten all the locks we need.
- const char* no_lock_arg = "--no-lock-output";
- sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
- sprintf(output_oat_fd_arg, "--output-oat-fd=%d", out_oat_fd);
- sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_oat_fd);
- ALOGV("Running %s isa=%s in-oat-fd=%d (%s) in-vdex-fd=%d (%s) "
- "out-oat-fd=%d (%s) out-vdex-fd=%d (%s)\n",
- PATCHOAT_BIN, instruction_set,
- input_oat_fd, input_oat_file_name,
- input_vdex_fd, input_vdex_file_name,
- out_oat_fd, output_oat_file_name,
- out_vdex_fd, output_vdex_file_name);
-
- /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */
- char* argv[9];
- argv[0] = (char*) PATCHOAT_BIN;
- argv[1] = (char*) patched_image_location_arg;
- argv[2] = (char*) no_lock_arg;
- argv[3] = instruction_set_arg;
- argv[4] = input_oat_fd_arg;
- argv[5] = input_vdex_fd_arg;
- argv[6] = output_oat_fd_arg;
- argv[7] = output_vdex_fd_arg;
- argv[8] = NULL;
-
- execv(PATCHOAT_BIN, (char* const *)argv);
- ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno));
-}
-
static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd,
const char* input_file_name, const char* output_file_name, int swap_fd,
const char *instruction_set, const char* compiler_filter, bool vm_safe_mode,
@@ -502,7 +451,7 @@
return kDefaultProvideSwapFile;
}
-static void SetDex2OatAndPatchOatScheduling(bool set_to_bg) {
+static void SetDex2OatScheduling(bool set_to_bg) {
if (set_to_bg) {
if (set_sched_policy(0, SP_BACKGROUND) < 0) {
ALOGE("set_sched_policy failed: %s\n", strerror(errno));
@@ -955,7 +904,7 @@
//
// Usage example:
//
-// Dex2oatFileWrapper<std::function<void ()>> file(open(...),
+// Dex2oatFileWrapper file(open(...),
// [name]() {
// unlink(name.c_str());
// });
@@ -978,14 +927,30 @@
// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
// // (leaving the file around; after the fd is closed).
//
-template <typename Cleanup>
class Dex2oatFileWrapper {
public:
- Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true) {
+ Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(true) {
}
- Dex2oatFileWrapper(int value, Cleanup cleanup)
- : value_(value), cleanup_(cleanup), do_cleanup_(true) {}
+ Dex2oatFileWrapper(int value, std::function<void ()> cleanup)
+ : value_(value), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
+
+ Dex2oatFileWrapper(Dex2oatFileWrapper&& other) {
+ value_ = other.value_;
+ cleanup_ = other.cleanup_;
+ do_cleanup_ = other.do_cleanup_;
+ auto_close_ = other.auto_close_;
+ other.release();
+ }
+
+ Dex2oatFileWrapper& operator=(Dex2oatFileWrapper&& other) {
+ value_ = other.value_;
+ cleanup_ = other.cleanup_;
+ do_cleanup_ = other.do_cleanup_;
+ auto_close_ = other.auto_close_;
+ other.release();
+ return *this;
+ }
~Dex2oatFileWrapper() {
reset(-1);
@@ -1000,7 +965,7 @@
}
void reset(int new_value) {
- if (value_ >= 0) {
+ if (auto_close_ && value_ >= 0) {
close(value_);
}
if (do_cleanup_ && cleanup_ != nullptr) {
@@ -1010,8 +975,8 @@
value_ = new_value;
}
- void reset(int new_value, Cleanup new_cleanup) {
- if (value_ >= 0) {
+ void reset(int new_value, std::function<void ()> new_cleanup) {
+ if (auto_close_ && value_ >= 0) {
close(value_);
}
if (do_cleanup_ && cleanup_ != nullptr) {
@@ -1022,117 +987,127 @@
cleanup_ = new_cleanup;
}
+ void DisableAutoClose() {
+ auto_close_ = false;
+ }
+
private:
+ void release() {
+ value_ = -1;
+ do_cleanup_ = false;
+ cleanup_ = nullptr;
+ }
int value_;
- Cleanup cleanup_;
+ std::function<void ()> cleanup_;
bool do_cleanup_;
+ bool auto_close_;
};
-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 ATTRIBUTE_UNUSED, const char* shared_libraries) {
- bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0);
- bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
- bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
- bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
- bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+// (re)Creates the app image if needed.
+Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, bool profile_guided,
+ bool is_public, int uid) {
+ // Use app images only if it is enabled (by a set image format) and we are compiling
+ // profile-guided (so the app image doesn't conservatively contain all classes).
+ if (!profile_guided) {
+ return Dex2oatFileWrapper();
+ }
- CHECK(pkgname != nullptr);
- CHECK(pkgname[0] != 0);
+ const std::string image_path = create_image_filename(out_oat_path);
+ if (image_path.empty()) {
+ // Happens when the out_oat_path has an unknown extension.
+ return Dex2oatFileWrapper();
+ }
+ char app_image_format[kPropertyValueMax];
+ bool have_app_image_format =
+ get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+ if (!have_app_image_format) {
+ return Dex2oatFileWrapper();
+ }
+ // Recreate is true since we do not want to modify a mapped image. If the app is
+ // already running and we modify the image file, it can cause crashes (b/27493510).
+ Dex2oatFileWrapper wrapper_fd(
+ open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
+ [image_path]() { unlink(image_path.c_str()); });
+ if (wrapper_fd.get() < 0) {
+ // Could not create application image file. Go on since we can compile without it.
+ LOG(ERROR) << "installd could not create '" << image_path
+ << "' for image file during dexopt";
+ // If we have a valid image file path but no image fd, explicitly erase the image file.
+ if (unlink(image_path.c_str()) < 0) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Couldn't unlink image file " << image_path;
+ }
+ }
+ } else if (!set_permissions_and_ownership(
+ wrapper_fd.get(), is_public, uid, image_path.c_str())) {
+ ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
+ wrapper_fd.reset(-1);
+ }
+ return wrapper_fd;
+}
+
+// Creates the dexopt swap file if necessary and return its fd.
+// Returns -1 if there's no need for a swap or in case of errors.
+base::unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
+ if (!ShouldUseSwapFileForDexopt()) {
+ return base::unique_fd();
+ }
+ // Make sure there really is enough space.
+ char swap_file_name[PKG_PATH_MAX];
+ strcpy(swap_file_name, out_oat_path);
+ if (!add_extension_to_file_name(swap_file_name, ".swap")) {
+ return base::unique_fd();
+ }
+ base::unique_fd swap_fd(open_output_file(
+ swap_file_name, /*recreate*/true, /*permissions*/0600));
+ if (swap_fd.get() < 0) {
+ // Could not create swap file. Optimistically go on and hope that we can compile
+ // without it.
+ ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
+ } else {
+ // Immediately unlink. We don't really want to hit flash.
+ if (unlink(swap_file_name) < 0) {
+ PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
+ }
+ }
+ return swap_fd;
+}
+
+// Opens the reference profiles if needed.
+// Note that the reference profile might not exist so it's OK if the fd will be -1.
+Dex2oatFileWrapper maybe_open_reference_profile(const char* pkgname, bool profile_guided,
+ bool is_public, int uid) {
// Public apps should not be compiled with profile information ever. Same goes for the special
// package '*' used for the system server.
- Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd;
- if (!is_public && pkgname[0] != '*') {
+ if (profile_guided && !is_public && (pkgname[0] != '*')) {
// Open reference profile in read only mode as dex2oat does not get write permissions.
const std::string pkgname_str(pkgname);
- reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false),
- [pkgname_str]() {
- clear_reference_profile(pkgname_str.c_str());
- });
- // Note: it's OK to not find a profile here.
+ return Dex2oatFileWrapper(
+ open_reference_profile(uid, pkgname, /*read_write*/ false),
+ [pkgname_str]() {
+ clear_reference_profile(pkgname_str.c_str());
+ });
+ } else {
+ return Dex2oatFileWrapper();
}
+}
- if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
- LOG_FATAL("dexopt flags contains unknown fields\n");
- }
-
- char out_oat_path[PKG_PATH_MAX];
- if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_oat_path)) {
- return false;
- }
-
- const char *input_file;
+// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
+// out_vdex_wrapper_fd. Returns true for success or false in case of errors.
+bool open_vdex_files(const char* apk_path, const char* out_oat_path, int dexopt_needed,
+ const char* instruction_set, bool is_public, int uid,
+ Dex2oatFileWrapper* in_vdex_wrapper_fd,
+ Dex2oatFileWrapper* out_vdex_wrapper_fd) {
+ CHECK(in_vdex_wrapper_fd != nullptr);
+ CHECK(out_vdex_wrapper_fd != nullptr);
+ // Open the existing VDEX. We do this before creating the new output VDEX, which will
+ // unlink the old one.
char in_odex_path[PKG_PATH_MAX];
int dexopt_action = abs(dexopt_needed);
bool is_odex_location = dexopt_needed < 0;
- switch (dexopt_action) {
- case DEX2OAT_FROM_SCRATCH:
- case DEX2OAT_FOR_BOOT_IMAGE:
- case DEX2OAT_FOR_FILTER:
- case DEX2OAT_FOR_RELOCATION:
- input_file = apk_path;
- break;
-
- case PATCHOAT_FOR_RELOCATION:
- if (is_odex_location) {
- if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
- return -1;
- }
- input_file = in_odex_path;
- } else {
- input_file = out_oat_path;
- }
- break;
-
- default:
- ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
- return 72;
- }
-
- struct stat input_stat;
- memset(&input_stat, 0, sizeof(input_stat));
- stat(input_file, &input_stat);
-
- // Open the input file. If running dex2oat, `input_file` is the APK. If running
- // patchoat, it is the OAT file to be relocated.
- base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
- if (input_fd.get() < 0) {
- ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
- return -1;
- }
-
- // Create the output OAT file.
- const std::string out_oat_path_str(out_oat_path);
- Dex2oatFileWrapper<std::function<void ()>> out_oat_fd(
- open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
- [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
- if (out_oat_fd.get() < 0) {
- ALOGE("installd cannot open '%s' for output during dexopt\n", out_oat_path);
- return -1;
- }
- if (!set_permissions_and_ownership(out_oat_fd.get(), is_public, uid, out_oat_path)) {
- return -1;
- }
-
- // Open the existing VDEX. We do this before creating the new output VDEX, which will
- // unlink the old one.
- base::unique_fd in_vdex_fd;
std::string in_vdex_path_str;
- if (dexopt_action == PATCHOAT_FOR_RELOCATION) {
- // `input_file` is the OAT file to be relocated. The VDEX has to be there as well.
- in_vdex_path_str = create_vdex_filename(input_file);
- if (in_vdex_path_str.empty()) {
- ALOGE("installd cannot compute input vdex location for '%s'\n", input_file);
- return -1;
- }
- in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
- if (in_vdex_fd.get() < 0) {
- ALOGE("installd cannot open '%s' for input during dexopt: %s\n",
- in_vdex_path_str.c_str(), strerror(errno));
- return -1;
- }
- } else if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
+ if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
// Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
const char* path = nullptr;
if (is_odex_location) {
@@ -1140,7 +1115,7 @@
path = in_odex_path;
} else {
ALOGE("installd cannot compute input vdex location for '%s'\n", apk_path);
- return -1;
+ return false;
}
} else {
path = out_oat_path;
@@ -1148,169 +1123,183 @@
in_vdex_path_str = create_vdex_filename(path);
if (in_vdex_path_str.empty()) {
ALOGE("installd cannot compute input vdex location for '%s'\n", path);
- return -1;
+ return false;
}
if (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) {
// When we dex2oat because iof boot image change, we are going to update
// in-place the vdex file.
- in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
+ in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
} else {
- in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+ in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
}
}
// Infer the name of the output VDEX and create it.
- const std::string out_vdex_path_str = create_vdex_filename(out_oat_path_str);
+ const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
if (out_vdex_path_str.empty()) {
- return -1;
+ return false;
}
- Dex2oatFileWrapper<std::function<void ()>> out_vdex_wrapper_fd;
- int out_vdex_fd = -1;
// If we are compiling because the boot image is out of date, we do not
// need to recreate a vdex, and can use the same existing one.
if (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE &&
- in_vdex_fd != -1 &&
+ in_vdex_wrapper_fd->get() != -1 &&
in_vdex_path_str == out_vdex_path_str) {
- out_vdex_fd = in_vdex_fd;
+ out_vdex_wrapper_fd->reset(in_vdex_wrapper_fd->get());
+ // Disable auto close for the in wrapper fd (it will be done when destructing the out
+ // wrapper).
+ in_vdex_wrapper_fd->DisableAutoClose();
} else {
- out_vdex_wrapper_fd.reset(
+ out_vdex_wrapper_fd->reset(
open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
[out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
- out_vdex_fd = out_vdex_wrapper_fd.get();
- if (out_vdex_fd < 0) {
- ALOGE("installd cannot open '%s' for output during dexopt\n", out_vdex_path_str.c_str());
- return -1;
+ if (out_vdex_wrapper_fd->get() < 0) {
+ ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
+ return false;
}
}
- if (!set_permissions_and_ownership(out_vdex_fd, is_public,
- uid, out_vdex_path_str.c_str())) {
+ if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
+ out_vdex_path_str.c_str())) {
+ ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
+ return false;
+ }
+
+ // If we got here we successfully opened the vdex files.
+ return true;
+}
+
+// Opens the output oat file for the given apk.
+// If successful it stores the output path into out_oat_path and returns true.
+Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir,
+ bool is_public, int uid, const char* instruction_set, char* out_oat_path) {
+ if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_oat_path)) {
+ return Dex2oatFileWrapper();
+ }
+ const std::string out_oat_path_str(out_oat_path);
+ Dex2oatFileWrapper wrapper_fd(
+ open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
+ [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
+ if (wrapper_fd.get() < 0) {
+ ALOGE("installd cannot open '%s' for output during dexopt\n", out_oat_path);
+ } else if (!set_permissions_and_ownership(wrapper_fd.get(), is_public, uid, out_oat_path)) {
+ ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
+ wrapper_fd.reset(-1);
+ }
+ return wrapper_fd;
+}
+
+// Updates the access times of out_oat_path based on those from apk_path.
+void update_out_oat_access_times(const char* apk_path, const char* out_oat_path) {
+ struct stat input_stat;
+ memset(&input_stat, 0, sizeof(input_stat));
+ if (stat(apk_path, &input_stat) != 0) {
+ PLOG(ERROR) << "Could not stat " << apk_path << " during dexopt";
+ return;
+ }
+
+ struct utimbuf ut;
+ ut.actime = input_stat.st_atime;
+ ut.modtime = input_stat.st_mtime;
+ if (utime(out_oat_path, &ut) != 0) {
+ PLOG(WARNING) << "Could not update access times for " << apk_path << " during dexopt";
+ }
+}
+
+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 ATTRIBUTE_UNUSED, const char* shared_libraries) {
+ CHECK(pkgname != nullptr);
+ CHECK(pkgname[0] != 0);
+ if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
+ LOG_FATAL("dexopt flags contains unknown fields\n");
+ }
+
+ bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0);
+ bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
+ bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
+ bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
+ bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+
+ // Open the input file.
+ base::unique_fd input_fd(open(apk_path, O_RDONLY, 0));
+ if (input_fd.get() < 0) {
+ ALOGE("installd cannot open '%s' for input during dexopt\n", apk_path);
+ return -1;
+ }
+
+ // Create the output OAT file.
+ char out_oat_path[PKG_PATH_MAX];
+ Dex2oatFileWrapper out_oat_fd = open_oat_out_file(apk_path, oat_dir, is_public, uid,
+ instruction_set, out_oat_path);
+ if (out_oat_fd.get() < 0) {
+ return -1;
+ }
+
+ // Open vdex files.
+ Dex2oatFileWrapper in_vdex_fd;
+ Dex2oatFileWrapper out_vdex_fd;
+ if (!open_vdex_files(apk_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
+ &in_vdex_fd, &out_vdex_fd)) {
return -1;
}
// Create a swap file if necessary.
- base::unique_fd swap_fd;
- if (ShouldUseSwapFileForDexopt()) {
- // Make sure there really is enough space.
- char swap_file_name[PKG_PATH_MAX];
- strcpy(swap_file_name, out_oat_path);
- if (add_extension_to_file_name(swap_file_name, ".swap")) {
- swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600));
- }
- if (swap_fd.get() < 0) {
- // Could not create swap file. Optimistically go on and hope that we can compile
- // without it.
- ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
- } else {
- // Immediately unlink. We don't really want to hit flash.
- if (unlink(swap_file_name) < 0) {
- PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
- }
- }
- }
+ base::unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
- // Avoid generating an app image for extract only since it will not contain any classes.
- Dex2oatFileWrapper<std::function<void ()>> image_fd;
- const std::string image_path = create_image_filename(out_oat_path);
- if (dexopt_action != PATCHOAT_FOR_RELOCATION && !image_path.empty()) {
- char app_image_format[kPropertyValueMax];
- bool have_app_image_format =
- get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
- // Use app images only if it is enabled (by a set image format) and we are compiling
- // profile-guided (so the app image doesn't conservatively contain all classes).
- if (profile_guided && have_app_image_format) {
- // Recreate is true since we do not want to modify a mapped image. If the app is
- // already running and we modify the image file, it can cause crashes (b/27493510).
- image_fd.reset(open_output_file(image_path.c_str(),
- true /*recreate*/,
- 0600 /*permissions*/),
- [image_path]() { unlink(image_path.c_str()); }
- );
- if (image_fd.get() < 0) {
- // Could not create application image file. Go on since we can compile without
- // it.
- LOG(ERROR) << "installd could not create '"
- << image_path
- << "' for image file during dexopt";
- } else if (!set_permissions_and_ownership(image_fd.get(),
- is_public,
- uid,
- image_path.c_str())) {
- image_fd.reset(-1);
- }
- }
- // If we have a valid image file path but no image fd, explicitly erase the image file.
- if (image_fd.get() < 0) {
- if (unlink(image_path.c_str()) < 0) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "Couldn't unlink image file " << image_path;
- }
- }
- }
- }
+ // Create the app image file if needed.
+ Dex2oatFileWrapper image_fd =
+ maybe_open_app_image(out_oat_path, profile_guided, is_public, uid);
- ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
+ // Open the reference profile if needed.
+ Dex2oatFileWrapper reference_profile_fd =
+ maybe_open_reference_profile(pkgname, profile_guided, is_public, uid);
+
+ ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
pid_t pid = fork();
if (pid == 0) {
/* child -- drop privileges before continuing */
drop_capabilities(uid);
- SetDex2OatAndPatchOatScheduling(boot_complete);
+ SetDex2OatScheduling(boot_complete);
if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
ALOGE("flock(%s) failed: %s\n", out_oat_path, strerror(errno));
_exit(67);
}
- if (dexopt_action == PATCHOAT_FOR_RELOCATION) {
- run_patchoat(input_fd.get(),
- in_vdex_fd.get(),
- out_oat_fd.get(),
- out_vdex_fd,
- input_file,
- in_vdex_path_str.c_str(),
- out_oat_path,
- out_vdex_path_str.c_str(),
- pkgname,
- instruction_set);
- } else {
- // Pass dex2oat the relative path to the input file.
- const char *input_file_name = get_location_from_path(input_file);
- run_dex2oat(input_fd.get(),
- out_oat_fd.get(),
- in_vdex_fd.get(),
- out_vdex_fd,
- image_fd.get(),
- input_file_name,
- out_oat_path,
- swap_fd.get(),
- instruction_set,
- compiler_filter,
- vm_safe_mode,
- debuggable,
- boot_complete,
- reference_profile_fd.get(),
- shared_libraries);
- }
+ // Pass dex2oat the relative path to the input file.
+ const char *input_file_name = get_location_from_path(apk_path);
+ run_dex2oat(input_fd.get(),
+ out_oat_fd.get(),
+ in_vdex_fd.get(),
+ out_vdex_fd.get(),
+ image_fd.get(),
+ input_file_name,
+ out_oat_path,
+ swap_fd.get(),
+ instruction_set,
+ compiler_filter,
+ vm_safe_mode,
+ debuggable,
+ boot_complete,
+ reference_profile_fd.get(),
+ shared_libraries);
_exit(68); /* only get here on exec failure */
} else {
int res = wait_child(pid);
if (res == 0) {
- ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
+ ALOGV("DexInv: --- END '%s' (success) ---\n", apk_path);
} else {
- ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
+ ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", apk_path, res);
return -1;
}
}
- struct utimbuf ut;
- ut.actime = input_stat.st_atime;
- ut.modtime = input_stat.st_mtime;
- utime(out_oat_path, &ut);
+ update_out_oat_access_times(apk_path, out_oat_path);
// We've been successful, don't delete output.
out_oat_fd.SetCleanup(false);
- out_vdex_wrapper_fd.SetCleanup(false);
+ out_vdex_fd.SetCleanup(false);
image_fd.SetCleanup(false);
reference_profile_fd.SetCleanup(false);
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index c0ea79c..edcdb6a 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -19,7 +19,7 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h> // TODO: Move everything to base::logging.
+#include <log/log.h> // TODO: Move everything to base::logging.
#include <globals.h>
#include <installd_constants.h>
diff --git a/cmds/installd/matchgen.py b/cmds/installd/matchgen.py
new file mode 100644
index 0000000..b37352b
--- /dev/null
+++ b/cmds/installd/matchgen.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+
+# 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.
+
+import collections
+
+TYPES = {
+ "AID_MEDIA_AUDIO": ["aac","aac","amr","awb","snd","flac","flac","mp3","mpga","mpega","mp2","m4a","aif","aiff","aifc","gsm","mka","m3u","wma","wax","ra","rm","ram","ra","pls","sd2","wav","ogg","oga"],
+ "AID_MEDIA_VIDEO": ["3gpp","3gp","3gpp2","3g2","avi","dl","dif","dv","fli","m4v","ts","mpeg","mpg","mpe","mp4","vob","qt","mov","mxu","webm","lsf","lsx","mkv","mng","asf","asx","wm","wmv","wmx","wvx","movie","wrf"],
+ "AID_MEDIA_IMAGE": ["bmp","gif","jpg","jpeg","jpe","pcx","png","svg","svgz","tiff","tif","wbmp","webp","dng","cr2","ras","art","jng","nef","nrw","orf","rw2","pef","psd","pnm","pbm","pgm","ppm","srw","arw","rgb","xbm","xpm","xwd"]
+}
+
+print """/*
+ * 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.
+ */
+
+/******************************************************************
+ * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY *
+ ******************************************************************/
+
+#include <private/android_filesystem_config.h>
+
+int MatchExtension(const char* ext) {
+"""
+
+trie = collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: ""))))))
+
+for t in TYPES:
+ for v in TYPES[t]:
+ v = v.lower()
+ target = trie
+ for c in v:
+ target = target[c]
+ target["\0"] = t
+
+def dump(target, index):
+ prefix = " " * (index + 1)
+ print "%sswitch (ext[%d]) {" % (prefix, index)
+ for k in sorted(target.keys()):
+ if k == "\0":
+ print "%scase '\\0': return %s;" % (prefix, target[k])
+ else:
+ upper = k.upper()
+ if k != upper:
+ print "%scase '%s': case '%s':" % (prefix, k, upper)
+ else:
+ print "%scase '%s':" % (prefix, k)
+ dump(target[k], index + 1)
+ print "%s}" % (prefix)
+
+dump(trie, 0)
+
+print """
+ return 0;
+}
+"""
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 7aba5d0..c74c65b 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -27,13 +27,13 @@
#include <sys/stat.h>
#include <sys/wait.h>
-#include <android/log.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/fs.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include "InstalldNativeService.h"
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index e1a59d4..0b1cd7e 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -30,10 +30,10 @@
#include <sys/statfs.h>
#endif
-#include <android/log.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/fs.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include "globals.h" // extern variables.
@@ -199,6 +199,10 @@
return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
}
+std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name) {
+ return StringPrintf("%s/media/obb/%s", create_data_path(volume_uuid).c_str(), package_name);
+}
+
std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
const char* data_type, const char* package_name) {
return StringPrintf("%s/Android/%s/%s", create_data_media_path(volume_uuid, userid).c_str(),
@@ -209,13 +213,17 @@
return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid);
}
-std::string create_data_user_profiles_path(userid_t userid) {
+std::string create_data_user_profile_path(userid_t userid) {
return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
}
std::string create_data_user_profile_package_path(userid_t user, const char* package_name) {
check_package_name(package_name);
- return StringPrintf("%s/%s",create_data_user_profiles_path(user).c_str(), package_name);
+ return StringPrintf("%s/%s",create_data_user_profile_path(user).c_str(), package_name);
+}
+
+std::string create_data_ref_profile_path() {
+ return StringPrintf("%s/ref", android_profiles_dir.path);
}
std::string create_data_ref_profile_package_path(const char* package_name) {
@@ -271,9 +279,10 @@
}
int calculate_tree_size(const std::string& path, int64_t* size,
- gid_t include_gid, gid_t exclude_gid) {
+ int32_t include_gid, int32_t exclude_gid, bool exclude_apps) {
FTS *fts;
FTSENT *p;
+ int64_t matchedSize = 0;
char *argv[] = { (char*) path.c_str(), nullptr };
if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
if (errno != ENOENT) {
@@ -288,17 +297,37 @@
case FTS_F:
case FTS_SL:
case FTS_SLNONE:
- if (include_gid != 0 && p->fts_statp->st_gid != include_gid) {
+ int32_t uid = p->fts_statp->st_uid;
+ int32_t gid = p->fts_statp->st_gid;
+ int32_t user_uid = multiuser_get_app_id(uid);
+ int32_t user_gid = multiuser_get_app_id(gid);
+ if (exclude_apps && ((user_uid >= AID_APP_START && user_uid <= AID_APP_END)
+ || (user_gid >= AID_CACHE_GID_START && user_gid <= AID_CACHE_GID_END)
+ || (user_gid >= AID_SHARED_GID_START && user_gid <= AID_SHARED_GID_END))) {
+ // Don't traverse inside or measure
+ fts_set(fts, p, FTS_SKIP);
break;
}
- if (exclude_gid != 0 && p->fts_statp->st_gid == exclude_gid) {
+ if (include_gid != -1 && gid != include_gid) {
break;
}
- *size += (p->fts_statp->st_blocks * 512);
+ if (exclude_gid != -1 && gid == exclude_gid) {
+ break;
+ }
+ matchedSize += (p->fts_statp->st_blocks * 512);
break;
}
}
fts_close(fts);
+#if MEASURE_DEBUG
+ if ((include_gid == -1) && (exclude_gid == -1)) {
+ LOG(DEBUG) << "Measured " << path << " size " << matchedSize;
+ } else {
+ LOG(DEBUG) << "Measured " << path << " size " << matchedSize << "; include " << include_gid
+ << " exclude " << exclude_gid;
+ }
+#endif
+ *size += matchedSize;
return 0;
}
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index ff04118..5e396c7 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -30,6 +30,8 @@
#include <installd_constants.h>
+#define MEASURE_DEBUG 0
+
namespace android {
namespace installd {
@@ -86,13 +88,16 @@
userid_t user, const char* package_name);
std::string create_data_media_path(const char* volume_uuid, userid_t userid);
+std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
const char* data_type, const char* package_name);
std::string create_data_misc_legacy_path(userid_t userid);
-std::string create_data_user_profiles_path(userid_t userid);
+std::string create_data_user_profile_path(userid_t userid);
std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
+
+std::string create_data_ref_profile_path();
std::string create_data_ref_profile_package_path(const char* package_name);
std::string create_data_dalvik_cache_path();
@@ -103,7 +108,7 @@
std::vector<userid_t> get_known_users(const char* volume_uuid);
int calculate_tree_size(const std::string& path, int64_t* size,
- gid_t include_gid = 0, gid_t exclude_gid = 0);
+ int32_t include_gid = -1, int32_t exclude_gid = -1, bool exclude_apps = false);
int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
index 5c566fc..3b8955b 100644
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -29,7 +29,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <android/log.h>
+#include <log/log.h>
#define DIR "/data/misc/vpn/"
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index cb557aa..753aeb5 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -12,7 +12,7 @@
#include <sys/mman.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include "binder.h"
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 8f4e01b..43c4c8b 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -22,7 +22,7 @@
#define ALOGE(x...) fprintf(stderr, "svcmgr: " x)
#else
#define LOG_TAG "ServiceManager"
-#include <android/log.h>
+#include <log/log.h>
#endif
struct audit_data {
@@ -363,6 +363,7 @@
int main()
{
struct binder_state *bs;
+ union selinux_callback cb;
bs = binder_open(128*1024);
if (!bs) {
@@ -375,6 +376,11 @@
return -1;
}
+ cb.func_audit = audit_callback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+ cb.func_log = selinux_log_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+
sehandle = selinux_android_service_context_handle();
selinux_status_open(true);
@@ -388,11 +394,6 @@
abort();
}
- union selinux_callback cb;
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
- cb.func_log = selinux_log_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
binder_loop(bs, svcmgr_handler);
diff --git a/data/etc/android.hardware.telephony.carrierlock.xml b/data/etc/android.hardware.telephony.carrierlock.xml
new file mode 100644
index 0000000..50b1fe9
--- /dev/null
+++ b/data/etc/android.hardware.telephony.carrierlock.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 with telephony carrier restriction mechanism. -->
+<permissions>
+ <feature name="android.hardware.telephony.carrierlock" />
+</permissions>
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 8e10f67..c9f63ae 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -267,6 +267,36 @@
ACONFIGURATION_SCREENROUND_NO = 0x1,
ACONFIGURATION_SCREENROUND_YES = 0x2,
+ /** Wide color gamut: not specified. */
+ ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00,
+ /**
+ * Wide color gamut: value that corresponds to
+ * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
+ * nowidecg</a> resource qualifier specified.
+ */
+ ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1,
+ /**
+ * Wide color gamut: value that corresponds to
+ * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
+ * widecg</a> resource qualifier specified.
+ */
+ ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2,
+
+ /** HDR: not specified. */
+ ACONFIGURATION_HDR_ANY = 0x00,
+ /**
+ * HDR: value that corresponds to
+ * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+ * lowdr</a> resource qualifier specified.
+ */
+ ACONFIGURATION_HDR_NO = 0x1,
+ /**
+ * HDR: value that corresponds to
+ * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+ * highdr</a> resource qualifier specified.
+ */
+ ACONFIGURATION_HDR_YES = 0x2,
+
/** UI mode: not specified. */
ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
/**
@@ -300,6 +330,11 @@
* <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06,
+ /**
+ * UI mode: value that corresponds to
+ * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
+ */
+ ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07,
/** UI night mode: not specified.*/
ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
@@ -426,6 +461,12 @@
ACONFIGURATION_LAYOUTDIR = 0x4000,
ACONFIGURATION_SCREEN_ROUND = 0x8000,
/**
+ * Bit mask for
+ * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
+ * and <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
+ */
+ ACONFIGURATION_COLORIMETRY = 0x10000,
+ /**
* Constant used to to represent MNC (Mobile Network Code) zero.
* 0 cannot be used, since it is used to represent an undefined MNC.
*/
diff --git a/include/android/hardware_buffer.h b/include/android/hardware_buffer.h
new file mode 100644
index 0000000..a6c832f
--- /dev/null
+++ b/include/android/hardware_buffer.h
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file hardware_buffer.h
+ */
+
+#ifndef ANDROID_HARDWARE_BUFFER_H
+#define ANDROID_HARDWARE_BUFFER_H
+
+#include <inttypes.h>
+
+#include <sys/cdefs.h>
+
+#include <android/rect.h>
+
+__BEGIN_DECLS
+
+/**
+ * Buffer pixel formats.
+ */
+enum {
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8G8B8A8_UNORM
+ * OpenGL ES: GL_RGBA8
+ */
+ AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8G8B8A8_UNORM
+ * OpenGL ES: GL_RGBA8
+ */
+ AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8G8B8_UNORM
+ * OpenGL ES: GL_RGB8
+ */
+ AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R5G6B5_UNORM_PACK16
+ * OpenGL ES: GL_RGB565
+ */
+ AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = 4,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R16G16B16A16_SFLOAT
+ * OpenGL ES: GL_RGBA16F
+ */
+ AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT = 5,
+};
+
+enum {
+ /* The buffer will sometimes be read by the CPU */
+ AHARDWAREBUFFER_USAGE0_CPU_READ = 1ULL << 1,
+ /* The buffer will often be read by the CPU*/
+ AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN = 1ULL << 2 |
+ AHARDWAREBUFFER_USAGE0_CPU_READ,
+ /* The buffer will sometimes be written to by the CPU */
+ AHARDWAREBUFFER_USAGE0_CPU_WRITE = 1ULL << 5,
+ /* The buffer will often be written to by the CPU */
+ AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN = 1ULL << 6 |
+ AHARDWAREBUFFER_USAGE0_CPU_WRITE,
+ /* The buffer will be read from by the GPU */
+ AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE = 1ULL << 10,
+ /* The buffer will be written to by the GPU */
+ AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT = 1ULL << 11,
+ /* The buffer will be read from and written to by the GPU */
+ AHARDWAREBUFFER_USAGE0_GPU_STORAGE_IMAGE =
+ AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE |
+ AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT,
+ /* The buffer will be used as a cubemap texture */
+ AHARDWAREBUFFER_USAGE0_GPU_CUBEMAP = 1ULL << 13,
+ /* The buffer will be used as a shader storage or uniform buffer object*/
+ AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER = 1ULL << 14,
+ /* The buffer must not be used outside of a protected hardware path */
+ AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT = 1ULL << 18,
+ /** The buffer will be used for sensor direct data */
+ AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA = 1ULL << 29,
+ /* The buffer will be read by a hardware video encoder */
+ AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE = 1ULL << 21,
+};
+
+typedef struct AHardwareBuffer_Desc {
+ uint32_t width;
+ uint32_t height;
+ uint32_t layers;
+ uint64_t usage0; // Combination of AHARDWAREBUFFER_USAGE0_*
+ uint64_t usage1; // Initialize to zero, reserved for future use
+ uint32_t format; // One of AHARDWAREBUFFER_FORMAT_*
+} AHardwareBuffer_Desc;
+
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+/**
+ * Allocates a buffer that backs an AHardwareBuffer using the passed
+ * AHardwareBuffer_Desc.
+ *
+ * Returns NO_ERROR on success, or an error number of the allocation fails for
+ * any reason.
+ */
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
+ AHardwareBuffer** outBuffer);
+/**
+ * Acquire a reference on the given AHardwareBuffer object. This prevents the
+ * object from being deleted until the last reference is removed.
+ */
+void AHardwareBuffer_acquire(AHardwareBuffer* buffer);
+
+/**
+ * Remove a reference that was previously acquired with
+ * AHardwareBuffer_acquire().
+ */
+void AHardwareBuffer_release(AHardwareBuffer* buffer);
+
+/**
+ * Return a description of the AHardwareBuffer in the passed
+ * AHardwareBuffer_Desc struct.
+ */
+void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
+ AHardwareBuffer_Desc* outDesc);
+
+/*
+ * Lock the AHardwareBuffer for reading or writing, depending on the usage flags
+ * passed. This call may block if the hardware needs to finish rendering or if
+ * CPU caches need to be synchronized, or possibly for other implementation-
+ * specific reasons. If fence is not negative, then it specifies a fence file
+ * descriptor that will be signaled when the buffer is locked, otherwise the
+ * caller will block until the buffer is available.
+ *
+ * If rect is not NULL, the caller promises to modify only data in the area
+ * specified by rect. If rect is NULL, the caller may modify the contents of the
+ * entire buffer.
+ *
+ * The content of the buffer outside of the specified rect is NOT modified
+ * by this call.
+ *
+ * The buffer usage may only specify AHARDWAREBUFFER_USAGE0_CPU_*. If set, then
+ * outVirtualAddress is filled with the address of the buffer in virtual memory,
+ * otherwise this function will fail.
+ *
+ * THREADING CONSIDERATIONS:
+ *
+ * It is legal for several different threads to lock a buffer for read access;
+ * none of the threads are blocked.
+ *
+ * Locking a buffer simultaneously for write or read/write is undefined, but
+ * will neither terminate the process nor block the caller; AHardwareBuffer_lock
+ * may return an error or leave the buffer's content into an indeterminate
+ * state.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL or if the usage0
+ * flags are not a combination of AHARDWAREBUFFER_USAGE0_CPU_*, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage0,
+ int32_t fence, const ARect* rect, void** outVirtualAddress);
+
+/*
+ * Unlock the AHardwareBuffer; must be called after all changes to the buffer
+ * are completed by the caller. If fence is not NULL then it will be set to a
+ * file descriptor that is signaled when all pending work on the buffer is
+ * completed. The caller is responsible for closing the fence when it is no
+ * longer needed.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence);
+
+/*
+ * Send the AHardwareBuffer to an AF_UNIX socket.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer,
+ int socketFd);
+
+/*
+ * Receive the AHardwareBuffer from an AF_UNIX socket.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd,
+ AHardwareBuffer** outBuffer);
+
+// ----------------------------------------------------------------------------
+// Everything below here is part of the public NDK API, but is intended only
+// for use by device-specific graphics drivers.
+struct native_handle;
+const struct native_handle* AHardwareBuffer_getNativeHandle(
+ const AHardwareBuffer* buffer);
+
+__END_DECLS
+
+#endif // ANDROID_HARDWARE_BUFFER_H
diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h
new file mode 100644
index 0000000..6020870
--- /dev/null
+++ b/include/android/hardware_buffer_jni.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file hardware_buffer_jni.h
+ */
+
+#ifndef ANDROID_HARDWARE_BUFFER_JNI_H
+#define ANDROID_HARDWARE_BUFFER_JNI_H
+
+#include <sys/cdefs.h>
+
+#include <android/hardware_buffer.h>
+
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+ * Return the AHardwareBuffer associated with a Java HardwareBuffer object,
+ * for interacting with it through native code. This acquires a reference
+ * on the AHardwareBuffer that is returned; be sure to use
+ * AHardwareBuffer_release() when done with it so that it doesn't leak.
+ */
+AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env,
+ jobject hardwareBufferObj);
+
+/**
+ * Return a new Java HardwareBuffer object that wraps the passed native
+ * AHardwareBuffer object.
+ */
+jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env,
+ AHardwareBuffer* hardwareBuffer);
+
+__END_DECLS
+
+#endif // ANDROID_HARDWARE_BUFFER_JNI_H
diff --git a/include/android/native_window.h b/include/android/native_window.h
index 021dc42..7d8d657 100644
--- a/include/android/native_window.h
+++ b/include/android/native_window.h
@@ -28,6 +28,7 @@
#include <sys/cdefs.h>
+#include <android/hardware_buffer.h>
#include <android/rect.h>
#ifdef __cplusplus
@@ -35,17 +36,18 @@
#endif
/**
- * Pixel formats that a window can use.
+ * Legacy window pixel format names, kept for backwards compatibility.
+ * New code and APIs should use AHARDWAREBUFFER_FORMAT_*.
*/
enum {
// NOTE: these values must match the values from graphics/common/x.x/types.hal
/** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
- WINDOW_FORMAT_RGBA_8888 = 1,
+ WINDOW_FORMAT_RGBA_8888 = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
/** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
- WINDOW_FORMAT_RGBX_8888 = 2,
+ WINDOW_FORMAT_RGBX_8888 = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
/** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
- WINDOW_FORMAT_RGB_565 = 4,
+ WINDOW_FORMAT_RGB_565 = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
/** Red: 16 bits, Green: 16 bits, Blue: 16 bits, Alpha: 16 bits. **/
WINDOW_FORMAT_RGBA_FP16 = 0x16,
};
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index d561594..834dcbd 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -23,19 +23,19 @@
#define PLAYER_PIID_INVALID -1
-enum {
+typedef enum {
PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11,
PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12,
-};
+} player_type_t;
-enum {
+typedef enum {
PLAYER_STATE_UNKNOWN = -1,
PLAYER_STATE_RELEASED = 0,
PLAYER_STATE_IDLE = 1,
PLAYER_STATE_STARTED = 2,
PLAYER_STATE_PAUSED = 3,
PLAYER_STATE_STOPPED = 4,
-};
+} player_state_t;
}; // namespace android
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index f5ea259..ce7804b 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -108,16 +108,21 @@
PLAYER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 69,
RELEASE_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 70,
+ /*
+ DISABLE_RINGTONE_SYNC = IBinder::FIRST_CALL_TRANSACTION + 71,
+ */
};
DECLARE_META_INTERFACE(AudioManager)
// The parcels created by these methods must be kept in sync with the
// corresponding methods from IAudioService.aidl and objects it imports.
- virtual audio_unique_id_t trackPlayer(int playerType, int usage, int content) = 0;
- /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, int usage, int content)= 0;
- /*oneway*/ virtual status_t playerEvent(int piid, int event) = 0;
- /*oneway*/ virtual status_t releasePlayer(int piid) = 0;
+ virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
+ audio_content_type_t content, const sp<IBinder>& player) = 0;
+ /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
+ audio_content_type_t content)= 0;
+ /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
+ /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/audiomanager/IPlayer.h b/include/audiomanager/IPlayer.h
new file mode 100644
index 0000000..efcac74
--- /dev/null
+++ b/include/audiomanager/IPlayer.h
@@ -0,0 +1,61 @@
+/*
+ * 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_IPLAYER_H
+#define ANDROID_IPLAYER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IPlayer : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(Player);
+
+ virtual void start() = 0;
+
+ virtual void pause() = 0;
+
+ virtual void stop() = 0;
+
+ virtual void setVolume(float vol) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnPlayer : public BnInterface<IPlayer>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IPLAYER_H
diff --git a/include/binder/Status.h b/include/binder/Status.h
index dd61616..c3738f8 100644
--- a/include/binder/Status.h
+++ b/include/binder/Status.h
@@ -62,6 +62,7 @@
EX_NETWORK_MAIN_THREAD = -6,
EX_UNSUPPORTED_OPERATION = -7,
EX_SERVICE_SPECIFIC = -8,
+ EX_PARCELABLE = -9,
// This is special and Java specific; see Parcel.java.
EX_HAS_REPLY_HEADER = -128,
diff --git a/include/gfx/SafeLog.h b/include/gfx/SafeLog.h
new file mode 100644
index 0000000..e45e541
--- /dev/null
+++ b/include/gfx/SafeLog.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016 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
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#include <fmt/format.h>
+#include <log/log.h>
+#pragma clang diagnostic pop
+
+#include <type_traits>
+
+namespace android {
+namespace gfx {
+
+/* SafeLog is a mix-in that can be used to easily add typesafe logging using fmtlib to any class.
+ * To use it, inherit from it using CRTP and implement the getLogTag method.
+ *
+ * For example:
+ *
+ * class Frobnicator : private SafeLog<Frobnicator> {
+ * friend class SafeLog<Frobnicator>; // Allows getLogTag to be private
+ *
+ * public:
+ * void frobnicate(int32_t i);
+ *
+ * private:
+ * // SafeLog does member access on the object calling alog*, so this tag can theoretically vary
+ * // by instance unless getLogTag is made static
+ * const char* getLogTag() { return "Frobnicator"; }
+ * };
+ *
+ * void Frobnicator::frobnicate(int32_t i) {
+ * // Logs something like "04-16 21:35:46.811 3513 3513 I Frobnicator: frobnicating 42"
+ * alogi("frobnicating {}", i);
+ * }
+ *
+ * See http://fmtlib.net for more information on the formatting API.
+ */
+
+template <typename T>
+class SafeLog {
+ protected:
+ template <typename... Args>
+#if LOG_NDEBUG
+ void alogv(Args&&... /*unused*/) const {
+ }
+#else
+ void alogv(Args&&... args) const {
+ alog<ANDROID_LOG_VERBOSE>(std::forward<Args>(args)...);
+ }
+#endif
+
+ template <typename... Args>
+ void alogd(Args&&... args) const {
+ alog<ANDROID_LOG_DEBUG>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void alogi(Args&&... args) const {
+ alog<ANDROID_LOG_INFO>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void alogw(Args&&... args) const {
+ alog<ANDROID_LOG_WARN>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void aloge(Args&&... args) const {
+ alog<ANDROID_LOG_ERROR>(std::forward<Args>(args)...);
+ }
+
+ private:
+ // Suppresses clang-tidy check cppcoreguidelines-pro-bounds-array-to-pointer-decay
+ template <size_t strlen, typename... Args>
+ void write(fmt::MemoryWriter* writer, const char (&str)[strlen], Args&&... args) const {
+ writer->write(static_cast<const char*>(str), std::forward<Args>(args)...);
+ }
+
+ template <int priority, typename... Args>
+ void alog(Args&&... args) const {
+ static_assert(std::is_base_of<SafeLog<T>, T>::value, "Can't convert to SafeLog pointer");
+ fmt::MemoryWriter writer;
+ write(&writer, std::forward<Args>(args)...);
+ auto derivedThis = static_cast<const T*>(this);
+ android_writeLog(priority, derivedThis->getLogTag(), writer.c_str());
+ }
+};
+
+} // namespace gfx
+} // namespace android
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index e2bafec..da574ec 100644
--- a/include/gui/BufferQueueConsumer.h
+++ b/include/gui/BufferQueueConsumer.h
@@ -22,6 +22,7 @@
#include <gui/BufferQueueDefs.h>
#include <gui/IGraphicBufferConsumer.h>
+#include <utils/String8.h>
namespace android {
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
index 8a3fa39..46ca2c2 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -44,6 +44,7 @@
GL_COMPOSITION_DONE,
DISPLAY_PRESENT,
DISPLAY_RETIRE,
+ DEQUEUE_READY,
RELEASE,
EVENT_COUNT, // Not an actual event.
};
@@ -61,6 +62,7 @@
bool hasDisplayPresentInfo() const;
bool hasDisplayRetireInfo() const;
bool hasReleaseInfo() const;
+ bool hasDequeueReadyInfo() const;
void checkFencesForCompletion();
void dump(String8& outString) const;
@@ -84,6 +86,7 @@
nsecs_t latchTime{-1};
nsecs_t firstRefreshStartTime{-1};
nsecs_t lastRefreshStartTime{-1};
+ nsecs_t dequeueReadyTime{-1};
std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
@@ -116,13 +119,22 @@
public:
~ProducerFrameEventHistory() override;
- void updateAcquireFence(
+ // virtual for testing.
+ virtual void updateAcquireFence(
uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire);
void applyDelta(const FrameEventHistoryDelta& delta);
void updateSignalTimes();
-private:
+protected:
+ void applyFenceDelta(FenceTimeline* timeline,
+ std::shared_ptr<FenceTime>* dst,
+ const FenceTime::Snapshot& src) const;
+
+ // virtual for testing.
+ virtual std::shared_ptr<FenceTime> createFenceTime(
+ const sp<Fence>& fence) const;
+
size_t mAcquireOffset{0};
// The consumer updates it's timelines in Layer and SurfaceFlinger since
@@ -185,7 +197,7 @@
const std::shared_ptr<FenceTime>& displayPresent);
void addRetire(uint64_t frameNumber,
const std::shared_ptr<FenceTime>& displayRetire);
- void addRelease(uint64_t frameNumber,
+ void addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime,
std::shared_ptr<FenceTime>&& release);
void getAndResetDelta(FrameEventHistoryDelta* delta);
@@ -246,6 +258,7 @@
nsecs_t mLatchTime{0};
nsecs_t mFirstRefreshStartTime{0};
nsecs_t mLastRefreshStartTime{0};
+ nsecs_t mDequeueReadyTime{0};
FenceTime::Snapshot mGpuCompositionDoneFence;
FenceTime::Snapshot mDisplayPresentFence;
diff --git a/include/gui/GraphicsEnv.h b/include/gui/GraphicsEnv.h
new file mode 100644
index 0000000..4c7366f
--- /dev/null
+++ b/include/gui/GraphicsEnv.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_GUI_GRAPHICS_ENV_H
+#define ANDROID_GUI_GRAPHICS_ENV_H 1
+
+#include <string>
+
+struct android_namespace_t;
+
+namespace android {
+
+class GraphicsEnv {
+public:
+ static GraphicsEnv& getInstance();
+
+ // Set a search path for loading graphics drivers. The path is a list of
+ // directories separated by ':'. A directory can be contained in a zip file
+ // (drivers must be stored uncompressed and page aligned); such elements
+ // in the search path must have a '!' after the zip filename, e.g.
+ // /data/app/com.example.driver/base.apk!/lib/arm64-v8a
+ void setDriverPath(const std::string path);
+ android_namespace_t* getDriverNamespace();
+
+private:
+ GraphicsEnv() = default;
+ std::string mDriverPath;
+ android_namespace_t* mDriverNamespace = nullptr;
+};
+
+} // namespace android
+
+/* FIXME
+ * Export an un-mangled function that just does
+ * return android::GraphicsEnv::getInstance().getDriverNamespace();
+ * This allows libEGL to get the function pointer via dlsym, since it can't
+ * directly link against libgui. In a future release, we'll fix this so that
+ * libgui does not depend on graphics API libraries, and libEGL can link
+ * against it. The current dependencies from libgui -> libEGL are:
+ * - the GLConsumer class, which should be moved to its own library
+ * - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and
+ * will be removed soon.
+ */
+extern "C" android_namespace_t* android_getDriverNamespace();
+
+#endif // ANDROID_GUI_GRAPHICS_ENV_H
diff --git a/include/gui/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h
index 857444b..2ccd832 100644
--- a/include/gui/ISensorEventConnection.h
+++ b/include/gui/ISensorEventConnection.h
@@ -40,6 +40,7 @@
nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0;
virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
virtual status_t flush() = 0;
+ virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h
index 737c430..0c36c99 100644
--- a/include/gui/ISensorServer.h
+++ b/include/gui/ISensorServer.h
@@ -25,6 +25,8 @@
#include <binder/IInterface.h>
+struct native_handle;
+typedef struct native_handle native_handle_t;
namespace android {
// ----------------------------------------------------------------------------
@@ -43,6 +45,9 @@
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int mode, const String16& opPackageName) = 0;
virtual int32_t isDataInjectionEnabled() = 0;
+
+ virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+ uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
index 7506835..d886b2b 100644
--- a/include/gui/Sensor.h
+++ b/include/gui/Sensor.h
@@ -91,6 +91,8 @@
bool isWakeUpSensor() const;
bool isDynamicSensor() const;
bool hasAdditionalInfo() const;
+ int32_t getHighestDirectReportRateLevel() const;
+ bool isDirectChannelTypeSupported(int32_t sharedMemType) const;
int32_t getReportingMode() const;
// Note that after setId() has been called, getUuid() no longer
diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h
index 6c6230f..5b34ff4 100644
--- a/include/gui/SensorManager.h
+++ b/include/gui/SensorManager.h
@@ -34,10 +34,15 @@
#include <gui/SensorEventQueue.h>
+#include <unordered_map>
+
// ----------------------------------------------------------------------------
// Concrete types for the NDK
struct ASensorManager { };
+struct native_handle;
+typedef struct native_handle native_handle_t;
+
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -59,6 +64,9 @@
Sensor const* getDefaultSensor(int type);
sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
bool isDataInjectionEnabled();
+ int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
+ void destroyDirectChannel(int channelNativeHandle);
+ int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel);
private:
// DeathRecipient interface
@@ -77,6 +85,8 @@
Vector<Sensor> mSensors;
sp<IBinder::DeathRecipient> mDeathObserver;
const String16 mOpPackageName;
+ std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
+ int32_t mDirectConnectionHandle;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index a10dad1..0b6a8f7 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -33,6 +33,8 @@
namespace android {
+class ISurfaceComposer;
+
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* BufferQueue.
@@ -66,7 +68,8 @@
* the controlledByApp flag indicates that this Surface (producer) is
* controlled by the application. This flag is used at connect time.
*/
- explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false);
+ explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer,
+ bool controlledByApp = false);
/* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
* Surface was created with. Usually it's an error to use the
@@ -143,15 +146,19 @@
// See IGraphicBufferProducer::getFrameTimestamps
status_t getFrameTimestamps(uint64_t frameNumber,
nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
- nsecs_t* outRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+ nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
- nsecs_t* outReleaseTime);
+ nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime);
status_t getUniqueId(uint64_t* outId) const;
protected:
virtual ~Surface();
+ // Virtual for testing.
+ virtual sp<ISurfaceComposer> composerService() const;
+
private:
// can't be copied
Surface& operator = (const Surface& rhs);
@@ -245,7 +252,6 @@
enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
-private:
void querySupportedTimestampsLocked() const;
void freeAllBuffers();
@@ -389,7 +395,8 @@
Condition mQueueBufferCondition;
- uint64_t mNextFrameNumber;
+ uint64_t mNextFrameNumber = 1;
+ uint64_t mLastFrameNumber = 0;
// Mutable because ANativeWindow::query needs this class const.
mutable bool mQueriedSupportedTimestamps;
@@ -398,7 +405,7 @@
// A cached copy of the FrameEventHistory maintained by the consumer.
bool mEnableFrameTimestamps = false;
- ProducerFrameEventHistory mFrameEventHistory;
+ std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory;
};
namespace view {
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index 9352c57..b86c72c 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -214,7 +214,13 @@
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ,
bool useIdentityTransform);
-
+ static status_t captureToBuffer(
+ const sp<IBinder>& display,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool useIdentityTransform,
+ uint32_t rotation,
+ sp<GraphicBuffer>* outbuffer);
private:
mutable sp<CpuConsumer> mCpuConsumer;
mutable sp<IGraphicBufferProducer> mProducer;
diff --git a/include/media/hardware/VideoAPI.h b/include/media/hardware/VideoAPI.h
index 3667c4b..a090876 100644
--- a/include/media/hardware/VideoAPI.h
+++ b/include/media/hardware/VideoAPI.h
@@ -110,7 +110,7 @@
// though could verify that nSize is at least the size of the structure at the
// time of implementation. All new fields will be added at the end of the structure
// ensuring backward compatibility.
-struct __attribute__ ((__packed__)) ColorAspects {
+struct __attribute__ ((__packed__, aligned(alignof(uint32_t)))) ColorAspects {
// this is in sync with the range values in graphics.h
enum Range : uint32_t {
RangeUnspecified,
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index 99b39d8..58df24c 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -18,21 +18,17 @@
#define ANDROID_FENCE_H
#include <stdint.h>
-#include <sys/types.h>
-#include <ui/ANativeObjectBase.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
#include <utils/Flattenable.h>
-#include <utils/String8.h>
+#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <experimental/optional>
-struct ANativeWindowBuffer;
-
namespace android {
+class String8;
+
// ===========================================================================
// Fence
// ===========================================================================
@@ -62,6 +58,12 @@
// closed.
explicit Fence(int fenceFd);
+ // Not copyable or movable.
+ Fence(const Fence& rhs) = delete;
+ Fence& operator=(const Fence& rhs) = delete;
+ Fence(Fence&& rhs) = delete;
+ Fence& operator=(Fence&& rhs) = delete;
+
// Check whether the Fence has an open fence file descriptor. Most Fence
// methods treat an invalid file descriptor just like a valid fence that
// is already signalled, so using this is usually not necessary.
@@ -135,11 +137,6 @@
friend class LightRefBase<Fence>;
~Fence();
- // Disallow copying
- Fence(const Fence& rhs);
- Fence& operator = (const Fence& rhs);
- const Fence& operator = (const Fence& rhs) const;
-
int mFenceFd;
};
diff --git a/include/ui/FenceTime.h b/include/ui/FenceTime.h
index 27cc720..871fcf2 100644
--- a/include/ui/FenceTime.h
+++ b/include/ui/FenceTime.h
@@ -24,13 +24,17 @@
#include <atomic>
#include <mutex>
#include <queue>
+#include <unordered_map>
namespace android {
+class FenceToFenceTimeMap;
+
// A wrapper around fence that only implements isValid and getSignalTime.
// It automatically closes the fence in a thread-safe manner once the signal
// time is known.
class FenceTime {
+friend class FenceToFenceTimeMap;
public:
// An atomic snapshot of the FenceTime that is flattenable.
//
@@ -107,15 +111,22 @@
// Returns a snapshot of the FenceTime in its current state.
Snapshot getSnapshot() const;
+ void signalForTest(nsecs_t signalTime);
+
// Override new and delete since this needs 8-byte alignment, which
// is not guaranteed on x86.
static void* operator new(size_t nbytes) noexcept;
static void operator delete(void *p);
private:
+ // For tests only. If forceValidForTest is true, then getSignalTime will
+ // never return SIGNAL_TIME_INVALID and isValid will always return true.
+ FenceTime(const sp<Fence>& fence, bool forceValidForTest);
+
enum class State {
VALID,
INVALID,
+ FORCED_VALID_FOR_TEST,
};
const State mState{State::INVALID};
@@ -156,6 +167,42 @@
std::queue<std::weak_ptr<FenceTime>> mQueue;
};
+// Used by test code to create or get FenceTimes for a given Fence.
+//
+// By design, Fences cannot be signaled from user space. However, this class
+// allows test code to set the apparent signalTime of a Fence and
+// have it be visible to all FenceTimes. Release code should not use
+// FenceToFenceTimeMap.
+//
+// FenceToFenceTimeMap keeps a weak reference to the FenceTime and automatically
+// garbage collects entries every time a new FenceTime is created to avoid
+// leaks. This prevents us from having to make the Fence destructor
+// automatically notify that the underlying fence has been destroyed, which
+// would affect release code paths. Garbage collecting so often is inefficient,
+// but acceptable for testing.
+//
+// Since FenceTimes maintain a strong reference to underlying Fences, there
+// should not be any aliasing issues where a new Fence happens to have the same
+// address as a previous Fence; the previous entry will be garbage collected
+// before the new one is added.
+class FenceToFenceTimeMap {
+public:
+ // Create a new FenceTime with that wraps the provided Fence.
+ std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence);
+
+ // Signals all FenceTimes created through this class that are wrappers
+ // around |fence|.
+ void signalAllForTest(const sp<Fence>& fence, nsecs_t signalTime);
+
+private:
+ // Cleans up the entries that no longer have a strong reference.
+ void garbageCollectLocked();
+
+ mutable std::mutex mMutex;
+ std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap;
+};
+
+
}; // namespace android
#endif // ANDROID_FENCE_TIME_H
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index 8aadd58..ce33d4e 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -21,9 +21,10 @@
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
+#include <log/log.h>
+
#include <ui/Point.h>
-#include <android/log.h>
#include <android/rect.h>
namespace android {
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 45633f3..f4e0a60 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -25,9 +25,9 @@
#include <sys/mman.h>
#include <unistd.h>
-#include <android/log.h>
#include <binder/IMemory.h>
#include <binder/Parcel.h>
+#include <log/log.h>
#include <utils/CallStack.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 415c252..03f00be 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -25,11 +25,10 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
+#include <binder/MemoryHeapBase.h>
#include <cutils/ashmem.h>
#include <cutils/atomic.h>
-
-#include <binder/MemoryHeapBase.h>
+#include <log/log.h>
namespace android {
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index 70d425e..e7078ba 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -20,9 +20,9 @@
#include <limits>
-#include <android/log.h>
#include <binder/IBinder.h>
#include <binder/Parcel.h>
+#include <log/log.h>
#include <utils/Errors.h>
using android::BAD_TYPE;
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index 8466863..006f7f9 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -104,6 +104,16 @@
if (mException == EX_SERVICE_SPECIFIC) {
status = parcel.readInt32(&mErrorCode);
+ } else if (mException == EX_PARCELABLE) {
+ // Skip over the blob of Parcelable data
+ const int32_t header_start = parcel.dataPosition();
+ int32_t header_size;
+ status = parcel.readInt32(&header_size);
+ if (status != OK) {
+ setFromStatusT(status);
+ return status;
+ }
+ parcel.setDataPosition(header_start + header_size);
}
if (status != OK) {
setFromStatusT(status);
@@ -127,11 +137,12 @@
return status;
}
status = parcel->writeString16(String16(mMessage));
- if (mException != EX_SERVICE_SPECIFIC) {
- // We have no more information to write.
- return status;
+ if (mException == EX_SERVICE_SPECIFIC) {
+ status = parcel->writeInt32(mErrorCode);
+ } else if (mException == EX_PARCELABLE) {
+ // Sending Parcelable blobs currently not supported
+ status = parcel->writeInt32(0);
}
- status = parcel->writeInt32(mErrorCode);
return status;
}
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index cb17da4..3815bdc 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -77,6 +77,7 @@
"FrameTimestamps.cpp",
"GLConsumer.cpp",
"GraphicBufferAlloc.cpp",
+ "GraphicsEnv.cpp",
"GuiConfig.cpp",
"IDisplayEventConnection.cpp",
"IGraphicBufferAlloc.cpp",
@@ -99,6 +100,7 @@
],
shared_libs: [
+ "libnativeloader",
"libsync",
"libbinder",
"libcutils",
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index c9b8948..73537bf 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -18,6 +18,7 @@
#include <cutils/compiler.h> // For CC_[UN]LIKELY
#include <inttypes.h>
+#include <utils/Log.h>
#include <utils/String8.h>
#include <algorithm>
@@ -54,6 +55,10 @@
return addRetireCalled || addReleaseCalled;
}
+bool FrameEvents::hasDequeueReadyInfo() const {
+ return Fence::isValidTimestamp(dequeueReadyTime);
+}
+
bool FrameEvents::hasAcquireInfo() const {
return acquireFence->isValid();
}
@@ -140,6 +145,14 @@
!addPostCompositeCalled, *displayPresentFence);
dumpFenceTime(outString, "Display Retire \t",
!addRetireCalled, *displayRetireFence);
+
+ outString.appendFormat("--- DequeueReady \t");
+ if (Fence::isValidTimestamp(dequeueReadyTime)) {
+ outString.appendFormat("%" PRId64 "\n", dequeueReadyTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
dumpFenceTime(outString, "Release \t",
true, *releaseFence);
}
@@ -241,33 +254,6 @@
}
}
-static void applyFenceDelta(FenceTimeline* timeline,
- std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) {
- if (CC_UNLIKELY(dst == nullptr)) {
- ALOGE("applyFenceDelta: dst is null.");
- return;
- }
-
- switch (src.state) {
- case FenceTime::Snapshot::State::EMPTY:
- return;
- case FenceTime::Snapshot::State::FENCE:
- if (CC_UNLIKELY((*dst)->isValid())) {
- ALOGE("applyFenceDelta: Unexpected fence.");
- }
- *dst = std::make_shared<FenceTime>(src.fence);
- timeline->push(*dst);
- return;
- case FenceTime::Snapshot::State::SIGNAL_TIME:
- if ((*dst)->isValid()) {
- (*dst)->applyTrustedSnapshot(src);
- } else {
- *dst = std::make_shared<FenceTime>(src.signalTime);
- }
- return;
- }
-}
-
void ProducerFrameEventHistory::applyDelta(
const FrameEventHistoryDelta& delta) {
for (auto& d : delta.mDeltas) {
@@ -288,6 +274,7 @@
frame.latchTime = d.mLatchTime;
frame.firstRefreshStartTime = d.mFirstRefreshStartTime;
frame.lastRefreshStartTime = d.mLastRefreshStartTime;
+ frame.dequeueReadyTime = d.mDequeueReadyTime;
if (frame.frameNumber != d.mFrameNumber) {
// We got a new frame. Initialize some of the fields.
@@ -320,6 +307,38 @@
mReleaseTimeline.updateSignalTimes();
}
+void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline,
+ std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) const {
+ if (CC_UNLIKELY(dst == nullptr)) {
+ ALOGE("applyFenceDelta: dst is null.");
+ return;
+ }
+
+ switch (src.state) {
+ case FenceTime::Snapshot::State::EMPTY:
+ return;
+ case FenceTime::Snapshot::State::FENCE:
+ if (CC_UNLIKELY((*dst)->isValid())) {
+ ALOGE("applyFenceDelta: Unexpected fence.");
+ }
+ *dst = createFenceTime(src.fence);
+ timeline->push(*dst);
+ return;
+ case FenceTime::Snapshot::State::SIGNAL_TIME:
+ if ((*dst)->isValid()) {
+ (*dst)->applyTrustedSnapshot(src);
+ } else {
+ *dst = std::make_shared<FenceTime>(src.signalTime);
+ }
+ return;
+ }
+}
+
+std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime(
+ const sp<Fence>& fence) const {
+ return std::make_shared<FenceTime>(fence);
+}
+
// ============================================================================
// ConsumerFrameEventHistory
@@ -405,14 +424,15 @@
mFramesDirty[mRetireOffset].setDirty<FrameEvent::DISPLAY_RETIRE>();
}
-void ConsumerFrameEventHistory::addRelease(
- uint64_t frameNumber, std::shared_ptr<FenceTime>&& release) {
+void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber,
+ nsecs_t dequeueReadyTime, std::shared_ptr<FenceTime>&& release) {
FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
if (frame == nullptr) {
ALOGE("ConsumerFrameEventHistory::addRelease: Did not find frame.");
return;
}
frame->addReleaseCalled = true;
+ frame->dequeueReadyTime = dequeueReadyTime;
frame->releaseFence = std::move(release);
mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
}
@@ -461,7 +481,8 @@
mRequestedPresentTime(frameTimestamps.requestedPresentTime),
mLatchTime(frameTimestamps.latchTime),
mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime),
- mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime) {
+ mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime),
+ mDequeueReadyTime(frameTimestamps.dequeueReadyTime) {
if (dirtyFields.isDirty<FrameEvent::GL_COMPOSITION_DONE>()) {
mGpuCompositionDoneFence =
frameTimestamps.gpuCompositionDoneFence->getSnapshot();
@@ -489,7 +510,8 @@
sizeof(FrameEventsDelta::mRequestedPresentTime) +
sizeof(FrameEventsDelta::mLatchTime) +
sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
- sizeof(FrameEventsDelta::mLastRefreshStartTime);
+ sizeof(FrameEventsDelta::mLastRefreshStartTime) +
+ sizeof(FrameEventsDelta::mDequeueReadyTime);
return min;
}
@@ -538,6 +560,7 @@
FlattenableUtils::write(buffer, size, mLatchTime);
FlattenableUtils::write(buffer, size, mFirstRefreshStartTime);
FlattenableUtils::write(buffer, size, mLastRefreshStartTime);
+ FlattenableUtils::write(buffer, size, mDequeueReadyTime);
// Fences
for (auto fence : allFences(this)) {
@@ -576,6 +599,7 @@
FlattenableUtils::read(buffer, size, mLatchTime);
FlattenableUtils::read(buffer, size, mFirstRefreshStartTime);
FlattenableUtils::read(buffer, size, mLastRefreshStartTime);
+ FlattenableUtils::read(buffer, size, mDequeueReadyTime);
// Fences
for (auto fence : allFences(this)) {
diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp
index 7d91d33..30f5e53 100644
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ b/libs/gui/GraphicBufferAlloc.cpp
@@ -15,7 +15,7 @@
** limitations under the License.
*/
-#include <android/log.h>
+#include <log/log.h>
#include <ui/GraphicBuffer.h>
diff --git a/libs/gui/GraphicsEnv.cpp b/libs/gui/GraphicsEnv.cpp
new file mode 100644
index 0000000..68f0f98
--- /dev/null
+++ b/libs/gui/GraphicsEnv.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 1
+#define LOG_TAG "GraphicsEnv"
+#include <gui/GraphicsEnv.h>
+
+#include <mutex>
+
+#include <log/log.h>
+#include <nativeloader/dlext_namespaces.h>
+
+namespace android {
+
+/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
+ static GraphicsEnv env;
+ return env;
+}
+
+void GraphicsEnv::setDriverPath(const std::string path) {
+ if (!mDriverPath.empty()) {
+ ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
+ mDriverPath.c_str(), path.c_str());
+ return;
+ }
+ ALOGV("setting driver path to '%s'", path.c_str());
+ mDriverPath = path;
+}
+
+android_namespace_t* GraphicsEnv::getDriverNamespace() {
+ static std::once_flag once;
+ std::call_once(once, [this]() {
+ // TODO; In the next version of Android, all graphics drivers will be
+ // loaded into a custom namespace. To minimize risk for this release,
+ // only updated drivers use a custom namespace.
+ //
+ // Additionally, the custom namespace will be
+ // ANDROID_NAMESPACE_TYPE_ISOLATED, and will only have access to a
+ // subset of the system.
+ if (mDriverPath.empty())
+ return;
+
+ char defaultPath[PATH_MAX];
+ android_get_LD_LIBRARY_PATH(defaultPath, sizeof(defaultPath));
+ size_t defaultPathLen = strlen(defaultPath);
+
+ std::string path;
+ path.reserve(mDriverPath.size() + 1 + defaultPathLen);
+ path.append(mDriverPath);
+ path.push_back(':');
+ path.append(defaultPath, defaultPathLen);
+
+ mDriverNamespace = android_create_namespace(
+ "gfx driver",
+ nullptr, // ld_library_path
+ path.c_str(), // default_library_path
+ ANDROID_NAMESPACE_TYPE_SHARED,
+ nullptr, // permitted_when_isolated_path
+ nullptr); // parent
+ });
+ return mDriverNamespace;
+}
+
+} // namespace android
+
+extern "C" android_namespace_t* android_getDriverNamespace() {
+ return android::GraphicsEnv::getInstance().getDriverNamespace();
+}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 2401464..ef770e8 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -19,6 +19,7 @@
#include <utils/Errors.h>
#include <utils/NativeHandle.h>
+#include <utils/String8.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index e37b65b..18a80e8 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -20,6 +20,7 @@
#include <utils/Errors.h>
#include <utils/NativeHandle.h>
#include <utils/RefBase.h>
+#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp
index 59ecee7..8af51c5 100644
--- a/libs/gui/ISensorEventConnection.cpp
+++ b/libs/gui/ISensorEventConnection.cpp
@@ -34,7 +34,8 @@
GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
ENABLE_DISABLE,
SET_EVENT_RATE,
- FLUSH_SENSOR
+ FLUSH_SENSOR,
+ CONFIGURE_CHANNEL
};
class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
@@ -85,6 +86,15 @@
remote()->transact(FLUSH_SENSOR, data, &reply);
return reply.readInt32();
}
+
+ virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+ data.writeInt32(handle);
+ data.writeInt32(rateLevel);
+ remote()->transact(CONFIGURE_CHANNEL, data, &reply);
+ return reply.readInt32();
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -131,6 +141,15 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case CONFIGURE_CHANNEL: {
+ CHECK_INTERFACE(ISensorEventConnection, data, reply);
+ int handle = data.readInt32();
+ int rateLevel = data.readInt32();
+ status_t result = configureChannel(handle, rateLevel);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp
index 07c507a..aea7403 100644
--- a/libs/gui/ISensorServer.cpp
+++ b/libs/gui/ISensorServer.cpp
@@ -17,6 +17,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <cutils/native_handle.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
@@ -37,6 +38,7 @@
CREATE_SENSOR_EVENT_CONNECTION,
ENABLE_DATA_INJECTION,
GET_DYNAMIC_SENSOR_LIST,
+ CREATE_SENSOR_DIRECT_CONNECTION,
};
class BpSensorServer : public BpInterface<ISensorServer>
@@ -101,6 +103,19 @@
remote()->transact(ENABLE_DATA_INJECTION, data, &reply);
return reply.readInt32();
}
+
+ virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+ uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ data.writeString16(opPackageName);
+ data.writeUint32(size);
+ data.writeInt32(type);
+ data.writeInt32(format);
+ data.writeNativeHandle(resource);
+ remote()->transact(CREATE_SENSOR_DIRECT_CONNECTION, data, &reply);
+ return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -153,6 +168,20 @@
}
return NO_ERROR;
}
+ case CREATE_SENSOR_DIRECT_CONNECTION: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ const String16& opPackageName = data.readString16();
+ uint32_t size = data.readUint32();
+ int32_t type = data.readInt32();
+ int32_t format = data.readInt32();
+ native_handle_t *resource = data.readNativeHandle();
+ sp<ISensorEventConnection> ch =
+ createSensorDirectConnection(opPackageName, size, type, format, resource);
+ native_handle_close(resource);
+ native_handle_delete(resource);
+ reply->writeStrongBinder(IInterface::asBinder(ch));
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index dbf03a5..2fd29d5 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -19,11 +19,11 @@
#include <sys/limits.h>
#include <sys/types.h>
-#include <android/log.h>
#include <binder/AppOpsManager.h>
#include <binder/IServiceManager.h>
#include <gui/Sensor.h>
#include <hardware/sensors.h>
+#include <log/log.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Flattenable.h>
@@ -219,7 +219,10 @@
break;
case SENSOR_TYPE_DYNAMIC_SENSOR_META:
mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
- mFlags = SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger and non-wake up
+ mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger
+ if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+ mFlags |= SENSOR_FLAG_WAKE_UP;
+ }
break;
case SENSOR_TYPE_POSE_6DOF:
mStringType = SENSOR_STRING_TYPE_POSE_6DOF;
@@ -243,6 +246,14 @@
mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
break;
+
+ // TODO: Placeholder for LLOB sensor type
+
+
+ case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED:
+ mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
+ mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+ break;
default:
// Only pipe the stringType, requiredPermission and flags for custom sensors.
if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
@@ -289,7 +300,15 @@
// Feature flags
// Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3.
if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= (hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK));
+ mFlags |= hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK);
+ }
+ // Set DIRECT_REPORT_MASK and DIRECT_CHANNEL_MASK flags. Compatible with HAL 1_3.
+ if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+ // only on continuous sensors direct report mode is defined
+ if ((mFlags & REPORTING_MODE_MASK) == SENSOR_FLAG_CONTINUOUS_MODE) {
+ mFlags |= hwSensor.flags
+ & (SENSOR_FLAG_MASK_DIRECT_REPORT | SENSOR_FLAG_MASK_DIRECT_CHANNEL);
+ }
}
// Set DATA_INJECTION flag here. Defined in HAL 1_4.
if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) {
@@ -399,6 +418,21 @@
return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0;
}
+int32_t Sensor::getHighestDirectReportRateLevel() const {
+ return ((mFlags & SENSOR_FLAG_MASK_DIRECT_REPORT) >> SENSOR_FLAG_SHIFT_DIRECT_REPORT);
+}
+
+bool Sensor::isDirectChannelTypeSupported(int32_t sharedMemType) const {
+ switch (sharedMemType) {
+ case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+ return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM;
+ case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+ return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC;
+ default:
+ return false;
+ }
+}
+
int32_t Sensor::getReportingMode() const {
return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
}
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
index 57c3073..46eaf28 100644
--- a/libs/gui/SensorManager.cpp
+++ b/libs/gui/SensorManager.cpp
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <cutils/native_handle.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
@@ -89,7 +90,7 @@
}
SensorManager::SensorManager(const String16& opPackageName)
- : mSensorList(0), mOpPackageName(opPackageName) {
+ : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
// okay we're not locked here, but it's not needed during construction
assertStateLocked();
}
@@ -237,5 +238,62 @@
return false;
}
+int SensorManager::createDirectChannel(
+ size_t size, int channelType, const native_handle_t *resourceHandle) {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() != NO_ERROR) {
+ return NO_INIT;
+ }
+
+ switch (channelType) {
+ case SENSOR_DIRECT_MEM_TYPE_ASHMEM: {
+ sp<ISensorEventConnection> conn =
+ mSensorServer->createSensorDirectConnection(mOpPackageName,
+ static_cast<uint32_t>(size),
+ static_cast<int32_t>(channelType),
+ SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle);
+ if (conn == nullptr) {
+ return NO_MEMORY;
+ }
+ int nativeHandle = mDirectConnectionHandle++;
+ mDirectConnection.emplace(nativeHandle, conn);
+ return nativeHandle;
+ }
+ case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+ LOG_FATAL("%s: Finish implementation of ION and GRALLOC or remove", __FUNCTION__);
+ return BAD_VALUE;
+ default:
+ ALOGE("Bad channel shared memory type %d", channelType);
+ return BAD_VALUE;
+ }
+}
+
+void SensorManager::destroyDirectChannel(int channelNativeHandle) {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() == NO_ERROR) {
+ mDirectConnection.erase(channelNativeHandle);
+ }
+}
+
+int SensorManager::configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel) {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() != NO_ERROR) {
+ return NO_INIT;
+ }
+
+ auto i = mDirectConnection.find(channelNativeHandle);
+ if (i == mDirectConnection.end()) {
+ ALOGE("Cannot find the handle in client direct connection table");
+ return BAD_VALUE;
+ }
+
+ int ret;
+ ret = i->second->configureChannel(sensorHandle, rateLevel);
+ ALOGE_IF(ret < 0, "SensorManager::configureChannel (%d, %d) returns %d",
+ static_cast<int>(sensorHandle), static_cast<int>(rateLevel),
+ static_cast<int>(ret));
+ return ret;
+}
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 1e79e06..c859828 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -49,11 +49,11 @@
mAutoRefresh(false),
mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
mSharedBufferHasBeenQueued(false),
- mNextFrameNumber(1),
mQueriedSupportedTimestamps(false),
mFrameTimestampsSupportsPresent(false),
mFrameTimestampsSupportsRetire(false),
- mEnableFrameTimestamps(false)
+ mEnableFrameTimestamps(false),
+ mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>())
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
@@ -97,6 +97,10 @@
}
}
+sp<ISurfaceComposer> Surface::composerService() const {
+ return ComposerService::getComposerService();
+}
+
sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
return mGraphicBufferProducer;
}
@@ -144,6 +148,43 @@
mEnableFrameTimestamps = enable;
}
+static bool checkConsumerForUpdates(
+ const FrameEvents* e, const uint64_t lastFrameNumber,
+ const nsecs_t* outLatchTime,
+ const nsecs_t* outFirstRefreshStartTime,
+ const nsecs_t* outLastRefreshStartTime,
+ const nsecs_t* outGlCompositionDoneTime,
+ const nsecs_t* outDisplayPresentTime,
+ const nsecs_t* outDisplayRetireTime,
+ const nsecs_t* outDequeueReadyTime,
+ const nsecs_t* outReleaseTime) {
+ bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo();
+ bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) &&
+ !e->hasFirstRefreshStartInfo();
+ bool checkForGlCompositionDone = (outGlCompositionDoneTime != nullptr) &&
+ !e->hasGpuCompositionDoneInfo();
+ bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) &&
+ !e->hasDisplayPresentInfo();
+
+ // LastRefreshStart, DisplayRetire, DequeueReady, and Release are never
+ // available for the last frame.
+ bool checkForLastRefreshStart = (outLastRefreshStartTime != nullptr) &&
+ !e->hasLastRefreshStartInfo() &&
+ (e->frameNumber != lastFrameNumber);
+ bool checkForDisplayRetire = (outDisplayRetireTime != nullptr) &&
+ !e->hasDisplayRetireInfo() && (e->frameNumber != lastFrameNumber);
+ bool checkForDequeueReady = (outDequeueReadyTime != nullptr) &&
+ !e->hasDequeueReadyInfo() && (e->frameNumber != lastFrameNumber);
+ bool checkForRelease = (outReleaseTime != nullptr) &&
+ !e->hasReleaseInfo() && (e->frameNumber != lastFrameNumber);
+
+ // RequestedPresent and Acquire info are always available producer-side.
+ return checkForLatch || checkForFirstRefreshStart ||
+ checkForLastRefreshStart || checkForGlCompositionDone ||
+ checkForDisplayPresent || checkForDisplayRetire ||
+ checkForDequeueReady || checkForRelease;
+}
+
static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) {
if (dst != nullptr) {
*dst = Fence::isValidTimestamp(src) ? src : 0;
@@ -159,9 +200,10 @@
status_t Surface::getFrameTimestamps(uint64_t frameNumber,
nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
- nsecs_t* outRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+ nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
- nsecs_t* outReleaseTime) {
+ nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime) {
ATRACE_CALL();
Mutex::Autolock lock(mMutex);
@@ -179,30 +221,35 @@
return BAD_VALUE;
}
- FrameEvents* events = mFrameEventHistory.getFrame(frameNumber);
-
- // Update our cache of events if the requested events are not available.
- if (events == nullptr ||
- (outRequestedPresentTime && !events->hasRequestedPresentInfo()) ||
- (outAcquireTime && !events->hasAcquireInfo()) ||
- (outRefreshStartTime && !events->hasFirstRefreshStartInfo()) ||
- (outGlCompositionDoneTime && !events->hasGpuCompositionDoneInfo()) ||
- (outDisplayPresentTime && !events->hasDisplayPresentInfo()) ||
- (outDisplayRetireTime && !events->hasDisplayRetireInfo()) ||
- (outReleaseTime && !events->hasReleaseInfo())) {
- FrameEventHistoryDelta delta;
- mGraphicBufferProducer->getFrameTimestamps(&delta);
- mFrameEventHistory.applyDelta(delta);
- events = mFrameEventHistory.getFrame(frameNumber);
+ FrameEvents* events = mFrameEventHistory->getFrame(frameNumber);
+ if (events == nullptr) {
+ // If the entry isn't available in the producer, it's definitely not
+ // available in the consumer.
+ return NAME_NOT_FOUND;
}
- // A record for the requested frame does not exist.
+ // Update our cache of events if the requested events are not available.
+ if (checkConsumerForUpdates(events, mLastFrameNumber,
+ outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGlCompositionDoneTime, outDisplayPresentTime,
+ outDisplayRetireTime, outDequeueReadyTime, outReleaseTime)) {
+ FrameEventHistoryDelta delta;
+ mGraphicBufferProducer->getFrameTimestamps(&delta);
+ mFrameEventHistory->applyDelta(delta);
+ events = mFrameEventHistory->getFrame(frameNumber);
+ }
+
if (events == nullptr) {
+ // The entry was available before the update, but was overwritten
+ // after the update. Make sure not to send the wrong frame's data.
return NAME_NOT_FOUND;
}
getFrameTimestamp(outRequestedPresentTime, events->requestedPresentTime);
- getFrameTimestamp(outRefreshStartTime, events->firstRefreshStartTime);
+ getFrameTimestamp(outLatchTime, events->latchTime);
+ getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime);
+ getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime);
+ getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime);
getFrameTimestampFence(outAcquireTime, events->acquireFence);
getFrameTimestampFence(
@@ -347,11 +394,9 @@
nsecs_t now = systemTime();
FrameEventHistoryDelta frameTimestamps;
- FrameEventHistoryDelta* frameTimestampsOrNull =
- enableFrameTimestamps ? &frameTimestamps : nullptr;
-
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
- reqWidth, reqHeight, reqFormat, reqUsage, frameTimestampsOrNull);
+ reqWidth, reqHeight, reqFormat, reqUsage,
+ enableFrameTimestamps ? &frameTimestamps : nullptr);
mLastDequeueDuration = systemTime() - now;
if (result < 0) {
@@ -373,7 +418,7 @@
}
if (enableFrameTimestamps) {
- mFrameEventHistory.applyDelta(frameTimestamps);
+ mFrameEventHistory->applyDelta(frameTimestamps);
}
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
@@ -567,18 +612,20 @@
}
if (mEnableFrameTimestamps) {
- mFrameEventHistory.applyDelta(output.frameTimestamps);
+ mFrameEventHistory->applyDelta(output.frameTimestamps);
// Update timestamps with the local acquire fence.
// The consumer doesn't send it back to prevent us from having two
// file descriptors of the same fence.
- mFrameEventHistory.updateAcquireFence(mNextFrameNumber,
+ mFrameEventHistory->updateAcquireFence(mNextFrameNumber,
std::make_shared<FenceTime>(std::move(fence)));
// Cache timestamps of signaled fences so we can close their file
// descriptors.
- mFrameEventHistory.updateSignalTimes();
+ mFrameEventHistory->updateSignalTimes();
}
+ mLastFrameNumber = mNextFrameNumber;
+
mDefaultWidth = output.width;
mDefaultHeight = output.height;
mNextFrameNumber = output.nextFrameNumber;
@@ -613,8 +660,7 @@
mQueriedSupportedTimestamps = true;
std::vector<FrameEvent> supportedFrameTimestamps;
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- status_t err = composer->getSupportedFrameTimestamps(
+ status_t err = composerService()->getSupportedFrameTimestamps(
&supportedFrameTimestamps);
if (err != NO_ERROR) {
@@ -643,9 +689,8 @@
}
break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
- sp<ISurfaceComposer> composer(
- ComposerService::getComposerService());
- if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) {
+ if (composerService()->authenticateSurfaceTexture(
+ mGraphicBufferProducer)) {
*value = 1;
} else {
*value = 0;
@@ -913,15 +958,19 @@
uint32_t framesAgo = va_arg(args, uint32_t);
nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*);
nsecs_t* outAcquireTime = va_arg(args, int64_t*);
- nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
+ nsecs_t* outLatchTime = va_arg(args, int64_t*);
+ nsecs_t* outFirstRefreshStartTime = va_arg(args, int64_t*);
+ nsecs_t* outLastRefreshStartTime = va_arg(args, int64_t*);
nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
+ nsecs_t* outDequeueReadyTime = va_arg(args, int64_t*);
nsecs_t* outReleaseTime = va_arg(args, int64_t*);
return getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
- outRequestedPresentTime, outAcquireTime, outRefreshStartTime,
+ outRequestedPresentTime, outAcquireTime, outLatchTime,
+ outFirstRefreshStartTime, outLastRefreshStartTime,
outGlCompositionDoneTime, outDisplayPresentTime,
- outDisplayRetireTime, outReleaseTime);
+ outDisplayRetireTime, outDequeueReadyTime, outReleaseTime);
}
int Surface::connect(int api) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 78afc76..58b2a87 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -33,6 +33,7 @@
#include <ui/DisplayInfo.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
@@ -859,6 +860,33 @@
reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform);
}
+status_t ScreenshotClient::captureToBuffer(const sp<IBinder>& display,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ, bool useIdentityTransform,
+ uint32_t rotation,
+ sp<GraphicBuffer>* outBuffer) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+
+ sp<IGraphicBufferConsumer> gbpConsumer;
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &gbpConsumer);
+ sp<BufferItemConsumer> consumer(new BufferItemConsumer(gbpConsumer,
+ GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER,
+ 1, true));
+
+ status_t ret = s->captureScreen(display, producer, sourceCrop, reqWidth, reqHeight,
+ minLayerZ, maxLayerZ, useIdentityTransform,
+ static_cast<ISurfaceComposer::Rotation>(rotation));
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ BufferItem b;
+ consumer->acquireBuffer(&b, 0, true);
+ *outBuffer = b.mGraphicBuffer;
+ return ret;
+}
+
ScreenshotClient::ScreenshotClient()
: mHaveBuffer(false) {
memset(&mBuffer, 0, sizeof(mBuffer));
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 66e1bb6..e40b4eb 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -19,22 +19,28 @@
#include <gtest/gtest.h>
#include <binder/IMemory.h>
+#include <binder/ProcessState.h>
+#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/BufferItemConsumer.h>
+#include <private/gui/ComposerService.h>
#include <ui/Rect.h>
#include <utils/String8.h>
-#include <private/gui/ComposerService.h>
-#include <binder/ProcessState.h>
-
+#include <limits>
#include <thread>
namespace android {
using namespace std::chrono_literals;
+class FakeSurfaceComposer;
+class FakeProducerFrameEventHistory;
+
+static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
+
class SurfaceTest : public ::testing::Test {
protected:
@@ -102,7 +108,8 @@
BufferQueue::createBufferQueue(&producer, &consumer);
sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ sp<IBinder> display(sf->getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(),
64, 64, 0, 0x7fffffff, false));
@@ -272,4 +279,947 @@
ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
}
+
+class FakeConsumer : public BnConsumerListener {
+public:
+ void onFrameAvailable(const BufferItem& /*item*/) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+
+ void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override {
+ if (newTimestamps) {
+ if (mGetFrameTimestampsEnabled) {
+ EXPECT_GT(mNewFrameEntryOverride.frameNumber, 0u) <<
+ "Test should set mNewFrameEntryOverride before queuing "
+ "a frame.";
+ EXPECT_EQ(newTimestamps->frameNumber,
+ mNewFrameEntryOverride.frameNumber) <<
+ "Test attempting to add NewFrameEntryOverride with "
+ "incorrect frame number.";
+ mFrameEventHistory.addQueue(mNewFrameEntryOverride);
+ mNewFrameEntryOverride.frameNumber = 0;
+ }
+ mAddFrameTimestampsCount++;
+ mLastAddedFrameNumber = newTimestamps->frameNumber;
+ }
+ if (outDelta) {
+ mFrameEventHistory.getAndResetDelta(outDelta);
+ mGetFrameTimestampsCount++;
+ }
+ mAddAndGetFrameTimestampsCallCount++;
+ }
+
+ bool mGetFrameTimestampsEnabled = false;
+
+ ConsumerFrameEventHistory mFrameEventHistory;
+ int mAddAndGetFrameTimestampsCallCount = 0;
+ int mAddFrameTimestampsCount = 0;
+ int mGetFrameTimestampsCount = 0;
+ uint64_t mLastAddedFrameNumber = NO_FRAME_INDEX;
+
+ NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr };
+};
+
+
+class FakeSurfaceComposer : public ISurfaceComposer{
+public:
+ ~FakeSurfaceComposer() override {}
+
+ void setSupportedTimestamps(bool supportsPresent, bool supportsRetire) {
+ mSupportsPresent = supportsPresent;
+ mSupportsRetire = supportsRetire;
+ }
+
+ sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
+ sp<IGraphicBufferAlloc> createGraphicBufferAlloc() override {
+ return nullptr;
+ }
+ sp<IDisplayEventConnection> createDisplayEventConnection() override {
+ return nullptr;
+ }
+ sp<IBinder> createDisplay(const String8& /*displayName*/,
+ bool /*secure*/) override { return nullptr; }
+ void destroyDisplay(const sp<IBinder>& /*display */) override {}
+ sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
+ void setTransactionState(const Vector<ComposerState>& /*state*/,
+ const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/)
+ override {}
+ void bootFinished() override {}
+ bool authenticateSurfaceTexture(
+ const sp<IGraphicBufferProducer>& /*surface*/) const override {
+ return false;
+ }
+
+ status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported)
+ const override {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
+ FrameEvent::GL_COMPOSITION_DONE,
+ FrameEvent::DEQUEUE_READY,
+ FrameEvent::RELEASE
+ };
+ if (mSupportsPresent) {
+ outSupported->push_back(
+ FrameEvent::DISPLAY_PRESENT);
+ }
+ if (mSupportsRetire) {
+ outSupported->push_back(
+ FrameEvent::DISPLAY_RETIRE);
+ }
+ return NO_ERROR;
+ }
+
+ void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
+ status_t getDisplayConfigs(const sp<IBinder>& /*display*/,
+ Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
+ status_t getDisplayStats(const sp<IBinder>& /*display*/,
+ DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
+ int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
+ status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/)
+ override {
+ return NO_ERROR;
+ }
+ status_t getDisplayColorModes(const sp<IBinder>& /*display*/,
+ Vector<android_color_mode_t>* /*outColorModes*/) override {
+ return NO_ERROR;
+ }
+ android_color_mode_t getActiveColorMode(const sp<IBinder>& /*display*/)
+ override {
+ return HAL_COLOR_MODE_NATIVE;
+ }
+ status_t setActiveColorMode(const sp<IBinder>& /*display*/,
+ android_color_mode_t /*colorMode*/) override { return NO_ERROR; }
+ status_t captureScreen(const sp<IBinder>& /*display*/,
+ const sp<IGraphicBufferProducer>& /*producer*/,
+ Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
+ uint32_t /*minLayerZ*/, uint32_t /*maxLayerZ*/,
+ bool /*useIdentityTransform*/,
+ Rotation /*rotation*/) override { return NO_ERROR; }
+ status_t clearAnimationFrameStats() override { return NO_ERROR; }
+ status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
+ return NO_ERROR;
+ }
+ status_t getHdrCapabilities(const sp<IBinder>& /*display*/,
+ HdrCapabilities* /*outCapabilities*/) const override {
+ return NO_ERROR;
+ }
+ status_t enableVSyncInjections(bool /*enable*/) override {
+ return NO_ERROR;
+ }
+ status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
+
+protected:
+ IBinder* onAsBinder() override { return nullptr; }
+
+private:
+ bool mSupportsPresent{true};
+ bool mSupportsRetire{true};
+};
+
+class FakeProducerFrameEventHistory : public ProducerFrameEventHistory {
+public:
+ FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap)
+ : mFenceMap(fenceMap) {}
+
+ ~FakeProducerFrameEventHistory() {}
+
+ void updateAcquireFence(uint64_t frameNumber,
+ std::shared_ptr<FenceTime>&& acquire) override {
+ // Verify the acquire fence being added isn't the one from the consumer.
+ EXPECT_NE(mConsumerAcquireFence, acquire);
+ // Override the fence, so we can verify this was called by the
+ // producer after the frame is queued.
+ ProducerFrameEventHistory::updateAcquireFence(frameNumber,
+ std::shared_ptr<FenceTime>(mAcquireFenceOverride));
+ }
+
+ void setAcquireFenceOverride(
+ const std::shared_ptr<FenceTime>& acquireFenceOverride,
+ const std::shared_ptr<FenceTime>& consumerAcquireFence) {
+ mAcquireFenceOverride = acquireFenceOverride;
+ mConsumerAcquireFence = consumerAcquireFence;
+ }
+
+protected:
+ std::shared_ptr<FenceTime> createFenceTime(const sp<Fence>& fence)
+ const override {
+ return mFenceMap->createFenceTimeForTest(fence);
+ }
+
+ FenceToFenceTimeMap* mFenceMap{nullptr};
+
+ std::shared_ptr<FenceTime> mAcquireFenceOverride{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> mConsumerAcquireFence{FenceTime::NO_FENCE};
+};
+
+
+class TestSurface : public Surface {
+public:
+ TestSurface(const sp<IGraphicBufferProducer>& bufferProducer,
+ FenceToFenceTimeMap* fenceMap)
+ : Surface(bufferProducer),
+ mFakeSurfaceComposer(new FakeSurfaceComposer) {
+ mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap);
+ mFrameEventHistory.reset(mFakeFrameEventHistory);
+ }
+
+ ~TestSurface() override {}
+
+ sp<ISurfaceComposer> composerService() const override {
+ return mFakeSurfaceComposer;
+ }
+
+public:
+ sp<FakeSurfaceComposer> mFakeSurfaceComposer;
+
+ // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory,
+ // but this raw pointer gives access to test functionality.
+ FakeProducerFrameEventHistory* mFakeFrameEventHistory;
+};
+
+
+class GetFrameTimestampsTest : public SurfaceTest {
+protected:
+ struct FenceAndFenceTime {
+ explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap)
+ : mFence(new Fence),
+ mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {}
+ sp<Fence> mFence { nullptr };
+ std::shared_ptr<FenceTime> mFenceTime { nullptr };
+ };
+
+ struct RefreshEvents {
+ RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart)
+ : mFenceMap(fenceMap),
+ kStartTime(refreshStart + 1),
+ kGpuCompositionDoneTime(refreshStart + 2),
+ kPresentTime(refreshStart + 3) {}
+
+ void signalPostCompositeFences() {
+ mFenceMap.signalAllForTest(
+ mGpuCompositionDone.mFence, kGpuCompositionDoneTime);
+ mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime);
+ }
+
+ FenceToFenceTimeMap& mFenceMap;
+
+ FenceAndFenceTime mGpuCompositionDone { mFenceMap };
+ FenceAndFenceTime mPresent { mFenceMap };
+
+ const nsecs_t kStartTime;
+ const nsecs_t kGpuCompositionDoneTime;
+ const nsecs_t kPresentTime;
+ };
+
+ struct FrameEvents {
+ FrameEvents(FenceToFenceTimeMap& fenceMap, nsecs_t frameStartTime)
+ : mFenceMap(fenceMap),
+ kPostedTime(frameStartTime + 100),
+ kRequestedPresentTime(frameStartTime + 200),
+ kProducerAcquireTime(frameStartTime + 300),
+ kConsumerAcquireTime(frameStartTime + 301),
+ kLatchTime(frameStartTime + 500),
+ kDequeueReadyTime(frameStartTime + 600),
+ kRetireTime(frameStartTime + 700),
+ kReleaseTime(frameStartTime + 800),
+ mRefreshes {
+ { mFenceMap, frameStartTime + 410 },
+ { mFenceMap, frameStartTime + 420 },
+ { mFenceMap, frameStartTime + 430 } } {}
+
+ void signalQueueFences() {
+ mFenceMap.signalAllForTest(
+ mAcquireConsumer.mFence, kConsumerAcquireTime);
+ mFenceMap.signalAllForTest(
+ mAcquireProducer.mFence, kProducerAcquireTime);
+ }
+
+ void signalRefreshFences() {
+ for (auto& re : mRefreshes) {
+ re.signalPostCompositeFences();
+ }
+ }
+
+ void signalReleaseFences() {
+ mFenceMap.signalAllForTest(mRetire.mFence, kRetireTime);
+ mFenceMap.signalAllForTest(mRelease.mFence, kReleaseTime);
+ }
+
+ FenceToFenceTimeMap& mFenceMap;
+
+ FenceAndFenceTime mAcquireConsumer { mFenceMap };
+ FenceAndFenceTime mAcquireProducer { mFenceMap };
+ FenceAndFenceTime mRetire { mFenceMap };
+ FenceAndFenceTime mRelease { mFenceMap };
+
+ const nsecs_t kPostedTime;
+ const nsecs_t kRequestedPresentTime;
+ const nsecs_t kProducerAcquireTime;
+ const nsecs_t kConsumerAcquireTime;
+ const nsecs_t kLatchTime;
+ const nsecs_t kDequeueReadyTime;
+ const nsecs_t kRetireTime;
+ const nsecs_t kReleaseTime;
+
+ RefreshEvents mRefreshes[3];
+ };
+
+ GetFrameTimestampsTest() : SurfaceTest() {}
+
+ virtual void SetUp() {
+ SurfaceTest::SetUp();
+
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mFakeConsumer = new FakeConsumer;
+ mCfeh = &mFakeConsumer->mFrameEventHistory;
+ mConsumer->consumerConnect(mFakeConsumer, false);
+ mConsumer->setConsumerName(String8("TestConsumer"));
+ mSurface = new TestSurface(mProducer, &mFenceMap);
+ mWindow = mSurface;
+
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(),
+ NATIVE_WINDOW_API_CPU));
+ native_window_set_buffer_count(mWindow.get(), 4);
+ }
+
+ void enableFrameTimestamps() {
+ mFakeConsumer->mGetFrameTimestampsEnabled = true;
+ native_window_enable_frame_timestamps(mWindow.get(), 1);
+ mFrameTimestampsEnabled = true;
+ }
+
+ int getAllFrameTimestamps(uint32_t framesAgo) {
+ return native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, &outLatchTime,
+ &outFirstRefreshStartTime, &outLastRefreshStartTime,
+ &outGpuCompositionDoneTime, &outDisplayPresentTime,
+ &outDisplayRetireTime, &outDequeueReadyTime, &outReleaseTime);
+ }
+
+ void resetTimestamps() {
+ outRequestedPresentTime = -1;
+ outAcquireTime = -1;
+ outLatchTime = -1;
+ outFirstRefreshStartTime = -1;
+ outLastRefreshStartTime = -1;
+ outGpuCompositionDoneTime = -1;
+ outDisplayPresentTime = -1;
+ outDisplayRetireTime = -1;
+ outDequeueReadyTime = -1;
+ outReleaseTime = -1;
+ }
+
+ void dequeueAndQueue(uint64_t frameIndex) {
+ int fence = -1;
+ ANativeWindowBuffer* buffer = nullptr;
+ ASSERT_EQ(NO_ERROR,
+ mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+
+ int oldAddFrameTimestampsCount =
+ mFakeConsumer->mAddFrameTimestampsCount;
+
+ FrameEvents* frame = &mFrames[frameIndex];
+ uint64_t frameNumber = frameIndex + 1;
+
+ NewFrameEventsEntry fe;
+ fe.frameNumber = frameNumber;
+ fe.postedTime = frame->kPostedTime;
+ fe.requestedPresentTime = frame->kRequestedPresentTime;
+ fe.acquireFence = frame->mAcquireConsumer.mFenceTime;
+ mFakeConsumer->mNewFrameEntryOverride = fe;
+
+ mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+ frame->mAcquireProducer.mFenceTime,
+ frame->mAcquireConsumer.mFenceTime);
+
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+
+ EXPECT_EQ(frameNumber, mFakeConsumer->mLastAddedFrameNumber);
+
+ EXPECT_EQ(
+ oldAddFrameTimestampsCount + (mFrameTimestampsEnabled ? 1 : 0),
+ mFakeConsumer->mAddFrameTimestampsCount);
+ }
+
+ void addFrameEvents(
+ bool gpuComposited, uint64_t iOldFrame, int64_t iNewFrame) {
+ FrameEvents* oldFrame =
+ (iOldFrame == NO_FRAME_INDEX) ? nullptr : &mFrames[iOldFrame];
+ FrameEvents* newFrame = &mFrames[iNewFrame];
+
+ uint64_t nOldFrame = iOldFrame + 1;
+ uint64_t nNewFrame = iNewFrame + 1;
+
+ // Latch, Composite, Retire, and Release the frames in a plausible
+ // order. Note: The timestamps won't necessarily match the order, but
+ // that's okay for the purposes of this test.
+ std::shared_ptr<FenceTime> gpuDoneFenceTime = FenceTime::NO_FENCE;
+
+ // Composite the previous frame one more time, which helps verify
+ // LastRefresh is updated properly.
+ if (oldFrame != nullptr) {
+ mCfeh->addPreComposition(nOldFrame,
+ oldFrame->mRefreshes[2].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime,
+ oldFrame->mRefreshes[2].mPresent.mFenceTime);
+ }
+
+ // Latch the new frame.
+ mCfeh->addLatch(nNewFrame, newFrame->kLatchTime);
+
+ mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[0].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ // HWC2 releases the previous buffer after a new latch just before
+ // calling postComposition.
+ if (oldFrame != nullptr) {
+ mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime,
+ std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime));
+ }
+ mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+ newFrame->mRefreshes[0].mPresent.mFenceTime);
+
+ // Retire the previous buffer just after compositing the new buffer.
+ if (oldFrame != nullptr) {
+ mCfeh->addRetire(nOldFrame, oldFrame->mRetire.mFenceTime);
+ }
+
+ mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[1].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+ newFrame->mRefreshes[1].mPresent.mFenceTime);
+ }
+
+ void QueryPresentRetireSupported(
+ bool displayPresentSupported, bool displayRetireSupported);
+ void PresentOrRetireUnsupportedNoSyncTest(
+ bool displayPresentSupported, bool displayRetireSupported);
+
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<FakeConsumer> mFakeConsumer;
+ ConsumerFrameEventHistory* mCfeh;
+ sp<TestSurface> mSurface;
+ sp<ANativeWindow> mWindow;
+
+ FenceToFenceTimeMap mFenceMap;
+
+ bool mFrameTimestampsEnabled = false;
+
+ int64_t outRequestedPresentTime = -1;
+ int64_t outAcquireTime = -1;
+ int64_t outLatchTime = -1;
+ int64_t outFirstRefreshStartTime = -1;
+ int64_t outLastRefreshStartTime = -1;
+ int64_t outGpuCompositionDoneTime = -1;
+ int64_t outDisplayPresentTime = -1;
+ int64_t outDisplayRetireTime = -1;
+ int64_t outDequeueReadyTime = -1;
+ int64_t outReleaseTime = -1;
+
+ FrameEvents mFrames[2] { { mFenceMap, 1000 }, { mFenceMap, 2000 } };
+};
+
+
+// This test verifies that the frame timestamps are not retrieved when not
+// explicitly enabled via native_window_enable_frame_timestamps.
+// We want to check this to make sure there's no overhead for users
+// that don't need the timestamp information.
+TEST_F(GetFrameTimestampsTest, DefaultDisabled) {
+ int fence;
+ ANativeWindowBuffer* buffer;
+
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify the producer doesn't get frame timestamps piggybacked on dequeue.
+ ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify the producer doesn't get frame timestamps piggybacked on queue.
+ // It is okay that frame timestamps are added in the consumer since it is
+ // still needed for SurfaceFlinger dumps.
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+ EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify attempts to get frame timestamps fail.
+ const uint32_t framesAgo = 0;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(INVALID_OPERATION, result);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+// This test verifies that the frame timestamps are retrieved if explicitly
+// enabled via native_window_enable_frame_timestamps.
+TEST_F(GetFrameTimestampsTest, EnabledSimple) {
+ enableFrameTimestamps();
+
+ int fence;
+ ANativeWindowBuffer* buffer;
+
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify getFrameTimestamps is piggybacked on dequeue.
+ ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount);
+
+ NewFrameEventsEntry f1;
+ f1.frameNumber = 1;
+ f1.postedTime = mFrames[0].kPostedTime;
+ f1.requestedPresentTime = mFrames[0].kRequestedPresentTime;
+ f1.acquireFence = mFrames[0].mAcquireConsumer.mFenceTime;
+ mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+ mFrames[0].mAcquireProducer.mFenceTime,
+ mFrames[0].mAcquireConsumer.mFenceTime);
+ mFakeConsumer->mNewFrameEntryOverride = f1;
+ mFrames[0].signalQueueFences();
+
+ // Verify getFrameTimestamps is piggybacked on queue.
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+ EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber);
+ EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify queries for timestamps that the producer doesn't know about
+ // triggers a call to see if the consumer has any new timestamps.
+ const uint32_t framesAgo = 0;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+void GetFrameTimestampsTest::QueryPresentRetireSupported(
+ bool displayPresentSupported, bool displayRetireSupported) {
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(
+ displayPresentSupported, displayRetireSupported);
+
+ // Verify supported bits are forwarded.
+ int supportsPresent = -1;
+ mWindow.get()->query(mWindow.get(),
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+ EXPECT_EQ(displayPresentSupported, supportsPresent);
+
+ int supportsRetire = -1;
+ mWindow.get()->query(mWindow.get(),
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE, &supportsRetire);
+ EXPECT_EQ(displayRetireSupported, supportsRetire);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentSupported) {
+ QueryPresentRetireSupported(true, false);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryRetireSupported) {
+ QueryPresentRetireSupported(false, true);
+}
+
+// This test verifies that:
+// 1) The timestamps recorded in the consumer's FrameTimestampsHistory are
+// properly retrieved by the producer for the correct frames.
+// 2) When framesAgo is 0, it is querying for the most recently queued frame.
+TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) {
+ enableFrameTimestamps();
+
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ mFrames[0].signalRefreshFences();
+ addFrameEvents(true, 0, 1);
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify timestamps are correct for frame 1.
+ uint32_t framesAgo = 1;
+ resetTimestamps();
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+
+ // Verify timestamps are correct for frame 2.
+ framesAgo = 0;
+ resetTimestamps();
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(0, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+}
+
+// This test verifies the acquire fence recorded by the consumer is not sent
+// back to the producer and the producer saves its own fence.
+TEST_F(GetFrameTimestampsTest, QueueTimestampsNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ const uint32_t framesAgo = 0;
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+
+ // Verify queue-related timestamps for f1 are available immediately in the
+ // producer without asking the consumer again, even before signaling the
+ // acquire fence.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(0, outAcquireTime);
+
+ // Signal acquire fences. Verify a sync call still isn't necessary.
+ mFrames[0].signalQueueFences();
+
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+
+ // Verify queue-related timestamps for f2 are available immediately in the
+ // producer without asking the consumer again, even before signaling the
+ // acquire fence.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(0, outAcquireTime);
+
+ // Signal acquire fences. Verify a sync call still isn't necessary.
+ mFrames[1].signalQueueFences();
+
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+}
+
+TEST_F(GetFrameTimestampsTest, ZeroRequestedTimestampsNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ mFrames[0].signalRefreshFences();
+ addFrameEvents(true, 0, 1);
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify a request for no timestamps doesn't result in a sync call.
+ const uint32_t framesAgo = 0;
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+// This test verifies that fences can signal and update timestamps producer
+// side without an additional sync call to the consumer.
+TEST_F(GetFrameTimestampsTest, FencesInProducerNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ addFrameEvents(true, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ uint32_t framesAgo = 1;
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(0, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+
+ // Verify available timestamps are correct for frame 1 again, before any
+ // fence has been signaled.
+ // This time a sync call should not be necessary.
+ framesAgo = 1;
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(0, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+
+ // Signal the fences for frame 1.
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+
+ // Verify all timestamps are available without a sync call.
+ framesAgo = 1;
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+}
+
+// This test verifies that if the frame wasn't GPU composited but has a refresh
+// event a sync call isn't made to get the GPU composite done time since it will
+// never exist.
+TEST_F(GetFrameTimestampsTest, NoGpuNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ const uint32_t framesAgo = 1;
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(false, NO_FRAME_INDEX, 0);
+ addFrameEvents(false, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(0, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+
+ // Signal the fences for frame 1.
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+
+ // Verify all timestamps, except GPU composition, are available without a
+ // sync call.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+}
+
+// This test verifies that if the certain timestamps can't possibly exist for
+// the most recent frame, then a sync call is not done.
+TEST_F(GetFrameTimestampsTest, NoRetireOrReleaseNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(false, NO_FRAME_INDEX, 0);
+ addFrameEvents(false, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ uint32_t framesAgo = 1;
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(0, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify querying for all timestmaps of f2 does not do a sync call.
+ // Even though the lastRefresh, retire, dequeueReady, and release times aren't
+ // available, a sync call should not occur because it's not possible for f2
+ // to encounter the final value for those events until another frame is
+ // queued.
+ framesAgo = 0;
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(0, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+}
+
+// This test verifies there are no sync calls for present or retire times
+// when they aren't supported and that an error is returned.
+void GetFrameTimestampsTest::PresentOrRetireUnsupportedNoSyncTest(
+ bool displayPresentSupported, bool displayRetireSupported) {
+
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(
+ displayPresentSupported, displayRetireSupported);
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+
+ // Verify a query for the Present and Retire times do not trigger
+ // a sync call if they are not supported.
+ const uint32_t framesAgo = 0;
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ displayPresentSupported ? nullptr : &outDisplayPresentTime,
+ displayRetireSupported ? nullptr : &outDisplayRetireTime,
+ nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(BAD_VALUE, result);
+ EXPECT_EQ(-1, outDisplayRetireTime);
+ EXPECT_EQ(-1, outDisplayPresentTime);
+}
+
+TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) {
+ PresentOrRetireUnsupportedNoSyncTest(false, true);
+}
+
+TEST_F(GetFrameTimestampsTest, RetireUnsupportedNoSync) {
+ PresentOrRetireUnsupportedNoSyncTest(true, false);
+}
+
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 136dd7f..af1c0af 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -27,8 +27,9 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
+
#include <input/InputTransport.h>
namespace android {
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
index 1a60f0f..6296abe 100644
--- a/libs/ui/ColorSpace.cpp
+++ b/libs/ui/ColorSpace.cpp
@@ -137,7 +137,7 @@
{0.3127f, 0.3290f},
std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
- std::bind(clamp<float>, _1, -0.5f, 7.5f)
+ std::bind(clamp<float>, _1, -0.799f, 2.399f)
};
}
@@ -148,7 +148,7 @@
{0.3127f, 0.3290f},
linearReponse,
linearReponse,
- std::bind(clamp<float>, _1, -0.5f, 7.5f)
+ std::bind(clamp<float>, _1, -0.5f, 7.499f)
};
}
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index a1dda3a..02d4137 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <ui/Fence.h>
+
#define LOG_TAG "Fence"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -25,9 +27,10 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
-#include <ui/Fence.h>
+#include <sys/types.h>
#include <unistd.h>
#include <utils/Log.h>
+#include <utils/String8.h>
#include <utils/Trace.h>
namespace android {
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index c0245eb..8106b16 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -17,6 +17,7 @@
#include <ui/FenceTime.h>
#include <cutils/compiler.h> // For CC_[UN]LIKELY
+#include <utils/Log.h>
#include <inttypes.h>
#include <stdlib.h>
@@ -130,6 +131,14 @@
// Make the system call without the lock held.
signalTime = fence->getSignalTime();
+ // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests
+ // use invalid underlying Fences without real file descriptors.
+ if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) {
+ if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+ signalTime = Fence::SIGNAL_TIME_PENDING;
+ }
+ }
+
// Make the signal time visible to everyone if it is no longer pending
// and remove the class' reference to the fence.
if (signalTime != Fence::SIGNAL_TIME_PENDING) {
@@ -163,10 +172,28 @@
return Snapshot(mFence);
}
+// For tests only. If forceValidForTest is true, then getSignalTime will
+// never return SIGNAL_TIME_INVALID and isValid will always return true.
+FenceTime::FenceTime(const sp<Fence>& fence, bool forceValidForTest)
+ : mState(forceValidForTest ?
+ State::FORCED_VALID_FOR_TEST : State::INVALID),
+ mFence(fence),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+void FenceTime::signalForTest(nsecs_t signalTime) {
+ // To be realistic, this should really set a hidden value that
+ // gets picked up in the next call to getSignalTime, but this should
+ // be good enough.
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(signalTime, std::memory_order_relaxed);
+}
+
// ============================================================================
// FenceTime::Snapshot
// ============================================================================
-
FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence)
: state(State::FENCE), fence(srcFence) {
}
@@ -279,4 +306,60 @@
}
}
+// ============================================================================
+// FenceToFenceTimeMap
+// ============================================================================
+std::shared_ptr<FenceTime> FenceToFenceTimeMap::createFenceTimeForTest(
+ const sp<Fence>& fence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ // Always garbage collecting isn't efficient, but this is only for testing.
+ garbageCollectLocked();
+ std::shared_ptr<FenceTime> fenceTime(new FenceTime(fence, true));
+ mMap[fence.get()].push_back(fenceTime);
+ return fenceTime;
+}
+
+void FenceToFenceTimeMap::signalAllForTest(
+ const sp<Fence>& fence, nsecs_t signalTime) {
+ bool signaled = false;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto it = mMap.find(fence.get());
+ if (it != mMap.end()) {
+ for (auto& weakFenceTime : it->second) {
+ std::shared_ptr<FenceTime> fenceTime = weakFenceTime.lock();
+ if (!fenceTime) {
+ continue;
+ }
+ ALOGE_IF(!fenceTime->isValid(),
+ "FenceToFenceTimeMap::signalAllForTest: "
+ "Signaling invalid fence.");
+ fenceTime->signalForTest(signalTime);
+ signaled = true;
+ }
+ }
+
+ if (!signaled) {
+ ALOGE("FenceToFenceTimeMap::signalAllForTest: Nothing to signal.");
+ }
+}
+
+void FenceToFenceTimeMap::garbageCollectLocked() {
+ for (auto& it : mMap) {
+ // Erase all expired weak pointers from the vector.
+ auto& vect = it.second;
+ vect.erase(
+ std::remove_if(vect.begin(), vect.end(),
+ [](const std::weak_ptr<FenceTime>& ft) {
+ return ft.expired();
+ }),
+ vect.end());
+
+ // Also erase the map entry if the vector is now empty.
+ if (vect.empty()) {
+ mMap.erase(it.first);
+ }
+ }
+}
+
} // namespace android
diff --git a/libs/ui/GrallocMapper.cpp b/libs/ui/GrallocMapper.cpp
index b444871..7ee01ad 100644
--- a/libs/ui/GrallocMapper.cpp
+++ b/libs/ui/GrallocMapper.cpp
@@ -140,7 +140,7 @@
int Mapper::unlock(buffer_handle_t handle) const
{
- int releaseFence;
+ int releaseFence = -1;
Error error = kDefaultError;
mMapper->unlock(handle,
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 6f57d37..5ae4faa 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -18,8 +18,7 @@
#define LOG_TAG "GraphicBufferAllocator"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <android/log.h>
-
+#include <log/log.h>
#include <utils/Singleton.h>
#include <utils/String8.h>
#include <utils/Trace.h>
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 874e712..8b754d5 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -634,11 +634,14 @@
#define EGL_TIMESTAMPS_ANDROID 0x314D
#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x314E
#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
-#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430
-#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
-#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3432
-#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3433
-#define EGL_READS_DONE_TIME_ANDROID 0x3434
+#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3150
+#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151
+#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152
+#define EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153
+#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155
+#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3156
+#define EGL_READS_DONE_TIME_ANDROID 0x3157
#ifdef EGL_EGLEXT_PROTOTYPES
EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 4e9cb5b..48bf676 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -27,7 +27,7 @@
#include <sys/mman.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/threads.h>
#include <ui/ANativeObjectBase.h>
@@ -740,6 +740,7 @@
case GGL_PIXEL_FORMAT_RGB_565: size *= 2; break;
case GGL_PIXEL_FORMAT_RGBA_8888: size *= 4; break;
case GGL_PIXEL_FORMAT_RGBX_8888: size *= 4; break;
+ case GGL_PIXEL_FORMAT_BGRA_8888: size *= 4; break;
default:
ALOGE("incompatible pixel format for pbuffer (format=%d)", f);
pbuffer.data = 0;
@@ -1027,6 +1028,19 @@
{ EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
};
+// BGRA 8888 config
+static config_pair_t const config_8_attribute_list[] = {
+ { EGL_BUFFER_SIZE, 32 },
+ { EGL_ALPHA_SIZE, 8 },
+ { EGL_BLUE_SIZE, 8 },
+ { EGL_GREEN_SIZE, 8 },
+ { EGL_RED_SIZE, 8 },
+ { EGL_DEPTH_SIZE, 0 },
+ { EGL_CONFIG_ID, 8 },
+ { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_BGRA_8888 },
+ { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
+};
+
static configs_t const gConfigs[] = {
{ config_0_attribute_list, NELEM(config_0_attribute_list) },
{ config_1_attribute_list, NELEM(config_1_attribute_list) },
@@ -1036,6 +1050,7 @@
{ config_5_attribute_list, NELEM(config_5_attribute_list) },
{ config_6_attribute_list, NELEM(config_6_attribute_list) },
{ config_7_attribute_list, NELEM(config_7_attribute_list) },
+ { config_8_attribute_list, NELEM(config_8_attribute_list) },
};
static config_management_t const gConfigManagement[] = {
@@ -1118,6 +1133,10 @@
pixelFormat = GGL_PIXEL_FORMAT_A_8;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
+ case 8:
+ pixelFormat = GGL_PIXEL_FORMAT_BGRA_8888;
+ depthFormat = 0;
+ break;
default:
return NAME_NOT_FOUND;
}
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 27d81fe..01d7bbb 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -14,6 +14,10 @@
** limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <array>
#include <ctype.h>
#include <dirent.h>
#include <dlfcn.h>
@@ -23,8 +27,10 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h>
+#include <android/dlext.h>
#include <cutils/properties.h>
+#include <log/log.h>
+#include <utils/Trace.h>
#include <EGL/egl.h>
@@ -96,6 +102,11 @@
return atoi(prop);
}
+static void* do_dlopen(const char* path, int mode) {
+ ATRACE_CALL();
+ return dlopen(path, mode);
+}
+
// ----------------------------------------------------------------------------
Loader::driver_t::driver_t(void* gles)
@@ -136,14 +147,30 @@
// ----------------------------------------------------------------------------
Loader::Loader()
- : getProcAddress(NULL) {
+ : getProcAddress(NULL),
+ mLibGui(nullptr),
+ mGetDriverNamespace(nullptr)
+{
+ // FIXME: See note in GraphicsEnv.h about android_getDriverNamespace().
+ // libgui should already be loaded in any process that uses libEGL, but
+ // if for some reason it isn't, then we're not going to get a driver
+ // namespace anyway, so don't force it to be loaded.
+ mLibGui = dlopen("libgui.so", RTLD_NOLOAD | RTLD_LOCAL | RTLD_LAZY);
+ if (!mLibGui) {
+ ALOGD("failed to load libgui: %s", dlerror());
+ return;
+ }
+ mGetDriverNamespace = reinterpret_cast<decltype(mGetDriverNamespace)>(
+ dlsym(mLibGui, "android_getDriverNamespace"));
}
Loader::~Loader() {
+ if (mLibGui)
+ dlclose(mLibGui);
}
static void* load_wrapper(const char* path) {
- void* so = dlopen(path, RTLD_NOW | RTLD_LOCAL);
+ void* so = do_dlopen(path, RTLD_NOW | RTLD_LOCAL);
ALOGE_IF(!so, "dlopen(\"%s\") failed: %s", path, dlerror());
return so;
}
@@ -190,6 +217,8 @@
void* Loader::open(egl_connection_t* cnx)
{
+ ATRACE_CALL();
+
void* dso;
driver_t* hnd = 0;
@@ -235,6 +264,8 @@
__eglMustCastToProperFunctionPointerType* curr,
getProcAddressType getProcAddress)
{
+ ATRACE_CALL();
+
const ssize_t SIZE = 256;
char scrap[SIZE];
while (*api) {
@@ -286,9 +317,8 @@
}
}
-void *Loader::load_driver(const char* kind,
- egl_connection_t* cnx, uint32_t mask)
-{
+static void* load_system_driver(const char* kind) {
+ ATRACE_CALL();
class MatchFile {
public:
static String8 find(const char* kind) {
@@ -404,7 +434,7 @@
}
const char* const driver_absolute_path = absolutePath.string();
- void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
+ void* dso = do_dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
if (dso == 0) {
const char* err = dlerror();
ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown");
@@ -413,11 +443,63 @@
ALOGD("loaded %s", driver_absolute_path);
+ return dso;
+}
+
+static void* do_android_dlopen_ext(const char* path, int mode, const android_dlextinfo* info) {
+ ATRACE_CALL();
+ return android_dlopen_ext(path, mode, info);
+}
+
+static const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
+ "ro.hardware.egl",
+ "ro.board.platform",
+}};
+
+static void* load_updated_driver(const char* kind, android_namespace_t* ns) {
+ ATRACE_CALL();
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = ns,
+ };
+ void* so = nullptr;
+ char prop[PROPERTY_VALUE_MAX + 1];
+ for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
+ if (property_get(key, prop, nullptr) > 0) {
+ String8 name;
+ name.appendFormat("lib%s_%s.so", kind, prop);
+ so = do_android_dlopen_ext(name.string(), RTLD_LOCAL | RTLD_NOW,
+ &dlextinfo);
+ if (so)
+ return so;
+ }
+ }
+ return nullptr;
+}
+
+void *Loader::load_driver(const char* kind,
+ egl_connection_t* cnx, uint32_t mask)
+{
+ ATRACE_CALL();
+
+ void* dso = nullptr;
+ if (mGetDriverNamespace) {
+ android_namespace_t* ns = mGetDriverNamespace();
+ if (ns) {
+ dso = load_updated_driver(kind, ns);
+ }
+ }
+ if (!dso) {
+ dso = load_system_driver(kind);
+ if (!dso)
+ return NULL;
+ }
+
if (mask & EGL) {
getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");
ALOGE_IF(!getProcAddress,
- "can't find eglGetProcAddress() in %s", driver_absolute_path);
+ "can't find eglGetProcAddress() in EGL driver library");
egl_t* egl = &cnx->egl;
__eglMustCastToProperFunctionPointerType* curr =
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 94f680e..d0435e7 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -25,6 +25,8 @@
#include <utils/Singleton.h>
#include <utils/String8.h>
+#include <gui/GraphicsEnv.h>
+
#include <EGL/egl.h>
// ----------------------------------------------------------------------------
@@ -53,7 +55,10 @@
};
getProcAddressType getProcAddress;
-
+
+ void* mLibGui;
+ decltype(android_getDriverNamespace)* mGetDriverNamespace;
+
public:
~Loader();
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 4245a92..ee83ada 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -24,10 +24,9 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <android/log.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
-
+#include <log/log.h>
#include <utils/CallStack.h>
#include <utils/String8.h>
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 3af7e93..b38b4c2 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -27,11 +27,11 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <android/log.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <cutils/memory.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <gui/ISurfaceComposer.h>
@@ -267,6 +267,7 @@
EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
{
+ ATRACE_CALL();
clearError();
uintptr_t index = reinterpret_cast<uintptr_t>(display);
@@ -2041,10 +2042,13 @@
nsecs_t* requestedPresentTime = nullptr;
nsecs_t* acquireTime = nullptr;
- nsecs_t* refreshStartTime = nullptr;
+ nsecs_t* latchTime = nullptr;
+ nsecs_t* firstRefreshStartTime = nullptr;
nsecs_t* GLCompositionDoneTime = nullptr;
+ nsecs_t* lastRefreshStartTime = nullptr;
nsecs_t* displayPresentTime = nullptr;
nsecs_t* displayRetireTime = nullptr;
+ nsecs_t* dequeueReadyTime = nullptr;
nsecs_t* releaseTime = nullptr;
for (int i = 0; i < numTimestamps; i++) {
@@ -2055,10 +2059,16 @@
case EGL_RENDERING_COMPLETE_TIME_ANDROID:
acquireTime = &values[i];
break;
- case EGL_COMPOSITION_START_TIME_ANDROID:
- refreshStartTime = &values[i];
+ case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+ latchTime = &values[i];
break;
- case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+ firstRefreshStartTime = &values[i];
+ break;
+ case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+ lastRefreshStartTime = &values[i];
+ break;
+ case EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID:
GLCompositionDoneTime = &values[i];
break;
case EGL_DISPLAY_PRESENT_TIME_ANDROID:
@@ -2067,6 +2077,9 @@
case EGL_DISPLAY_RETIRE_TIME_ANDROID:
displayRetireTime = &values[i];
break;
+ case EGL_DEQUEUE_READY_TIME_ANDROID:
+ dequeueReadyTime = &values[i];
+ break;
case EGL_READS_DONE_TIME_ANDROID:
releaseTime = &values[i];
break;
@@ -2076,9 +2089,9 @@
}
status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
- requestedPresentTime, acquireTime, refreshStartTime,
- GLCompositionDoneTime, displayPresentTime, displayRetireTime,
- releaseTime);
+ requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+ lastRefreshStartTime, GLCompositionDoneTime, displayPresentTime,
+ displayRetireTime, dequeueReadyTime, releaseTime);
switch (ret) {
case NO_ERROR:
@@ -2122,8 +2135,11 @@
#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
case EGL_REQUESTED_PRESENT_TIME_ANDROID:
case EGL_RENDERING_COMPLETE_TIME_ANDROID:
- case EGL_COMPOSITION_START_TIME_ANDROID:
- case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
+ case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+ case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID:
+ case EGL_DEQUEUE_READY_TIME_ANDROID:
case EGL_READS_DONE_TIME_ANDROID:
return EGL_TRUE;
case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index a32f037..d7df40c 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -15,6 +15,7 @@
*/
#define __STDC_LIMIT_MACROS 1
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <string.h>
@@ -26,6 +27,7 @@
#include "egl_tls.h"
#include "Loader.h"
#include <cutils/properties.h>
+#include <utils/Trace.h>
// ----------------------------------------------------------------------------
namespace android {
@@ -103,6 +105,7 @@
EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {
Mutex::Autolock _l(lock);
+ ATRACE_CALL();
// get our driver loader
Loader& loader(Loader::getInstance());
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index 1257004..6de5f27 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -17,9 +17,8 @@
#include <pthread.h>
#include <stdlib.h>
-#include <android/log.h>
#include <cutils/properties.h>
-
+#include <log/log.h>
#include <utils/CallStack.h>
#include <EGL/egl.h>
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
index b5b6eb5..7aa0d30 100644
--- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -69,11 +69,14 @@
EGL_TIMESTAMPS_ANDROID 0x314D
EGL_REQUESTED_PRESENT_TIME_ANDROID 0x314E
EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
- EGL_COMPOSITION_START_TIME_ANDROID 0x3430
- EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
- EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3432
- EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3433
- EGL_READS_DONE_TIME_ANDROID 0x3434
+ EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3150
+ EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151
+ EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152
+ EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153
+ EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154
+ EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155
+ EGL_DEQUEUE_READY_TIME_ANDROID 0x3156
+ EGL_READS_DONE_TIME_ANDROID 0x3157
Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
"Surface Attributes", page 43:
@@ -119,16 +122,31 @@
this will correspond to buffer's queue time.
- EGL_RENDERING_COMPLETE_TIME_ANDROID - The time when all of the
application's rendering to the surface was completed.
- - EGL_COMPOSITION_START_TIME_ANDROID - The time at which the compositor
- began preparing composition for this frame.
- - EGL_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the
+ - EGL_COMPOSITION_LATCH_TIME_ANDROID - The time when the compositor
+ selected this frame as the one to use for the next composition. The
+ latch is the earliest indication that the frame was submitted in time
+ to be composited.
+ - EGL_FIRST_COMPOSITION_START_TIME_ANDROID - The first time at which
+ the compositor began preparing composition for this frame.
+ - EGL_LAST_COMPOSITION_START_TIME_ANDROID - The last time at which the
+ compositor began preparing composition for this frame. If this frame
+ is composited only once, it will have the same value as
+ EGL_FIRST_COMPOSITION_START_TIME_ANDROID. If the value is not equal,
+ that indicates the subsequent frame was not submitted in time to be
+ latched by the compositor. Note: The value may not be updated for
+ every display refresh if the compositor becomes idle.
+ - EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the
compositor's rendering work for this frame finished. This will be zero
if composition was handled by the display and the compositor didn't do
any rendering.
- EGL_DISPLAY_PRESENT_TIME_ANDROID - The time at which this frame
- started to scan out on the physical display.
+ started to scan out to the physical display.
- EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was
replaced by the next frame on-screen.
+ - EGL_DEQUEUE_READY_TIME_ANDROID - The time when the buffer became
+ available for reuse as a buffer the client can target without
+ blocking. This is generally the point when all read commands of the
+ buffer have been submitted, but not necessarily completed.
- EGL_READS_DONE_TIME_ANDROID - The time at which all reads for the
purpose of display/composition were completed for this frame.
@@ -152,3 +170,8 @@
#2 (Brian Anderson, July 22, 2016)
- Replace EGL_QUEUE_TIME_ANDROID with EGL_REQUESTED_PRESENT_TIME_ANDROID.
- Add DISPLAY_PRESENT_TIME_ANDROID.
+
+#3 (Brian Anderson, November 30, 2016)
+ - Add EGL_COMPOSITION_LATCH_TIME_ANDROID,
+ EGL_LAST_COMPOSITION_START_TIME_ANDROID, and
+ EGL_DEQUEUE_READY_TIME_ANDROID.
diff --git a/opengl/specs/README b/opengl/specs/README
index 1ee99fb..8a3a7aa 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -22,9 +22,12 @@
0x314D EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps)
0x314E EGL_REQUESTED_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
0x314F EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3430 EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3431 EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3432 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3433 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3434 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3435 - 0x343F (unused)
+0x3150 EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3151 EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3152 EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3153 EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3154 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3155 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3156 EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3157 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3158 - 0x315F (unused)
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index d69a275..2b9c38e 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -24,6 +24,8 @@
namespace android {
+#define EGL_UNSIGNED_TRUE static_cast<EGLBoolean>(EGL_TRUE)
+
class EGLTest : public ::testing::Test {
protected:
EGLDisplay mEglDisplay;
@@ -48,7 +50,7 @@
virtual void TearDown() {
EGLBoolean success = eglTerminate(mEglDisplay);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
}
};
@@ -65,20 +67,20 @@
};
success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_GE(numConfigs, 1);
EGLint components[3];
success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
EXPECT_GE(components[0], 8);
@@ -139,23 +141,23 @@
};
success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_GE(numConfigs, 1);
EGLint components[4];
success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_ALPHA_SIZE, &components[3]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
EXPECT_GE(components[0], 8);
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
index a5a8968..523bc57 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
@@ -28,6 +28,7 @@
public static final int EGL_CONTEXT_MINOR_VERSION_KHR = 0x30FB;
public static final int EGL_CONTEXT_FLAGS_KHR = 0x30FC;
public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040;
+ public static final int EGL_RECORDABLE_ANDROID = 0x3142;
native private static void _nativeClassInit();
static {
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
index 04dd967..22b084a 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -1,7 +1,10 @@
cc_library_shared {
name: "libaudiomanager",
- srcs: ["IAudioManager.cpp"],
+ srcs: [
+ "IAudioManager.cpp",
+ "IPlayer.cpp",
+ ],
shared_libs: [
"libutils",
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index a41804f..b9b0706 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -35,15 +35,16 @@
{
}
- virtual audio_unique_id_t trackPlayer(int playerType, int usage, int content) {
+ virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
+ audio_content_type_t content, const sp<IBinder>& player) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
data.writeInt32(1); // non-null PlayerIdCard parcelable
// marshall PlayerIdCard data
data.writeInt32((int32_t) playerType);
// write attributes of PlayerIdCard
- data.writeInt32(usage);
- data.writeInt32(content);
+ data.writeInt32((int32_t) usage);
+ data.writeInt32((int32_t) content);
data.writeInt32(0 /*source: none here, this is a player*/);
data.writeInt32(0 /*flags*/);
// write attributes' tags
@@ -51,6 +52,8 @@
data.writeString16(String16("")); // no tags
// write attributes' bundle
data.writeInt32(-1977 /*ATTR_PARCEL_IS_NULL_BUNDLE*/); // no bundle
+ // write IPlayer
+ data.writeStrongBinder(player);
// get new PIId in reply
const status_t res = remote()->transact(TRACK_PLAYER, data, &reply, 0);
if (res != OK || reply.readExceptionCode() != 0) {
@@ -63,13 +66,14 @@
}
}
- virtual status_t playerAttributes(audio_unique_id_t piid, int usage, int content) {
+ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
+ audio_content_type_t content) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
- data.writeInt32(piid);
+ data.writeInt32((int32_t) piid);
data.writeInt32(1); // non-null AudioAttributes parcelable
- data.writeInt32(usage);
- data.writeInt32(content);
+ data.writeInt32((int32_t) usage);
+ data.writeInt32((int32_t) content);
data.writeInt32(0 /*source: none here, this is a player*/);
data.writeInt32(0 /*flags*/);
// write attributes' tags
@@ -80,18 +84,18 @@
return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual status_t playerEvent(int piid, int event) {
+ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
- data.writeInt32(piid);
- data.writeInt32(event);
+ data.writeInt32((int32_t) piid);
+ data.writeInt32((int32_t) event);
return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual status_t releasePlayer(int piid) {
+ virtual status_t releasePlayer(audio_unique_id_t piid) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
- data.writeInt32(piid);
+ data.writeInt32((int32_t) piid);
return remote()->transact(RELEASE_PLAYER, data, &reply, IBinder::FLAG_ONEWAY);
}
};
diff --git a/services/audiomanager/IPlayer.cpp b/services/audiomanager/IPlayer.cpp
new file mode 100644
index 0000000..3b0b4e9
--- /dev/null
+++ b/services/audiomanager/IPlayer.cpp
@@ -0,0 +1,109 @@
+/*
+**
+** 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.
+*/
+
+#define LOG_TAG "IPlayer"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <audiomanager/IPlayer.h>
+
+namespace android {
+
+enum {
+ START = IBinder::FIRST_CALL_TRANSACTION,
+ PAUSE = IBinder::FIRST_CALL_TRANSACTION + 1,
+ STOP = IBinder::FIRST_CALL_TRANSACTION + 2,
+ SET_VOLUME = IBinder::FIRST_CALL_TRANSACTION + 3,
+};
+
+class BpPlayer : public BpInterface<IPlayer>
+{
+public:
+ explicit BpPlayer(const sp<IBinder>& impl)
+ : BpInterface<IPlayer>(impl)
+ {
+ }
+
+ virtual void start()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+ remote()->transact(START, data, &reply);
+ }
+
+ virtual void pause()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+ remote()->transact(PAUSE, data, &reply);
+ }
+
+ virtual void stop()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+ remote()->transact(STOP, data, &reply);
+ }
+
+ virtual void setVolume(float vol)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+ data.writeFloat(vol);
+ remote()->transact(SET_VOLUME, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(Player, "android.media.IPlayer");
+
+// ----------------------------------------------------------------------
+
+status_t BnPlayer::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case START: {
+ CHECK_INTERFACE(IPlayer, data, reply);
+ start();
+ return NO_ERROR;
+ } break;
+ case PAUSE: {
+ CHECK_INTERFACE(IPlayer, data, reply);
+ pause();
+ return NO_ERROR;
+ }
+ case STOP: {
+ CHECK_INTERFACE(IPlayer, data, reply);
+ stop();
+ return NO_ERROR;
+ } break;
+ case SET_VOLUME: {
+ CHECK_INTERFACE(IPlayer, data, reply);
+ setVolume(data.readFloat());
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 793d59a..8771d45 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -51,7 +51,7 @@
#include <time.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/Trace.h>
#include <powermanager/PowerManager.h>
#include <ui/Region.h>
@@ -1224,15 +1224,8 @@
if (maskedAction == AMOTION_EVENT_ACTION_DOWN
&& (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
- int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
- if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
- outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- } else if (isWindowObscuredLocked(windowHandle)) {
- outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
- }
-
mTempTouchState.addOrUpdateWindow(
- windowHandle, outsideTargetFlags, BitSet32(0));
+ windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
}
}
}
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 6fe4d36..519faa6 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -20,7 +20,7 @@
#include "InputManager.h"
-#include <android/log.h>
+#include <log/log.h>
namespace android {
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index fbc7b12..c1e6365 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -52,7 +52,7 @@
#include <stdlib.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
index 297b068..5e82d75 100644
--- a/services/inputflinger/InputWindow.cpp
+++ b/services/inputflinger/InputWindow.cpp
@@ -19,7 +19,7 @@
#include "InputWindow.h"
-#include <android/log.h>
+#include <log/log.h>
#include <ui/Rect.h>
#include <ui/Region.h>
diff --git a/services/inputflinger/host/InputFlinger.cpp b/services/inputflinger/host/InputFlinger.cpp
index 8edea3f..f1d3726 100644
--- a/services/inputflinger/host/InputFlinger.cpp
+++ b/services/inputflinger/host/InputFlinger.cpp
@@ -20,10 +20,10 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
#include <binder/IPCThreadState.h>
#include <binder/PermissionCache.h>
#include <hardware/input.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include "InputFlinger.h"
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index f12320d..a7fe4f6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2078,6 +2078,25 @@
ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+ ASSERT_EQ(0, args.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
+ ASSERT_EQ(0, args.edgeFlags);
+ ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
// Button release. Should have same down time.
process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
@@ -2086,6 +2105,25 @@
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+ ASSERT_EQ(0, args.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(0, args.buttonState);
+ ASSERT_EQ(0, args.edgeFlags);
+ ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
ASSERT_EQ(0, args.flags);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
@@ -2140,10 +2178,20 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
// Button release.
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2167,6 +2215,12 @@
1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
// Move X, Y a bit while pressed.
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 2);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 1);
@@ -2181,6 +2235,11 @@
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2277,19 +2336,33 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2306,23 +2379,57 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
@@ -2336,19 +2443,35 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
@@ -2361,21 +2484,37 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -2386,21 +2525,37 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -2411,21 +2566,37 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -3312,11 +3483,19 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+
processKey(mapper, BTN_LEFT, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
processKey(mapper, BTN_RIGHT, 1);
@@ -3327,17 +3506,34 @@
ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ motionArgs.buttonState);
+
processKey(mapper, BTN_RIGHT, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
processKey(mapper, BTN_MIDDLE, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_BACK, release BTN_BACK
processKey(mapper, BTN_BACK, 1);
@@ -3345,15 +3541,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
processKey(mapper, BTN_BACK, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -3364,15 +3570,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
processKey(mapper, BTN_SIDE, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -3383,15 +3599,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
processKey(mapper, BTN_FORWARD, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -3402,44 +3628,72 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
processKey(mapper, BTN_EXTRA, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+
// press BTN_STYLUS, release BTN_STYLUS
processKey(mapper, BTN_STYLUS, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
processKey(mapper, BTN_STYLUS, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_STYLUS2, release BTN_STYLUS2
processKey(mapper, BTN_STYLUS2, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
processKey(mapper, BTN_STYLUS2, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// release touch
processUp(mapper);
@@ -4725,11 +4979,19 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+
processKey(mapper, BTN_LEFT, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
processKey(mapper, BTN_RIGHT, 1);
@@ -4740,17 +5002,34 @@
ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ motionArgs.buttonState);
+
processKey(mapper, BTN_RIGHT, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
processKey(mapper, BTN_MIDDLE, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_BACK, release BTN_BACK
processKey(mapper, BTN_BACK, 1);
@@ -4758,15 +5037,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
processKey(mapper, BTN_BACK, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4777,15 +5066,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
processKey(mapper, BTN_SIDE, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4796,15 +5095,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
processKey(mapper, BTN_FORWARD, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -4815,44 +5124,72 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
processKey(mapper, BTN_EXTRA, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+
// press BTN_STYLUS, release BTN_STYLUS
processKey(mapper, BTN_STYLUS, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
processKey(mapper, BTN_STYLUS, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_STYLUS2, release BTN_STYLUS2
processKey(mapper, BTN_STYLUS2, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
processKey(mapper, BTN_STYLUS2, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// release touch
processId(mapper, -1);
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 86af0ef..c41630a 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -10,6 +10,7 @@
OrientationSensor.cpp \
RecentEventLogger.cpp \
RotationVectorSensor.cpp \
+ SensorDirectConnection.cpp \
SensorEventConnection.cpp \
SensorFusion.cpp \
SensorInterface.cpp \
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 0245b26..41ad918 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -14,23 +14,27 @@
* limitations under the License.
*/
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Atomic.h>
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
+#include "SensorDevice.h"
+#include "SensorService.h"
+
#include <binder/BinderService.h>
#include <binder/Parcel.h>
#include <binder/IServiceManager.h>
-
+#include <cutils/ashmem.h>
#include <hardware/sensors.h>
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
-#include "SensorDevice.h"
-#include "SensorService.h"
+#include <inttypes.h>
+#include <math.h>
+#include <sys/mman.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sstream>
+#include <unistd.h>
namespace android {
// ---------------------------------------------------------------------------
@@ -386,7 +390,7 @@
void SensorDevice::disableAllSensors() {
Mutex::Autolock _l(mLock);
- for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ for (size_t i = 0; i< mActivationCount.size(); ++i) {
const Info& info = mActivationCount.valueAt(i);
// Check if this sensor has been activated previously and disable it.
if (info.batchParams.size() > 0) {
@@ -486,6 +490,29 @@
mDisabledClients.remove(ident);
}
+int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
+ Mutex::Autolock _l(mLock);
+
+ int32_t channelHandle = mSensorDevice->register_direct_channel(
+ mSensorDevice, memory, -1 /*channel_handle*/);
+ return channelHandle;
+}
+
+void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
+ Mutex::Autolock _l(mLock);
+
+ mSensorDevice->register_direct_channel(mSensorDevice, nullptr, channelHandle);
+}
+
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t *config) {
+ Mutex::Autolock _l(mLock);
+
+ int32_t ret = mSensorDevice->config_direct_report(
+ mSensorDevice, sensorHandle, channelHandle, config);
+ ALOGE_IF(ret < 0, "SensorDevice::configureDirectChannel ret %d", ret);
+ return ret;
+}
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 0bb0752..b6886a2 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -26,6 +26,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <string>
#ifdef ENABLE_TREBLE
#include <map>
@@ -57,6 +58,12 @@
status_t setDelay(void* ident, int handle, int64_t ns);
status_t flush(void* ident, int handle);
status_t setMode(uint32_t mode);
+
+ int32_t registerDirectChannel(const sensors_direct_mem_t *memory);
+ void unregisterDirectChannel(int32_t channelHandle);
+ int32_t configureDirectChannel(int32_t sensorHandle,
+ int32_t channelHandle, const struct sensors_direct_cfg_t *config);
+
void disableAllSensors();
void enableAllSensors();
void autoDisable(void *ident, int handle);
diff --git a/services/sensorservice/SensorDeviceTreble.cpp b/services/sensorservice/SensorDeviceTreble.cpp
index 6f946d0..0a75400 100644
--- a/services/sensorservice/SensorDeviceTreble.cpp
+++ b/services/sensorservice/SensorDeviceTreble.cpp
@@ -29,16 +29,9 @@
#include <sensors/convert.h>
-using android::hardware::sensors::V1_0::ISensors;
using android::hardware::hidl_vec;
-using Event = android::hardware::sensors::V1_0::Event;
-using SensorInfo = android::hardware::sensors::V1_0::SensorInfo;
-using SensorType = android::hardware::sensors::V1_0::SensorType;
-using DynamicSensorInfo = android::hardware::sensors::V1_0::DynamicSensorInfo;
-using SensorInfo = android::hardware::sensors::V1_0::SensorInfo;
-using Result = android::hardware::sensors::V1_0::Result;
-
+using namespace android::hardware::sensors::V1_0;
using namespace android::hardware::sensors::V1_0::implementation;
namespace android {
@@ -56,11 +49,13 @@
return PERMISSION_DENIED;
case Result::INVALID_OPERATION:
return INVALID_OPERATION;
+ case Result::NO_MEMORY:
+ return NO_MEMORY;
}
}
SensorDevice::SensorDevice() {
- mSensors = ISensors::getService("sensors");
+ mSensors = ISensors::getService();
if (mSensors == NULL) {
return;
@@ -79,8 +74,7 @@
mActivationCount.add(list[i].sensorHandle, model);
- /* auto result = */mSensors->activate(
- list[i].sensorHandle, 0 /* enabled */);
+ mSensors->activate(list[i].sensorHandle, 0 /* enabled */);
}
});
}
@@ -89,7 +83,7 @@
if (connected) {
Info model;
mActivationCount.add(handle, model);
- /* auto result = */mSensors->activate(handle, 0 /* enabled */);
+ mSensors->activate(handle, 0 /* enabled */);
} else {
mActivationCount.removeItem(handle);
}
@@ -98,13 +92,6 @@
std::string SensorDevice::dump() const {
if (mSensors == NULL) return "HAL not initialized\n";
-#if 0
- result.appendFormat("HAL: %s (%s), version %#010x\n",
- mSensorModule->common.name,
- mSensorModule->common.author,
- getHalDeviceVersion());
-#endif
-
String8 result;
mSensors->getSensorsList([&](const auto &list) {
const size_t count = list.size();
@@ -240,21 +227,17 @@
// This is the last connection, we need to de-activate the underlying h/w sensor.
actuateHardware = true;
} else {
- const int halVersion = getHalDeviceVersion();
- if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
- // Call batch for this sensor with the previously calculated best effort
- // batch_rate and timeout. One of the apps has unregistered for sensor
- // events, and the best effort batch parameters might have changed.
- ALOGD_IF(DEBUG_CONNECTIONS,
- "\t>>> actuating h/w batch %d %d %" PRId64 " %" PRId64, handle,
- info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
- info.bestBatchParams.batchTimeout);
- /* auto result = */mSensors->batch(
- handle,
- info.bestBatchParams.flags,
- info.bestBatchParams.batchDelay,
- info.bestBatchParams.batchTimeout);
- }
+ // Call batch for this sensor with the previously calculated best effort
+ // batch_rate and timeout. One of the apps has unregistered for sensor
+ // events, and the best effort batch parameters might have changed.
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "\t>>> actuating h/w batch %d %d %" PRId64 " %" PRId64, handle,
+ info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
+ mSensors->batch(
+ handle,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
}
} else {
// sensor wasn't enabled for this ident
@@ -278,13 +261,6 @@
}
}
- // On older devices which do not support batch, call setDelay().
- if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1 && info.numActiveClients() > 0) {
- ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w setDelay %d %" PRId64, handle,
- info.bestBatchParams.batchDelay);
- /* auto result = */mSensors->setDelay(
- handle, info.bestBatchParams.batchDelay);
- }
return err;
}
@@ -300,12 +276,6 @@
samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
}
- const int halVersion = getHalDeviceVersion();
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_1 && maxBatchReportLatencyNs != 0) {
- // Batch is not supported on older devices return invalid operation.
- return INVALID_OPERATION;
- }
-
ALOGD_IF(DEBUG_CONNECTIONS,
"SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64,
ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
@@ -334,21 +304,14 @@
status_t err(NO_ERROR);
// If the min period or min timeout has changed since the last batch call, call batch.
if (prevBestBatchParams != info.bestBatchParams) {
- if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
- ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %" PRId64 " %" PRId64, handle,
- info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
- info.bestBatchParams.batchTimeout);
- err = StatusFromResult(
- mSensors->batch(
- handle,
- info.bestBatchParams.flags,
- info.bestBatchParams.batchDelay,
- info.bestBatchParams.batchTimeout));
- } else {
- // For older devices which do not support batch, call setDelay() after activate() is
- // called. Some older devices may not support calling setDelay before activate(), so
- // call setDelay in SensorDevice::activate() method.
- }
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %" PRId64 " %" PRId64, handle,
+ info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
+ err = StatusFromResult(
+ mSensors->batch(
+ handle,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout));
if (err != NO_ERROR) {
ALOGE("sensor batch failed %p %d %d %" PRId64 " %" PRId64 " err=%s",
mSensors.get(), handle,
@@ -382,7 +345,7 @@
info.selectBatchParams();
return StatusFromResult(
- mSensors->setDelay(handle, info.bestBatchParams.batchDelay));
+ mSensors->batch(handle, info.bestBatchParams.batchDelay, 0));
}
int SensorDevice::getHalDeviceVersion() const {
@@ -391,9 +354,6 @@
}
status_t SensorDevice::flush(void* ident, int handle) {
- if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1) {
- return INVALID_OPERATION;
- }
if (isClientDisabled(ident)) return INVALID_OPERATION;
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
return StatusFromResult(mSensors->flush(handle));
@@ -412,7 +372,6 @@
Mutex::Autolock _l(mLock);
mDisabledClients.clear();
ALOGI("cleared mDisabledClients");
- const int halVersion = getHalDeviceVersion();
for (size_t i = 0; i< mActivationCount.size(); ++i) {
Info& info = mActivationCount.editValueAt(i);
if (info.batchParams.isEmpty()) continue;
@@ -420,29 +379,18 @@
const int sensor_handle = mActivationCount.keyAt(i);
ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
sensor_handle);
- status_t err(NO_ERROR);
- if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
- err = StatusFromResult(
- mSensors->batch(
- sensor_handle,
- info.bestBatchParams.flags,
- info.bestBatchParams.batchDelay,
- info.bestBatchParams.batchTimeout));
- ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
- }
+ status_t err = StatusFromResult(
+ mSensors->batch(
+ sensor_handle,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout));
+ ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
if (err == NO_ERROR) {
err = StatusFromResult(
mSensors->activate(sensor_handle, 1 /* enabled */));
ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
}
-
- if (halVersion <= SENSORS_DEVICE_API_VERSION_1_0) {
- err = StatusFromResult(
- mSensors->setDelay(
- sensor_handle, info.bestBatchParams.batchDelay));
- ALOGE_IF(err, "Error calling setDelay sensor %d (%s)", sensor_handle, strerror(-err));
- }
}
}
@@ -455,8 +403,7 @@
const int sensor_handle = mActivationCount.keyAt(i);
ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
sensor_handle);
- /* auto result = */mSensors->activate(
- sensor_handle, 0 /* enabled */);
+ mSensors->activate(sensor_handle, 0 /* enabled */);
// Add all the connections that were registered for this sensor to the disabled
// clients list.
@@ -478,10 +425,6 @@
injected_sensor_event->data[3], injected_sensor_event->data[4],
injected_sensor_event->data[5]);
- if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
- return INVALID_OPERATION;
- }
-
Event ev;
convertFromSensorEvent(*injected_sensor_event, &ev);
@@ -489,9 +432,6 @@
}
status_t SensorDevice::setMode(uint32_t mode) {
- if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
- return INVALID_OPERATION;
- }
return StatusFromResult(
mSensors->setOperationMode(
@@ -557,12 +497,96 @@
mDisabledClients.remove(ident);
}
+int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
+ Mutex::Autolock _l(mLock);
+
+ SharedMemType type;
+ switch (memory->type) {
+ case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+ type = SharedMemType::ASHMEM;
+ break;
+ case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+ type = SharedMemType::GRALLOC;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+
+ SharedMemFormat format;
+ if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
+ return BAD_VALUE;
+ }
+ format = SharedMemFormat::SENSORS_EVENT;
+
+ SharedMemInfo mem = {
+ .type = type,
+ .format = format,
+ .size = static_cast<uint32_t>(memory->size),
+ .memoryHandle = memory->handle,
+ };
+
+ int32_t ret;
+ mSensors->registerDirectChannel(mem,
+ [&ret](auto result, auto channelHandle) {
+ if (result == Result::OK) {
+ ret = channelHandle;
+ } else {
+ ret = StatusFromResult(result);
+ }
+ });
+ return ret;
+}
+
+void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
+ Mutex::Autolock _l(mLock);
+ mSensors->unregisterDirectChannel(channelHandle);
+}
+
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
+ int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
+ Mutex::Autolock _l(mLock);
+
+ RateLevel rate;
+ switch(config->rate_level) {
+ case SENSOR_DIRECT_RATE_STOP:
+ rate = RateLevel::STOP;
+ break;
+ case SENSOR_DIRECT_RATE_NORMAL:
+ rate = RateLevel::NORMAL;
+ break;
+ case SENSOR_DIRECT_RATE_FAST:
+ rate = RateLevel::FAST;
+ break;
+ case SENSOR_DIRECT_RATE_VERY_FAST:
+ rate = RateLevel::VERY_FAST;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+
+ int32_t ret;
+ mSensors->configDirectReport(sensorHandle, channelHandle, rate,
+ [&ret, rate] (auto result, auto token) {
+ if (rate == RateLevel::STOP) {
+ ret = StatusFromResult(result);
+ } else {
+ if (result == Result::OK) {
+ ret = token;
+ } else {
+ ret = StatusFromResult(result);
+ }
+ }
+ });
+
+ return ret;
+}
+
void SensorDevice::convertToSensorEvent(
const Event &src, sensors_event_t *dst) {
::android::hardware::sensors::V1_0::implementation::convertToSensorEvent(
src, dst);
- if (src.sensorType == SensorType::SENSOR_TYPE_DYNAMIC_SENSOR_META) {
+ if (src.sensorType == SensorType::DYNAMIC_SENSOR_META) {
const DynamicSensorInfo &dyn = src.u.dynamic;
dst->dynamic_sensor_meta.connected = dyn.connected;
@@ -606,4 +630,3 @@
// ---------------------------------------------------------------------------
}; // namespace android
-
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
new file mode 100644
index 0000000..662f320
--- /dev/null
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 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 "SensorDevice.h"
+#include "SensorDirectConnection.h"
+#include <hardware/sensors.h>
+
+#include <sys/stat.h>
+
+#define UNUSED(x) (void)(x)
+
+namespace android {
+
+SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
+ uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
+ const String16& opPackageName)
+ : mService(service), mUid(uid), mMem(*mem),
+ mHalChannelHandle(halChannelHandle),
+ mOpPackageName(opPackageName) {
+ ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
+}
+
+SensorService::SensorDirectConnection::~SensorDirectConnection() {
+ ALOGD_IF(DEBUG_CONNECTIONS, "~SensorDirectConnection %p", this);
+
+ stopAll();
+ mService->cleanupConnection(this);
+ if (mMem.handle != nullptr) {
+ native_handle_close(mMem.handle);
+ native_handle_delete(const_cast<struct native_handle*>(mMem.handle));
+ }
+}
+
+void SensorService::SensorDirectConnection::onFirstRef() {
+}
+
+void SensorService::SensorDirectConnection::dump(String8& result) const {
+ Mutex::Autolock _l(mConnectionLock);
+ result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n",
+ String8(mOpPackageName).string(), getHalChannelHandle(), mActivated.size());
+ for (auto &i : mActivated) {
+ result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second);
+ }
+}
+
+sp<BitTube> SensorService::SensorDirectConnection::getSensorChannel() const {
+ return nullptr;
+}
+
+status_t SensorService::SensorDirectConnection::enableDisable(
+ int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs,
+ int reservedFlags) {
+ // SensorDirectConnection does not support enableDisable, parameters not used
+ UNUSED(handle);
+ UNUSED(enabled);
+ UNUSED(samplingPeriodNs);
+ UNUSED(maxBatchReportLatencyNs);
+ UNUSED(reservedFlags);
+ return INVALID_OPERATION;
+}
+
+status_t SensorService::SensorDirectConnection::setEventRate(
+ int handle, nsecs_t samplingPeriodNs) {
+ // SensorDirectConnection does not support setEventRate, parameters not used
+ UNUSED(handle);
+ UNUSED(samplingPeriodNs);
+ return INVALID_OPERATION;
+}
+
+status_t SensorService::SensorDirectConnection::flush() {
+ // SensorDirectConnection does not support flush
+ return INVALID_OPERATION;
+}
+
+int32_t SensorService::SensorDirectConnection::configureChannel(int handle, int rateLevel) {
+
+ if (handle == -1 && rateLevel == SENSOR_DIRECT_RATE_STOP) {
+ stopAll();
+ return NO_ERROR;
+ }
+
+ if (mService->isOperationRestricted(mOpPackageName)) {
+ return PERMISSION_DENIED;
+ }
+
+ sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ if (si == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+
+ const Sensor& s = si->getSensor();
+ if (!SensorService::canAccessSensor(s, "config direct channel", mOpPackageName)) {
+ return PERMISSION_DENIED;
+ }
+
+ if (s.getHighestDirectReportRateLevel() == 0
+ || rateLevel > s.getHighestDirectReportRateLevel()
+ || !s.isDirectChannelTypeSupported(mMem.type)) {
+ return INVALID_OPERATION;
+ }
+
+ struct sensors_direct_cfg_t config = {
+ .rate_level = rateLevel
+ };
+
+ Mutex::Autolock _l(mConnectionLock);
+ SensorDevice& dev(SensorDevice::getInstance());
+ int ret = dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+
+ if (rateLevel == SENSOR_DIRECT_RATE_STOP) {
+ if (ret == NO_ERROR) {
+ mActivated.erase(handle);
+ } else if (ret > 0) {
+ ret = UNKNOWN_ERROR;
+ }
+ } else {
+ if (ret > 0) {
+ mActivated[handle] = rateLevel;
+ }
+ }
+
+ return ret;
+}
+
+void SensorService::SensorDirectConnection::stopAll(bool backupRecord) {
+
+ struct sensors_direct_cfg_t config = {
+ .rate_level = SENSOR_DIRECT_RATE_STOP
+ };
+
+ Mutex::Autolock _l(mConnectionLock);
+ SensorDevice& dev(SensorDevice::getInstance());
+ for (auto &i : mActivated) {
+ dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+ }
+
+ if (backupRecord && mActivatedBackup.empty()) {
+ mActivatedBackup = mActivated;
+ }
+ mActivated.clear();
+}
+
+void SensorService::SensorDirectConnection::recoverAll() {
+ stopAll(false);
+
+ Mutex::Autolock _l(mConnectionLock);
+ SensorDevice& dev(SensorDevice::getInstance());
+
+ // recover list of report from backup
+ mActivated = mActivatedBackup;
+ mActivatedBackup.clear();
+
+ // re-enable them
+ for (auto &i : mActivated) {
+ struct sensors_direct_cfg_t config = {
+ .rate_level = i.second
+ };
+ dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+ }
+}
+
+int32_t SensorService::SensorDirectConnection::getHalChannelHandle() const {
+ return mHalChannelHandle;
+}
+
+bool SensorService::SensorDirectConnection::isEquivalent(const sensors_direct_mem_t *mem) const {
+ bool ret = false;
+
+ if (mMem.type == mem->type) {
+ switch (mMem.type) {
+ case SENSOR_DIRECT_MEM_TYPE_ASHMEM: {
+ struct stat s1, s2;
+ int fd1, fd2;
+ fd1 = mMem.handle->data[0];
+ fd2 = mem->handle->data[1];
+ if (fstat(fd1, &s1) < 0 || fstat(fd2, &s2) < 0 || s1.st_ino == s2.st_ino) {
+ ret = true;
+ }
+ }
+ case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+ LOG_FATAL("%s: Implement GRALLOC or remove", __FUNCTION__);
+ ret = true;
+ default:
+ ALOGE("Unexpected mem type %d", mMem.type);
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+} // namespace android
+
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
new file mode 100644
index 0000000..692ef0d
--- /dev/null
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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_SENSOR_DIRECT_CONNECTION_H
+#define ANDROID_SENSOR_DIRECT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/BinderService.h>
+
+#include <gui/Sensor.h>
+#include <gui/BitTube.h>
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+
+#include "SensorService.h"
+
+namespace android {
+
+class SensorService;
+class BitTube;
+
+class SensorService::SensorDirectConnection: public BnSensorEventConnection {
+public:
+ SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
+ const sensors_direct_mem_t *mem, int32_t halChannelHandle,
+ const String16& opPackageName);
+ void dump(String8& result) const;
+ uid_t getUid() const { return mUid; }
+ int32_t getHalChannelHandle() const;
+ bool isEquivalent(const sensors_direct_mem_t *mem) const;
+
+ // stop all active sensor report. if backupRecord is set to false,
+ // those report can be recovered by recoverAll
+ // called by SensorService when enter restricted mode
+ void stopAll(bool clearRecord = false);
+
+ // recover sensor reports previously stopped by stopAll(true)
+ // called by SensorService when return to NORMAL mode.
+ void recoverAll();
+
+protected:
+ virtual ~SensorDirectConnection();
+ // ISensorEventConnection functions
+ virtual void onFirstRef();
+ virtual sp<BitTube> getSensorChannel() const;
+ virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
+ nsecs_t maxBatchReportLatencyNs, int reservedFlags);
+ virtual status_t setEventRate(int handle, nsecs_t samplingPeriodNs);
+ virtual status_t flush();
+ virtual int32_t configureChannel(int handle, int rateLevel);
+
+private:
+ const sp<SensorService> mService;
+ const uid_t mUid;
+ const sensors_direct_mem_t mMem;
+ const int32_t mHalChannelHandle;
+ const String16 mOpPackageName;
+
+ mutable Mutex mConnectionLock;
+ std::unordered_map<int, int> mActivated;
+ std::unordered_map<int, int> mActivatedBackup;
+};
+
+} // namepsace android
+
+#endif // ANDROID_SENSOR_DIRECT_CONNECTION_H
+
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index f2f1444..d84d36e 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -23,6 +23,8 @@
#include "SensorEventConnection.h"
#include "SensorDevice.h"
+#define UNUSED(x) (void)(x)
+
namespace android {
SensorService::SensorEventConnection::SensorEventConnection(
@@ -524,6 +526,13 @@
return mService->flushSensor(this, mOpPackageName);
}
+int32_t SensorService::SensorEventConnection::configureChannel(int handle, int rateLevel) {
+ // SensorEventConnection does not support configureChannel, parameters not used
+ UNUSED(handle);
+ UNUSED(rateLevel);
+ return INVALID_OPERATION;
+}
+
int SensorService::SensorEventConnection::handleEvent(int fd, int events, void* /*data*/) {
if (events & ALOOPER_EVENT_HANGUP || events & ALOOPER_EVENT_ERROR) {
{
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 883c16e..cd81ddd 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -74,6 +74,8 @@
nsecs_t maxBatchReportLatencyNs, int reservedFlags);
virtual status_t setEventRate(int handle, nsecs_t samplingPeriodNs);
virtual status_t flush();
+ virtual int32_t configureChannel(int handle, int rateLevel);
+
// Count the number of flush complete events which are about to be dropped in the buffer.
// Increment mPendingFlushEventsToSend in mSensorInfo. These flush complete events will be sent
// separately before the next batch of events.
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index e0101c1..31c8251 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -124,7 +124,7 @@
forEachSensor([&result] (const Sensor& s) -> bool {
result.appendFormat(
"%#010x) %-25s | %-15s | ver: %" PRId32 " | type: %20s(%" PRId32
- ") | perm: %s\n\t",
+ ") | perm: %s\n",
s.getHandle(),
s.getName().string(),
s.getVendor().string(),
@@ -133,17 +133,18 @@
s.getType(),
s.getRequiredPermission().size() ? s.getRequiredPermission().string() : "n/a");
+ result.append("\t");
const int reportingMode = s.getReportingMode();
if (reportingMode == AREPORTING_MODE_CONTINUOUS) {
- result.append(" continuous | ");
+ result.append("continuous | ");
} else if (reportingMode == AREPORTING_MODE_ON_CHANGE) {
- result.append(" on-change | ");
+ result.append("on-change | ");
} else if (reportingMode == AREPORTING_MODE_ONE_SHOT) {
- result.append(" one-shot | ");
+ result.append("one-shot | ");
} else if (reportingMode == AREPORTING_MODE_SPECIAL_TRIGGER) {
- result.append(" special-trigger | ");
+ result.append("special-trigger | ");
} else {
- result.append(" unknown-mode | ");
+ result.append("unknown-mode | ");
}
if (s.getMaxDelay() > 0) {
@@ -178,8 +179,19 @@
if (s.hasAdditionalInfo()) {
result.appendFormat("has-additional-info, ");
}
-
result.append("\n");
+
+ if (s.getHighestDirectReportRateLevel() > SENSOR_DIRECT_RATE_STOP) {
+ result.appendFormat("\thighest rate level = %d, support shared mem: ",
+ s.getHighestDirectReportRateLevel());
+ if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_ASHMEM)) {
+ result.append("ashmem, ");
+ }
+ if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC)) {
+ result.append("gralloc, ");
+ }
+ result.append("\n");
+ }
return true;
});
return std::string(result.string());
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 2e44736..143a3c5 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -21,6 +21,7 @@
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
+#include <cutils/ashmem.h>
#include <gui/SensorEventQueue.h>
#include <hardware/sensors.h>
@@ -40,6 +41,7 @@
#include "SensorInterface.h"
#include "SensorService.h"
+#include "SensorDirectConnection.h"
#include "SensorEventAckReceiver.h"
#include "SensorEventConnection.h"
#include "SensorRecord.h"
@@ -337,7 +339,16 @@
if (mCurrentOperatingMode != NORMAL) {
return INVALID_OPERATION;
}
+
mCurrentOperatingMode = RESTRICTED;
+ // temporarily stop all sensor direct report
+ for (auto &i : mDirectConnections) {
+ sp<SensorDirectConnection> connection(i.promote());
+ if (connection != nullptr) {
+ connection->stopAll(true /* backupRecord */);
+ }
+ }
+
dev.disableAllSensors();
// Clear all pending flush connections for all active sensors. If one of the active
// connections has called flush() and the underlying sensor has been disabled before a
@@ -352,6 +363,13 @@
if (mCurrentOperatingMode == RESTRICTED) {
mCurrentOperatingMode = NORMAL;
dev.enableAllSensors();
+ // recover all sensor direct report
+ for (auto &i : mDirectConnections) {
+ sp<SensorDirectConnection> connection(i.promote());
+ if (connection != nullptr) {
+ connection->recoverAll();
+ }
+ }
}
if (mCurrentOperatingMode == DATA_INJECTION) {
resetToNormalModeLocked();
@@ -430,8 +448,8 @@
case DATA_INJECTION:
result.appendFormat(" DATA_INJECTION : %s\n", mWhiteListedPackage.string());
}
- result.appendFormat("%zd active connections\n", mActiveConnections.size());
+ result.appendFormat("%zd active connections\n", mActiveConnections.size());
for (size_t i=0 ; i < mActiveConnections.size() ; i++) {
sp<SensorEventConnection> connection(mActiveConnections[i].promote());
if (connection != 0) {
@@ -440,6 +458,15 @@
}
}
+ result.appendFormat("%zd direct connections\n", mDirectConnections.size());
+ for (size_t i = 0 ; i < mDirectConnections.size() ; i++) {
+ sp<SensorDirectConnection> connection(mDirectConnections[i].promote());
+ if (connection != nullptr) {
+ result.appendFormat("Direct connection %zu:\n", i);
+ connection->dump(result);
+ }
+ }
+
result.appendFormat("Previous Registrations:\n");
// Log in the reverse chronological order.
int currentIndex = (mNextSensorRegIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
@@ -936,6 +963,85 @@
return (mCurrentOperatingMode == DATA_INJECTION);
}
+sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
+ const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
+ const native_handle *resource) {
+ Mutex::Autolock _l(mLock);
+
+ struct sensors_direct_mem_t mem = {
+ .type = type,
+ .format = format,
+ .size = size,
+ .handle = resource,
+ };
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+
+ if (mem.handle == nullptr) {
+ ALOGE("Failed to clone resource handle");
+ return nullptr;
+ }
+
+ // check format
+ if (format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
+ ALOGE("Direct channel format %d is unsupported!", format);
+ return nullptr;
+ }
+
+ // check for duplication
+ for (auto &i : mDirectConnections) {
+ sp<SensorDirectConnection> connection(i.promote());
+ if (connection != nullptr && connection->isEquivalent(&mem)) {
+ return nullptr;
+ }
+ }
+
+ // check specific to memory type
+ switch(type) {
+ case SENSOR_DIRECT_MEM_TYPE_ASHMEM: { // channel backed by ashmem
+ int fd = resource->data[0];
+ int size2 = ashmem_get_size_region(fd);
+ // check size consistency
+ if (size2 != static_cast<int>(size)) {
+ ALOGE("Ashmem direct channel size mismatch, %" PRIu32 " vs %d", size, size2);
+ return nullptr;
+ }
+ break;
+ }
+ case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+ LOG_FATAL("%s: Finish implementation of ION and GRALLOC or remove", __FUNCTION__);
+ break;
+ default:
+ ALOGE("Unknown direct connection memory type %d", type);
+ return nullptr;
+ }
+
+ native_handle_t *clone = native_handle_clone(resource);
+ if (!clone) {
+ return nullptr;
+ }
+
+ SensorDirectConnection* conn = nullptr;
+ SensorDevice& dev(SensorDevice::getInstance());
+ int channelHandle = dev.registerDirectChannel(&mem);
+
+ if (channelHandle <= 0) {
+ ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
+ } else {
+ mem.handle = clone;
+ conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName);
+ }
+
+ if (conn == nullptr) {
+ native_handle_close(clone);
+ native_handle_delete(clone);
+ } else {
+ // add to list of direct connections
+ // sensor service should never hold pointer or sp of SensorDirectConnection object.
+ mDirectConnections.add(wp<SensorDirectConnection>(conn));
+ }
+ return conn;
+}
+
status_t SensorService::resetToNormalMode() {
Mutex::Autolock _l(mLock);
return resetToNormalModeLocked();
@@ -995,11 +1101,18 @@
dev.notifyConnectionDestroyed(c);
}
+void SensorService::cleanupConnection(SensorDirectConnection* c) {
+ Mutex::Autolock _l(mLock);
+
+ SensorDevice& dev(SensorDevice::getInstance());
+ dev.unregisterDirectChannel(c->getHalChannelHandle());
+ mDirectConnections.remove(c);
+}
+
sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
return mSensors.getInterface(handle);
}
-
status_t SensorService::enable(const sp<SensorEventConnection>& connection,
int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,
const String16& opPackageName) {
@@ -1013,7 +1126,7 @@
}
Mutex::Autolock _l(mLock);
- if ((mCurrentOperatingMode == RESTRICTED || mCurrentOperatingMode == DATA_INJECTION)
+ if (mCurrentOperatingMode != NORMAL
&& !isWhiteListedPackage(connection->getPackageName())) {
return INVALID_OPERATION;
}
@@ -1331,5 +1444,14 @@
return (packageName.contains(mWhiteListedPackage.string()));
}
+bool SensorService::isOperationRestricted(const String16& opPackageName) {
+ Mutex::Autolock _l(mLock);
+ if (mCurrentOperatingMode != RESTRICTED) {
+ String8 package(opPackageName);
+ return !isWhiteListedPackage(package);
+ }
+ return false;
+}
+
}; // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index e969d8a..eeedd4a 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -67,9 +67,11 @@
{
// nested class/struct for internal use
class SensorEventConnection;
+ class SensorDirectConnection;
public:
void cleanupConnection(SensorEventConnection* connection);
+ void cleanupConnection(SensorDirectConnection* c);
status_t enable(const sp<SensorEventConnection>& connection, int handle,
nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,
@@ -154,6 +156,8 @@
const String8& packageName,
int requestedMode, const String16& opPackageName);
virtual int isDataInjectionEnabled();
+ virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+ uint32_t size, int32_t type, int32_t format, const native_handle *resource);
virtual status_t dump(int fd, const Vector<String16>& args);
String8 getSensorName(int handle) const;
@@ -203,6 +207,7 @@
// allowed to register for or call flush on sensors. Typically only cts test packages are
// allowed.
bool isWhiteListedPackage(const String8& packageName);
+ bool isOperationRestricted(const String16& opPackageName);
// Reset the state of SensorService to NORMAL mode.
status_t resetToNormalMode();
@@ -239,6 +244,7 @@
sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
wp<const SensorEventConnection> * mMapFlushEventsToConnections;
std::unordered_map<int, RecentEventLogger*> mRecentEvent;
+ SortedVector< wp<SensorDirectConnection> > mDirectConnections;
Mode mCurrentOperatingMode;
// This packagaName is set when SensorService is in RESTRICTED or DATA_INJECTION mode. Only
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 4987a18..fcc9241 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -69,10 +69,6 @@
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
endif
-ifeq ($(TARGET_DISABLE_TRIPLE_BUFFERING),true)
- LOCAL_CFLAGS += -DTARGET_DISABLE_TRIPLE_BUFFERING
-endif
-
ifeq ($(TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS),true)
LOCAL_CFLAGS += -DFORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS
endif
diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h
index f2e6491..d56b1c8 100644
--- a/services/surfaceflinger/Colorizer.h
+++ b/services/surfaceflinger/Colorizer.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_SURFACE_FLINGER_COLORIZER_H
#define ANDROID_SURFACE_FLINGER_COLORIZER_H
+#include <utils/String8.h>
+
namespace android {
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DdmConnection.cpp b/services/surfaceflinger/DdmConnection.cpp
index ba09b36..35d55f5 100644
--- a/services/surfaceflinger/DdmConnection.cpp
+++ b/services/surfaceflinger/DdmConnection.cpp
@@ -18,7 +18,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android/log.h>
+#include <log/log.h>
#include "jni.h"
#include "DdmConnection.h"
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 3229b66..86cf17d 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -24,7 +24,7 @@
#include <algorithm>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/String8.h>
#include <utils/Thread.h>
#include <utils/Trace.h>
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index b952fd7..2f2f3a9 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -723,7 +723,7 @@
bool CommandReader::parseSelectDisplay(uint16_t length)
{
- if (length != CommandWriter::kSelectDisplayLength) {
+ if (length != CommandWriterBase::kSelectDisplayLength) {
return false;
}
@@ -734,7 +734,7 @@
bool CommandReader::parseSetError(uint16_t length)
{
- if (length != CommandWriter::kSetErrorLength) {
+ if (length != CommandWriterBase::kSetErrorLength) {
return false;
}
@@ -796,7 +796,7 @@
bool CommandReader::parseSetPresentFence(uint16_t length)
{
- if (length != CommandWriter::kSetPresentFenceLength ||
+ if (length != CommandWriterBase::kSetPresentFenceLength ||
!mCurrentReturnData) {
return false;
}
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 6e42ba0..5ce5869 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -47,7 +47,7 @@
using android::hardware::graphics::composer::V2_1::Layer;
using android::hardware::graphics::composer::V2_1::Config;
-using android::hardware::graphics::composer::V2_1::CommandWriter;
+using android::hardware::graphics::composer::V2_1::CommandWriterBase;
using android::hardware::graphics::composer::V2_1::CommandReaderBase;
using android::hardware::kSynchronizedReadWrite;
@@ -228,7 +228,7 @@
// 64KiB minus a small space for metadata such as read/write pointers
static constexpr size_t kWriterInitialSize =
64 * 1024 / sizeof(uint32_t) - 16;
- CommandWriter mWriter;
+ CommandWriterBase mWriter;
CommandReader mReader;
};
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 96dd55f..1998edf 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -24,8 +24,8 @@
#include <stdlib.h>
#include <string.h>
-#include <android/log.h>
#include <utils/String8.h>
+#include <log/log.h>
#include <ui/Rect.h>
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 1502eeb..c89ca83 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -929,7 +929,7 @@
Error Display::present(sp<Fence>* outPresentFence)
{
- int32_t presentFenceFd = 0;
+ int32_t presentFenceFd = -1;
#ifdef BYPASS_IHWC
int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
&presentFenceFd);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index 51e92b2..7322c13 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -28,8 +28,8 @@
#include <cstdlib>
#include <sstream>
-#include <android/log.h>
#include <hardware/hwcomposer.h>
+#include <log/log.h>
#include <utils/Trace.h>
using namespace std::chrono_literals;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3f38c86..7914770 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -43,8 +43,8 @@
#include <android/configuration.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include "HWComposer.h"
#include "HWC2On1Adapter.h"
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index af280a4..cc5578d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -39,8 +39,8 @@
#include <android/configuration.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <system/graphics.h>
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 24948c8..b388448 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -166,9 +166,9 @@
mSurfaceFlingerConsumer->setContentsChangedListener(this);
mSurfaceFlingerConsumer->setName(mName);
-#ifndef TARGET_DISABLE_TRIPLE_BUFFERING
- mProducer->setMaxDequeuedBufferCount(2);
-#endif
+ if (mFlinger->isLayerTripleBufferingDisabled()) {
+ mProducer->setMaxDequeuedBufferCount(2);
+ }
const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
updateTransformHint(hw);
@@ -1815,7 +1815,7 @@
}
#ifdef USE_HWC2
-void Layer::releasePendingBuffer() {
+void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
mSurfaceFlingerConsumer->releasePendingBuffer();
auto releaseFenceTime = std::make_shared<FenceTime>(
mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
@@ -1823,7 +1823,7 @@
Mutex::Autolock lock(mFrameEventHistoryMutex);
mFrameEventHistory.addRelease(
- mPreviousFrameNumber, std::move(releaseFenceTime));
+ mPreviousFrameNumber, dequeueReadyTime, std::move(releaseFenceTime));
}
#endif
@@ -2000,7 +2000,7 @@
mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
mReleaseTimeline.push(releaseFenceTime);
mFrameEventHistory.addRelease(
- mPreviousFrameNumber, std::move(releaseFenceTime));
+ mPreviousFrameNumber, latchTime, std::move(releaseFenceTime));
#endif
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 801b9d0..1654a51 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -122,9 +122,11 @@
int32_t sequence; // changes when visible regions can change
bool modified;
+ // Crop is expressed in layer space coordinate.
Rect crop;
Rect requestedCrop;
+ // finalCrop is expressed in display space coordinate.
Rect finalCrop;
// If set, defers this state update until the Layer identified by handle
@@ -286,7 +288,7 @@
#ifdef USE_HWC2
// If a buffer was replaced this frame, release the former buffer
- void releasePendingBuffer();
+ void releasePendingBuffer(nsecs_t dequeueReadyTime);
#endif
/*
diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/services/surfaceflinger/RenderEngine/Program.cpp
index 38a0039..48a8da5 100644
--- a/services/surfaceflinger/RenderEngine/Program.cpp
+++ b/services/surfaceflinger/RenderEngine/Program.cpp
@@ -16,7 +16,7 @@
#include <stdint.h>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/String8.h>
#include "Program.h"
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 986c6df..9909bf9 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <android/log.h>
+#include <log/log.h>
#include <ui/Rect.h>
#include <ui/Region.h>
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f096134..5f25ef5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -28,8 +28,8 @@
#include <EGL/egl.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -189,6 +189,10 @@
property_get("debug.sf.disable_hwc_vds", value, "0");
mUseHwcVirtualDisplays = !atoi(value);
ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+
+ property_get("ro.sf.disable_triple_buffer", value, "0");
+ mLayerTripleBufferingDisabled = !atoi(value);
+ ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
}
void SurfaceFlinger::onFirstRef()
@@ -571,10 +575,13 @@
*outSupported = {
FrameEvent::REQUESTED_PRESENT,
FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
FrameEvent::GL_COMPOSITION_DONE,
getHwComposer().presentFenceRepresentsStartOfScanout() ?
FrameEvent::DISPLAY_PRESENT : FrameEvent::DISPLAY_RETIRE,
+ FrameEvent::DEQUEUE_READY,
FrameEvent::RELEASE,
};
return NO_ERROR;
@@ -1222,8 +1229,9 @@
ALOGV("postComposition");
// Release any buffers which were replaced this frame
+ nsecs_t dequeueReadyTime = systemTime();
for (auto& layer : mLayersWithQueuedFrames) {
- layer->releasePendingBuffer();
+ layer->releasePendingBuffer(dequeueReadyTime);
}
const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
@@ -2952,21 +2960,18 @@
mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
}
-/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
+void SurfaceFlinger::appendSfConfigString(String8& result) const
{
- static const char* config =
- " [sf"
+ result.append(" [sf");
#ifdef HAS_CONTEXT_PRIORITY
- " HAS_CONTEXT_PRIORITY"
+ result.append(" HAS_CONTEXT_PRIORITY");
#endif
#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
- " NEVER_DEFAULT_TO_ASYNC_MODE"
+ result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
#endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
- " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
- "]";
- result.append(config);
+ if (isLayerTripleBufferingDisabled())
+ result.append(" DISABLE_TRIPLE_BUFFERING");
+ result.append("]");
}
void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 2d6f9f6..f7f9ef5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -438,7 +438,7 @@
void clearStatsLocked(const Vector<String16>& args, size_t& index, String8& result);
void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const;
bool startDdmConnection();
- static void appendSfConfigString(String8& result);
+ void appendSfConfigString(String8& result) const;
void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
const sp<const DisplayDevice>& hw,
uint32_t minLayerZ, uint32_t maxLayerZ);
@@ -453,6 +453,9 @@
std::vector<OccupancyTracker::Segment>&& history);
void dumpBufferingStats(String8& result) const;
+ bool isLayerTripleBufferingDisabled() const {
+ return this->mLayerTripleBufferingDisabled;
+ }
/* ------------------------------------------------------------------------
* Attributes
*/
@@ -526,6 +529,9 @@
SurfaceInterceptor mInterceptor;
bool mUseHwcVirtualDisplays = true;
+ // Restrict layers to use two buffers in their bufferqueues.
+ bool mLayerTripleBufferingDisabled = false;
+
// these are thread safe
mutable MessageQueue mEventQueue;
FrameTracker mAnimFrameTracker;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 76680e0..fcf0185 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -16,19 +16,20 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <dlfcn.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdatomic.h>
#include <stdint.h>
#include <sys/types.h>
-#include <errno.h>
-#include <math.h>
+
#include <mutex>
-#include <dlfcn.h>
-#include <inttypes.h>
-#include <stdatomic.h>
#include <EGL/egl.h>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -164,7 +165,6 @@
{
ALOGI("SurfaceFlinger is starting");
- // debugging stuff...
char value[PROPERTY_VALUE_MAX];
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
@@ -187,6 +187,10 @@
property_get("debug.sf.disable_hwc_vds", value, "0");
mUseHwcVirtualDisplays = !atoi(value);
ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+
+ property_get("ro.sf.disable_triple_buffer", value, "0");
+ mLayerTripleBufferingDisabled = !atoi(value);
+ ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
}
void SurfaceFlinger::onFirstRef()
@@ -604,9 +608,12 @@
*outSupported = {
FrameEvent::REQUESTED_PRESENT,
FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
FrameEvent::GL_COMPOSITION_DONE,
FrameEvent::DISPLAY_RETIRE,
+ FrameEvent::DEQUEUE_READY,
FrameEvent::RELEASE,
};
return NO_ERROR;
@@ -2859,21 +2866,18 @@
mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
}
-/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
+void SurfaceFlinger::appendSfConfigString(String8& result) const
{
- static const char* config =
- " [sf"
+ result.append(" [sf");
#ifdef HAS_CONTEXT_PRIORITY
- " HAS_CONTEXT_PRIORITY"
+ result.append(" HAS_CONTEXT_PRIORITY");
#endif
#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
- " NEVER_DEFAULT_TO_ASYNC_MODE"
+ result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
#endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
- " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
- "]";
- result.append(config);
+ if (isLayerTripleBufferingDisabled())
+ result.append(" DISABLE_TRIPLE_BUFFERING");
+ result.append("]");
}
void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index a12276a..60bc7f3 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -21,14 +21,12 @@
#include "SurfaceFlinger.h"
#include "SurfaceInterceptor.h"
-#include <android-base/file.h>
-
-#include <cutils/log.h>
-
-#include <utils/Trace.h>
-
#include <fstream>
+#include <android-base/file.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
namespace android {
// ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 4695138..9af6e61 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -21,6 +21,9 @@
#include <mutex>
+#include <utils/SortedVector.h>
+#include <utils/Vector.h>
+
namespace android {
class BufferItem;
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 1264a4e..93b90d0 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -81,6 +81,9 @@
@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME "VK_ANDROID_native_buffer"
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_NAME "VK_GOOGLE_display_timing"
+
@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION 4
@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME "VK_EXT_debug_report"
@@ -147,6 +150,9 @@
@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
+@extension("VK_KHR_swapchain_front_buffered") define VK_KHR_SWAPCHAIN_FRONT_BUFFERED_SPEC_VERSION 2
+@extension("VK_KHR_swapchain_front_buffered") define VK_KHR_SWAPCHAIN_FRONT_BUFFERED_EXTENSION_NAME "VK_KHR_swapchain_front_buffered"
+
/////////////
@@ -753,6 +759,9 @@
VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID = 1000010000,
VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID = 1000010001,
+ //@extension("VK_GOOGLE_display_timing")
+ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000,
+
//@extension("VK_EXT_debug_report")
VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000,
@@ -918,6 +927,10 @@
VK_PRESENT_MODE_MAILBOX_KHR = 0x00000001,
VK_PRESENT_MODE_FIFO_KHR = 0x00000002,
VK_PRESENT_MODE_FIFO_RELAXED_KHR = 0x00000003,
+ //@extension("VK_KHR_swapchain_front_buffered")
+ VK_PRESENT_MODE_FRONT_BUFFERED_DEMAND_REFRESH_KHR = 1000111000,
+ //@extension("VK_KHR_swapchain_front_buffered")
+ VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR = 1000111001,
}
@extension("VK_KHR_surface")
@@ -2880,27 +2893,6 @@
platform.HWND hwnd
}
-@extension("VK_KHR_incremental_present")
-class VkRectLayerKHR {
- VkOffset2D offset
- VkExtent2D extent
- uint32_t layer
-}
-
-@extension("VK_KHR_incremental_present")
-class VkPresentRegionKHR {
- uint32_t rectangleCount
- const VkRectLayerKHR* pRectangles
-}
-
-@extension("VK_KHR_incremental_present")
-class VkPresentRegionsKHR {
- VkStructureType sType
- const void* pNext
- uint32_t swapchainCount
- const VkPresentRegionKHR* pRegions
-}
-
@extension("VK_ANDROID_native_buffer")
class VkNativeBufferANDROID {
VkStructureType sType
@@ -2915,7 +2907,36 @@
class VkSwapchainImageCreateInfoANDROID {
VkStructureType sType
const void* pNext
- VkSwapchainImageUsageFlagBitsANDROID flags
+ VkSwapchainImageUsageFlagsANDROID flags
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkRefreshCycleDurationGOOGLE {
+ u64 minRefreshDuration
+ u64 maxRefreshDuration
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPastPresentationTimingGOOGLE {
+ u32 presentID
+ u64 desiredPresentTime
+ u64 actualPresentTime
+ u64 earliestPresentTime
+ u64 presentMargin
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPresentTimeGOOGLE {
+ u32 presentID
+ u64 desiredPresentTime
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPresentTimesInfoGOOGLE {
+ VkStructureType sType
+ const void* pNext
+ u32 swapchainCount
+ const VkPresentTimeGOOGLE* pTimes
}
@extension("VK_EXT_debug_report")
@@ -5835,6 +5856,29 @@
return ?
}
+@extension("VK_GOOGLE_display_timing")
+cmd VkResult vkGetRefreshCycleDurationGOOGLE(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ deviceObject := GetDevice(device)
+ swapchainObject := GetSwapchain(swapchain)
+
+ displayTimingProperties := ?
+ pDisplayTimingProperties[0] = displayTimingProperties
+
+ return ?
+}
+
+@extension("VK_GOOGLE_display_timing")
+cmd VkResult vkGetPastPresentationTimingGOOGLE(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ u32* pPresentationTimingCount,
+ VkPastPresentationTimingGOOGLE* pPresentationTimings) {
+ return ?
+}
+
@extension("VK_EXT_debug_report")
@external type void* PFN_vkDebugReportCallbackEXT
@extension("VK_EXT_debug_report")
@@ -6072,6 +6116,13 @@
VkDeviceGeneratedCommandsLimitsNVX* pLimits) {
}
+@extension("VK_KHR_swapchain_front_buffered")
+cmd VkResult vkGetSwapchainStatusKHR(
+ VkDevice device,
+ VkSwapchainKHR swapchain) {
+ return ?
+}
+
////////////////
// Validation //
diff --git a/vulkan/doc/implementors_guide/implementors_guide.adoc b/vulkan/doc/implementors_guide/implementors_guide.adoc
index 7ace777..dc18e9d 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.adoc
+++ b/vulkan/doc/implementors_guide/implementors_guide.adoc
@@ -59,6 +59,28 @@
----
The +format+ and +imageUsage+ parameters are taken from the +VkSwapchainCreateInfoKHR+ structure. The driver should fill +*grallocUsage+ with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.
+Implementations may further need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on +format+ and +imageUsage+, but also on the intended usage of the swapchain. The additional usage bits are defined as
+[source,c]
+----
+typedef enum VkSwapchainImageUsageFlagBitsANDROID {
+ VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID = 0x00000001,
+ VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSwapchainImageUsageFlagBitsANDROID;
+typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
+----
+
+If the driver provides the +vkGetSwapchainGrallocUsage2ANDROID+ function, the platform will use it in preference to +vkGetSwapchainGrallocUsageANDROID+ when translating a requested format, image usage flags, and swapchain image usage flags into gralloc usage flags. +vkGetSwapchainGrallocUsage2ANDROID+ behaves in the same way as +vkGetSwapchainGrallocUsageANDROID+, and is declared as
+[source,c]
+----
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+ int* grallocUsage
+);
+----
+
+VkNativeBufferANDROID+ is a +vkCreateImage+ extension structure for creating an image backed by a gralloc buffer. This structure is provided to +vkCreateImage+ in the +VkImageCreateInfo+ structure chain. Calls to +vkCreateImage+ with this structure will happen during the first call to +vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)+. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a +VkImage+ for each one.
[source,c]
@@ -78,6 +100,7 @@
----
When creating a gralloc-backed image, the +VkImageCreateInfo+ will have:
+[source,txt]
----
.imageType = VK_IMAGE_TYPE_2D
.format = a VkFormat matching the format requested for the gralloc buffer
@@ -93,6 +116,17 @@
.pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices
----
+Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a +VkSwapchainImageCreateInfoANDROID+ extension structure in the +VkImageCreateInfo+ chain provided to +vkCreateImage+, containing the swapchain image usage flags:
+[source,c]
+----
+typedef struct {
+ VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+ const void* pNext;
+
+ VkSwapchainImageUsageFlagsANDROID usage;
+} VkSwapchainImageCreateInfoANDROID;
+----
+
+vkAcquireImageANDROID+ acquires ownership of a swapchain image and imports an
externally-signalled native fence into both an existing VkSemaphore object
and an existing VkFence object:
@@ -139,6 +173,8 @@
This will be called during +vkQueuePresentWSI+ on the provided queue. Effects are similar to +vkQueueSignalSemaphore+, except with a native fence instead of a semaphore. The native fence must: not signal until the +waitSemaphoreCount+ semaphores in +pWaitSemaphores+ have signaled. Unlike +vkQueueSignalSemaphore+, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set +*pNativeFenceFd+ to -1. The file descriptor returned in +*pNativeFenceFd+ is owned and will be closed by the caller. Many drivers will be able to ignore the +image+ parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to +VK_IMAGE_LAYOUT_PRESENT_SRC_KHR+.
+If +image+ was created with +VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID+, then the driver must tolerate +vkQueueSignalReleaseImageANDROID+ being called repeatedly without intervening calls to +vkAcquireImageANDROID+.
+
== History ==
. *2015-07-08* Initial version
@@ -158,4 +194,9 @@
. *2016-01-08*
* Added waitSemaphoreCount and pWaitSemaphores parameters to vkQueueSignalReleaseImageANDROID.
. *2016-06-17*
- * Updates to reflect final behavior, closed some TBDs now that they've BDed.
\ No newline at end of file
+ * Updates to reflect final behavior, closed some TBDs now that they've BDed.
+. *2017-01-06*
+ * Extension version 6
+ * Added VkSwapchainImageUsageFlagBitsANDROID
+ * Added vkGetSwapchainGrallocUsage2ANDROID
+ * Added VkSwapchainImageCreateInfoANDROID
diff --git a/vulkan/doc/implementors_guide/implementors_guide.html b/vulkan/doc/implementors_guide/implementors_guide.html
index 0bfeb81..ce52c7f 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.html
+++ b/vulkan/doc/implementors_guide/implementors_guide.html
@@ -730,7 +730,7 @@
/*]]>*/
</script>
</head>
-<body class="book">
+<body class="article">
<div id="header">
<h1>Vulkan on Android Implementor’s Guide</h1>
<span id="revnumber">version 5</span>
@@ -795,7 +795,7 @@
<div class="paragraph"><p>The <span class="monospaced">vk_wsi_swapchin</span> and <span class="monospaced">vk_wsi_device_swapchain</span> extensions are primarily be implemented by the platform and live in <span class="monospaced">libvulkan.so</span>. The <span class="monospaced">VkSwapchain</span> object and all interaction with <span class="monospaced">ANativeWindow</span> will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver’s <span class="monospaced">vkGetDeviceProcAddr</span> functions, after passing through any enabled layers.</p></div>
<div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
@@ -806,9 +806,33 @@
<span style="color: #009900">int</span><span style="color: #990000">*</span> grallocUsage
<span style="color: #990000">);</span></tt></pre></div></div>
<div class="paragraph"><p>The <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span> parameters are taken from the <span class="monospaced">VkSwapchainCreateInfoKHR</span> structure. The driver should fill <span class="monospaced">*grallocUsage</span> with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.</p></div>
+<div class="paragraph"><p>Implementations may further need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span>, but also on the intended usage of the swapchain. The additional usage bits are defined as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">enum</span></span> VkSwapchainImageUsageFlagBitsANDROID <span style="color: #FF0000">{</span>
+ VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID <span style="color: #990000">=</span> <span style="color: #993399">0x00000001</span><span style="color: #990000">,</span>
+ VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM <span style="color: #990000">=</span> <span style="color: #993399">0x7FFFFFFF</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageUsageFlagBitsANDROID<span style="color: #990000">;</span>
+<span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="color: #008080">VkFlags</span> VkSwapchainImageUsageFlagsANDROID<span style="color: #990000">;</span></tt></pre></div></div>
+<div class="paragraph"><p>If the driver provides the <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> function, the platform will use it in preference to <span class="monospaced">vkGetSwapchainGrallocUsageANDROID</span> when translating a requested format, image usage flags, and swapchain image usage flags into gralloc usage flags. <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> behaves in the same way as <span class="monospaced">vkGetSwapchainGrallocUsageANDROID</span>, and is declared as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>VKAPI_ATTR <span style="color: #008080">VkResult</span> <span style="color: #008080">VKAPI_CALL</span> <span style="font-weight: bold"><span style="color: #000000">vkGetSwapchainGrallocUsage2ANDROID</span></span><span style="color: #990000">(</span>
+ <span style="color: #008080">VkDevice</span> device<span style="color: #990000">,</span>
+ <span style="color: #008080">VkFormat</span> format<span style="color: #990000">,</span>
+ <span style="color: #008080">VkImageUsageFlags</span> imageUsage<span style="color: #990000">,</span>
+ <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span> swapchainImageUsage<span style="color: #990000">,</span>
+ <span style="color: #009900">int</span><span style="color: #990000">*</span> grallocUsage
+<span style="color: #990000">);</span></tt></pre></div></div>
<div class="paragraph"><p><span class="monospaced">VkNativeBufferANDROID</span> is a <span class="monospaced">vkCreateImage</span> extension structure for creating an image backed by a gralloc buffer. This structure is provided to <span class="monospaced">vkCreateImage</span> in the <span class="monospaced">VkImageCreateInfo</span> structure chain. Calls to <span class="monospaced">vkCreateImage</span> with this structure will happen during the first call to <span class="monospaced">vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)</span>. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a <span class="monospaced">VkImage</span> for each one.</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
@@ -826,8 +850,11 @@
<span style="color: #FF0000">}</span> VkNativeBufferANDROID<span style="color: #990000">;</span></tt></pre></div></div>
<div class="paragraph"><p>When creating a gralloc-backed image, the <span class="monospaced">VkImageCreateInfo</span> will have:</p></div>
<div class="listingblock">
-<div class="content monospaced">
-<pre> .imageType = VK_IMAGE_TYPE_2D
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt> .imageType = VK_IMAGE_TYPE_2D
.format = a VkFormat matching the format requested for the gralloc buffer
.extent = the 2D dimensions requested for the gralloc buffer
.mipLevels = 1
@@ -838,13 +865,24 @@
.flags = 0
.sharingMode = VkSwapChainCreateInfoWSI::sharingMode
.queueFamilyCount = VkSwapChainCreateInfoWSI::queueFamilyCount
- .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</pre>
-</div></div>
+ .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</tt></pre></div></div>
+<div class="paragraph"><p>Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a <span class="monospaced">VkSwapchainImageCreateInfoANDROID</span> extension structure in the <span class="monospaced">VkImageCreateInfo</span> chain provided to <span class="monospaced">vkCreateImage</span>, containing the swapchain image usage flags:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
+ <span style="color: #008080">VkStructureType</span> sType<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID</span></span>
+ <span style="font-weight: bold"><span style="color: #0000FF">const</span></span> <span style="color: #009900">void</span><span style="color: #990000">*</span> pNext<span style="color: #990000">;</span>
+
+ <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span> usage<span style="color: #990000">;</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageCreateInfoANDROID<span style="color: #990000">;</span></tt></pre></div></div>
<div class="paragraph"><p><span class="monospaced">vkAcquireImageANDROID</span> acquires ownership of a swapchain image and imports an
externally-signalled native fence into both an existing VkSemaphore object
and an existing VkFence object:</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
@@ -872,7 +910,7 @@
is as if the native fence was already signalled.</p></div>
<div class="paragraph"><p><span class="monospaced">vkQueueSignalReleaseImageANDROID</span> prepares a swapchain image for external use, and creates a native fence and schedules it to be signalled when prior work on the queue has completed.</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
@@ -884,6 +922,7 @@
<span style="color: #009900">int</span><span style="color: #990000">*</span> pNativeFenceFd
<span style="color: #990000">);</span></tt></pre></div></div>
<div class="paragraph"><p>This will be called during <span class="monospaced">vkQueuePresentWSI</span> on the provided queue. Effects are similar to <span class="monospaced">vkQueueSignalSemaphore</span>, except with a native fence instead of a semaphore. The native fence must: not signal until the <span class="monospaced">waitSemaphoreCount</span> semaphores in <span class="monospaced">pWaitSemaphores</span> have signaled. Unlike <span class="monospaced">vkQueueSignalSemaphore</span>, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set <span class="monospaced">*pNativeFenceFd</span> to -1. The file descriptor returned in <span class="monospaced">*pNativeFenceFd</span> is owned and will be closed by the caller. Many drivers will be able to ignore the <span class="monospaced">image</span> parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to <span class="monospaced">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>.</p></div>
+<div class="paragraph"><p>If <span class="monospaced">image</span> was created with <span class="monospaced">VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID</span>, then the driver must tolerate <span class="monospaced">vkQueueSignalReleaseImageANDROID</span> being called repeatedly without intervening calls to <span class="monospaced">vkAcquireImageANDROID</span>.</p></div>
</div>
</div>
<div class="sect1">
@@ -978,6 +1017,33 @@
</li>
</ul></div>
</li>
+<li>
+<p>
+<strong>2017-01-06</strong>
+</p>
+<div class="ulist"><ul>
+<li>
+<p>
+Extension version 6
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageUsageFlagBitsANDROID
+</p>
+</li>
+<li>
+<p>
+Added vkGetSwapchainGrallocUsage2ANDROID
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageCreateInfoANDROID
+</p>
+</li>
+</ul></div>
+</li>
</ol></div>
</div>
</div>
@@ -986,7 +1052,7 @@
<div id="footer">
<div id="footer-text">
Version 5<br>
-Last updated 2016-06-17 13:54:25 PDT
+Last updated 2017-01-12 14:25:30 NZDT
</div>
</div>
</body>
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 0fd041a..a2ab07b 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -32,6 +32,7 @@
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id) ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
+#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
typedef enum VkSwapchainImageUsageFlagBitsANDROID {
VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID = 0x00000001,
@@ -56,7 +57,7 @@
VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
const void* pNext;
- VkSwapchainImageUsageFlagBitsANDROID usage;
+ VkSwapchainImageUsageFlagsANDROID usage;
} VkSwapchainImageCreateInfoANDROID;
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index 6a02b9b..5d38ff9 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -242,6 +242,7 @@
VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
+ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000,
VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -3227,6 +3228,8 @@
VK_PRESENT_MODE_MAILBOX_KHR = 1,
VK_PRESENT_MODE_FIFO_KHR = 2,
VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3,
+ VK_PRESENT_MODE_FRONT_BUFFERED_DEMAND_REFRESH_KHR = 1000111000,
+ VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR = 1000111001,
VK_PRESENT_MODE_BEGIN_RANGE_KHR = VK_PRESENT_MODE_IMMEDIATE_KHR,
VK_PRESENT_MODE_END_RANGE_KHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR,
VK_PRESENT_MODE_RANGE_SIZE_KHR = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1),
@@ -3883,6 +3886,18 @@
const VkPresentRegionKHR* pRegions;
} VkPresentRegionsKHR;
+#define VK_KHR_swapchain_front_buffered 1
+#define VK_KHR_SWAPCHAIN_FRONT_BUFFERED_SPEC_VERSION 2
+#define VK_KHR_SWAPCHAIN_FRONT_BUFFERED_EXTENSION_NAME "VK_KHR_swapchain_front_buffered"
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainStatusKHR)(VkDevice device, VkSwapchainKHR swapchain);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainStatusKHR(
+ VkDevice device,
+ VkSwapchainKHR swapchain);
+#endif
+
#define VK_EXT_debug_report 1
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
@@ -4126,6 +4141,52 @@
} VkDedicatedAllocationMemoryAllocateInfoNV;
+#define VK_GOOGLE_display_timing 1
+#define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+#define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+typedef struct VkRefreshCycleDurationGOOGLE {
+ uint64_t minRefreshDuration;
+ uint64_t maxRefreshDuration;
+} VkRefreshCycleDurationGOOGLE;
+
+typedef struct VkPastPresentationTimingGOOGLE {
+ uint32_t presentID;
+ uint64_t desiredPresentTime;
+ uint64_t actualPresentTime;
+ uint64_t earliestPresentTime;
+ uint64_t presentMargin;
+} VkPastPresentationTimingGOOGLE;
+
+typedef struct VkPresentTimeGOOGLE {
+ uint32_t presentID;
+ uint64_t desiredPresentTime;
+} VkPresentTimeGOOGLE;
+
+typedef struct VkPresentTimesInfoGOOGLE {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t swapchainCount;
+ const VkPresentTimeGOOGLE* pTimes;
+} VkPresentTimesInfoGOOGLE;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ uint32_t* pPresentationTimingCount,
+ VkPastPresentationTimingGOOGLE* pPresentationTimings);
+#endif
+
#define VK_AMD_draw_indirect_count 1
#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index ba883f7..746ab4e 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -69,6 +69,7 @@
"vulkan_headers",
],
shared_libs: [
+ "libgui",
"libziparchive",
"libhardware",
"libsync",
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index f5daca7..e05ca5a 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -23,13 +23,14 @@
#include <stdlib.h>
#include <string.h>
+
#include <algorithm>
#include <mutex>
#include <new>
#include <utility>
-#include <android/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <vulkan/vk_layer_interface.h>
#include "api.h"
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index ca2a579..b8b7e94 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -17,8 +17,10 @@
// WARNING: This file is generated. See ../README.md for instructions.
#include <string.h>
+
#include <algorithm>
-#include <android/log.h>
+
+#include <log/log.h>
// to catch mismatches between vulkan.h and this file
#undef VK_NO_PROTOTYPES
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index b27b36c..9021972 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -91,8 +91,10 @@
// WARNING: This file is generated. See ../README.md for instructions.
¶
#include <string.h>
+¶
#include <algorithm>
-#include <android/log.h>
+¶
+#include <log/log.h>
¶
// to catch mismatches between vulkan.h and this file
#undef VK_NO_PROTOTYPES
@@ -270,8 +272,10 @@
// WARNING: This file is generated. See ../README.md for instructions.
¶
#include <string.h>
+¶
#include <algorithm>
-#include <android/log.h>
+¶
+#include <log/log.h>
¶
#include "driver.h"
¶
@@ -700,6 +704,8 @@
VK_KHR_incremental_present
VK_KHR_surface
VK_KHR_swapchain
+VK_GOOGLE_display_timing
+VK_KHR_swapchain_front_buffered
{{end}}
@@ -1146,7 +1152,6 @@
{{ if eq $ext "VK_KHR_surface"}}true
{{else if eq $ext "VK_KHR_swapchain"}}true
{{else if eq $ext "VK_KHR_android_surface"}}true
- {{else if eq $ext "VK_KHR_incremental_present"}}true
{{end}}
{{end}}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 2555272..a46f46e 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -14,13 +14,21 @@
* limitations under the License.
*/
+#include <malloc.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/prctl.h>
+
#include <algorithm>
#include <array>
+#include <dlfcn.h>
#include <new>
-#include <malloc.h>
-#include <sys/prctl.h>
+
+#include <log/log.h>
+
+#include <android/dlext.h>
+#include <cutils/properties.h>
+#include <gui/GraphicsEnv.h>
#include "driver.h"
#include "stubhal.h"
@@ -123,17 +131,74 @@
Hal Hal::hal_;
+void* LoadLibrary(const android_dlextinfo& dlextinfo,
+ const char* subname,
+ int subname_len) {
+ const char kLibFormat[] = "vulkan.%*s.so";
+ char* name = static_cast<char*>(
+ alloca(sizeof(kLibFormat) + static_cast<size_t>(subname_len)));
+ sprintf(name, kLibFormat, subname_len, subname);
+ return android_dlopen_ext(name, RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+}
+
+const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
+ "ro.hardware." HWVULKAN_HARDWARE_MODULE_ID,
+ "ro.board.platform",
+}};
+
+int LoadUpdatedDriver(const hw_module_t** module) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = android::GraphicsEnv::getInstance().getDriverNamespace(),
+ };
+ if (!dlextinfo.library_namespace)
+ return -ENOENT;
+
+ void* so = nullptr;
+ char prop[PROPERTY_VALUE_MAX];
+ for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
+ int prop_len = property_get(key, prop, nullptr);
+ if (prop_len > 0) {
+ so = LoadLibrary(dlextinfo, prop, prop_len);
+ if (so)
+ break;
+ }
+ }
+ if (!so)
+ return -ENOENT;
+
+ hw_module_t* hmi = static_cast<hw_module_t*>(dlsym(so, HAL_MODULE_INFO_SYM_AS_STR));
+ if (!hmi) {
+ ALOGE("couldn't find symbol '%s' in HAL library: %s", HAL_MODULE_INFO_SYM_AS_STR, dlerror());
+ dlclose(so);
+ return -EINVAL;
+ }
+ if (strcmp(hmi->id, HWVULKAN_HARDWARE_MODULE_ID) != 0) {
+ ALOGE("HAL id '%s' != '%s'", hmi->id, HWVULKAN_HARDWARE_MODULE_ID);
+ dlclose(so);
+ return -EINVAL;
+ }
+ hmi->dso = so;
+ *module = hmi;
+ ALOGD("loaded updated driver");
+ return 0;
+}
+
bool Hal::Open() {
ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
// Use a stub device unless we successfully open a real HAL device.
hal_.dev_ = &stubhal::kDevice;
- const hwvulkan_module_t* module;
- int result =
- hw_get_module("vulkan", reinterpret_cast<const hw_module_t**>(&module));
+ int result;
+ const hwvulkan_module_t* module = nullptr;
+
+ result = LoadUpdatedDriver(reinterpret_cast<const hw_module_t**>(&module));
+ if (result == -ENOENT) {
+ result = hw_get_module(HWVULKAN_HARDWARE_MODULE_ID, reinterpret_cast<const hw_module_t**>(&module));
+ }
if (result != 0) {
- ALOGI("no Vulkan HAL present, using stub HAL");
+ ALOGV("unable to load Vulkan HAL, using stub HAL (result=%d)", result);
return true;
}
@@ -402,9 +467,16 @@
name = VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
ext_bit = ProcHook::ANDROID_native_buffer;
break;
+ case ProcHook::GOOGLE_display_timing:
+ hook_extensions_.set(ext_bit);
+ // return now as these extensions do not require HAL support
+ return;
case ProcHook::EXTENSION_UNKNOWN:
// HAL's extensions
break;
+ case ProcHook::KHR_swapchain_front_buffered:
+ // Exposed by HAL, but API surface is all in the loader
+ break;
default:
ALOGW("Ignored invalid device extension %s", name);
return;
@@ -422,6 +494,10 @@
if (ext_bit == ProcHook::ANDROID_native_buffer)
hook_extensions_.set(ProcHook::KHR_swapchain);
+ // Exposed by HAL, but API surface is all in the loader
+ if (ext_bit == ProcHook::KHR_swapchain_front_buffered)
+ hook_extensions_.set(ext_bit);
+
hal_extensions_.set(ext_bit);
}
@@ -660,26 +736,51 @@
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties) {
const InstanceData& data = GetData(physicalDevice);
+ static const std::array<VkExtensionProperties, 2> loader_extensions = {{
+ // WSI extensions
+ {VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
+ VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION},
+ {VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
+ VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION},
+ }};
+
+ // enumerate our extensions first
+ if (!pLayerName && pProperties) {
+ uint32_t count = std::min(
+ *pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
+
+ std::copy_n(loader_extensions.begin(), count, pProperties);
+
+ if (count < loader_extensions.size()) {
+ *pPropertyCount = count;
+ return VK_INCOMPLETE;
+ }
+
+ pProperties += count;
+ *pPropertyCount -= count;
+ }
VkResult result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
- if (result != VK_SUCCESS && result != VK_INCOMPLETE)
- return result;
- if (!pProperties)
- return result;
+ if (pProperties) {
+ // map VK_ANDROID_native_buffer to VK_KHR_swapchain
+ for (uint32_t i = 0; i < *pPropertyCount; i++) {
+ auto& prop = pProperties[i];
- // map VK_ANDROID_native_buffer to VK_KHR_swapchain
- for (uint32_t i = 0; i < *pPropertyCount; i++) {
- auto& prop = pProperties[i];
+ if (strcmp(prop.extensionName,
+ VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
+ continue;
- if (strcmp(prop.extensionName,
- VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
- continue;
+ memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
+ prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+ }
+ }
- memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
- sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
- prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+ // restore loader extension count
+ if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE)) {
+ *pPropertyCount += loader_extensions.size();
}
return result;
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index d74d9e9..e058439 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -22,7 +22,7 @@
#include <bitset>
#include <type_traits>
-#include <android/log.h>
+#include <log/log.h>
#include <vulkan/vulkan.h>
#include <hardware/hwvulkan.h>
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index c59ba24..4cc2eb5 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -17,8 +17,10 @@
// WARNING: This file is generated. See ../README.md for instructions.
#include <string.h>
+
#include <algorithm>
-#include <android/log.h>
+
+#include <log/log.h>
#include "driver.h"
@@ -73,6 +75,33 @@
}
}
+VKAPI_ATTR VkResult checkedGetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
+ return GetRefreshCycleDurationGOOGLE(device, swapchain, pDisplayTimingProperties);
+ } else {
+ Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetRefreshCycleDurationGOOGLE not executed.");
+ return VK_SUCCESS;
+ }
+}
+
+VKAPI_ATTR VkResult checkedGetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) {
+ if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
+ return GetPastPresentationTimingGOOGLE(device, swapchain, pPresentationTimingCount, pPresentationTimings);
+ } else {
+ Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetPastPresentationTimingGOOGLE not executed.");
+ return VK_SUCCESS;
+ }
+}
+
+VKAPI_ATTR VkResult checkedGetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain) {
+ if (GetData(device).hook_extensions[ProcHook::KHR_swapchain_front_buffered]) {
+ return GetSwapchainStatusKHR(device, swapchain);
+ } else {
+ Logger(device).Err(device, "VK_KHR_swapchain_front_buffered not enabled. vkGetSwapchainStatusKHR not executed.");
+ return VK_SUCCESS;
+ }
+}
+
// clang-format on
const ProcHook g_proc_hooks[] = {
@@ -218,6 +247,13 @@
nullptr,
},
{
+ "vkGetPastPresentationTimingGOOGLE",
+ ProcHook::DEVICE,
+ ProcHook::GOOGLE_display_timing,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPastPresentationTimingGOOGLE),
+ reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE),
+ },
+ {
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
ProcHook::INSTANCE,
ProcHook::KHR_surface,
@@ -246,6 +282,13 @@
nullptr,
},
{
+ "vkGetRefreshCycleDurationGOOGLE",
+ ProcHook::DEVICE,
+ ProcHook::GOOGLE_display_timing,
+ reinterpret_cast<PFN_vkVoidFunction>(GetRefreshCycleDurationGOOGLE),
+ reinterpret_cast<PFN_vkVoidFunction>(checkedGetRefreshCycleDurationGOOGLE),
+ },
+ {
"vkGetSwapchainGrallocUsage2ANDROID",
ProcHook::DEVICE,
ProcHook::ANDROID_native_buffer,
@@ -267,6 +310,13 @@
reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainImagesKHR),
},
{
+ "vkGetSwapchainStatusKHR",
+ ProcHook::DEVICE,
+ ProcHook::KHR_swapchain_front_buffered,
+ reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainStatusKHR),
+ reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainStatusKHR),
+ },
+ {
"vkQueuePresentKHR",
ProcHook::DEVICE,
ProcHook::KHR_swapchain,
@@ -300,8 +350,11 @@
if (strcmp(name, "VK_ANDROID_native_buffer") == 0) return ProcHook::ANDROID_native_buffer;
if (strcmp(name, "VK_EXT_debug_report") == 0) return ProcHook::EXT_debug_report;
if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface;
+ if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present;
if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
+ if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing;
+ if (strcmp(name, "VK_KHR_swapchain_front_buffered") == 0) return ProcHook::KHR_swapchain_front_buffered;
// clang-format on
return ProcHook::EXTENSION_UNKNOWN;
}
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 0228ef2..a137ab6 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -36,8 +36,11 @@
ANDROID_native_buffer,
EXT_debug_report,
KHR_android_surface,
+ KHR_incremental_present,
KHR_surface,
KHR_swapchain,
+ GOOGLE_display_timing,
+ KHR_swapchain_front_buffered,
EXTENSION_CORE, // valid bit
EXTENSION_COUNT,
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 5f6d243..05856d3 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -26,10 +26,10 @@
#include <string>
#include <vector>
-#include <android/log.h>
#include <android/dlext.h>
#include <android-base/strings.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <ziparchive/zip_archive.h>
#include <vulkan/vulkan_loader_data.h>
diff --git a/vulkan/libvulkan/stubhal.cpp b/vulkan/libvulkan/stubhal.cpp
index 3de8970..2926268 100644
--- a/vulkan/libvulkan/stubhal.cpp
+++ b/vulkan/libvulkan/stubhal.cpp
@@ -30,7 +30,7 @@
#include <bitset>
#include <mutex>
-#include <android/log.h>
+#include <log/log.h>
#include <hardware/hwvulkan.h>
#include "stubhal.h"
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 3e87abf..243ea69 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,10 +16,11 @@
#include <algorithm>
-#include <android/log.h>
+#include <log/log.h>
#include <gui/BufferQueue.h>
#include <sync/sync.h>
#include <utils/StrongPointer.h>
+#include <utils/SortedVector.h>
#include "driver.h"
@@ -105,6 +106,69 @@
}
}
+class TimingInfo {
+ public:
+ TimingInfo()
+ : vals_{0, 0, 0, 0, 0},
+ timestamp_desired_present_time_(0),
+ timestamp_actual_present_time_(0),
+ timestamp_render_complete_time_(0),
+ timestamp_composition_latch_time_(0) {}
+ TimingInfo(const VkPresentTimeGOOGLE* qp)
+ : vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0},
+ timestamp_desired_present_time_(0),
+ timestamp_actual_present_time_(0),
+ timestamp_render_complete_time_(0),
+ timestamp_composition_latch_time_(0) {}
+ bool ready() {
+ return (timestamp_desired_present_time_ &&
+ timestamp_actual_present_time_ &&
+ timestamp_render_complete_time_ &&
+ timestamp_composition_latch_time_);
+ }
+ void calculate(uint64_t rdur) {
+ vals_.actualPresentTime = timestamp_actual_present_time_;
+ uint64_t margin = (timestamp_composition_latch_time_ -
+ timestamp_render_complete_time_);
+ // Calculate vals_.earliestPresentTime, and potentially adjust
+ // vals_.presentMargin. The initial value of vals_.earliestPresentTime
+ // is vals_.actualPresentTime. If we can subtract rdur (the duration
+ // of a refresh cycle) from vals_.earliestPresentTime (and also from
+ // vals_.presentMargin) and still leave a positive margin, then we can
+ // report to the application that it could have presented earlier than
+ // it did (per the extension specification). If for some reason, we
+ // can do this subtraction repeatedly, we do, since
+ // vals_.earliestPresentTime really is supposed to be the "earliest".
+ uint64_t early_time = vals_.actualPresentTime;
+ while ((margin > rdur) &&
+ ((early_time - rdur) > timestamp_composition_latch_time_)) {
+ early_time -= rdur;
+ margin -= rdur;
+ }
+ vals_.earliestPresentTime = early_time;
+ vals_.presentMargin = margin;
+ }
+ void get_values(VkPastPresentationTimingGOOGLE* values) { *values = vals_; }
+
+ public:
+ VkPastPresentationTimingGOOGLE vals_;
+
+ uint64_t timestamp_desired_present_time_;
+ uint64_t timestamp_actual_present_time_;
+ uint64_t timestamp_render_complete_time_;
+ uint64_t timestamp_composition_latch_time_;
+};
+
+static inline int compare_type(const TimingInfo& lhs, const TimingInfo& rhs) {
+ // TODO(ianelliott): Change this from presentID to the frame ID once
+ // brianderson lands the appropriate patch:
+ if (lhs.vals_.presentID < rhs.vals_.presentID)
+ return -1;
+ if (lhs.vals_.presentID > rhs.vals_.presentID)
+ return 1;
+ return 0;
+}
+
// ----------------------------------------------------------------------------
struct Surface {
@@ -120,12 +184,23 @@
return reinterpret_cast<Surface*>(handle);
}
+// Maximum number of TimingInfo structs to keep per swapchain:
+enum { MAX_TIMING_INFOS = 10 };
+// Minimum number of frames to look for in the past (so we don't cause
+// syncronous requests to Surface Flinger):
+enum { MIN_NUM_FRAMES_AGO = 5 };
+
struct Swapchain {
Swapchain(Surface& surface_, uint32_t num_images_)
- : surface(surface_), num_images(num_images_) {}
+ : surface(surface_),
+ num_images(num_images_),
+ frame_timestamps_enabled(false) {
+ timing.clear();
+ }
Surface& surface;
uint32_t num_images;
+ bool frame_timestamps_enabled;
struct Image {
Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
@@ -138,6 +213,8 @@
int dequeue_fence;
bool dequeued;
} images[android::BufferQueue::NUM_BUFFER_SLOTS];
+
+ android::SortedVector<TimingInfo> timing;
};
VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
@@ -205,6 +282,112 @@
ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
}
swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
+ swapchain->timing.clear();
+}
+
+uint32_t get_num_ready_timings(Swapchain& swapchain) {
+ uint32_t num_ready = 0;
+ uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
+ uint32_t frames_ago = num_timings;
+ for (uint32_t i = 0; i < num_timings; i++) {
+ TimingInfo* ti = &swapchain.timing.editItemAt(i);
+ if (ti) {
+ if (ti->ready()) {
+ // This TimingInfo is ready to be reported to the user. Add it
+ // to the num_ready.
+ num_ready++;
+ } else {
+ // This TimingInfo is not yet ready to be reported to the user,
+ // and so we should look for any available timestamps that
+ // might make it ready.
+ int64_t desired_present_time = 0;
+ int64_t render_complete_time = 0;
+ int64_t composition_latch_time = 0;
+ int64_t actual_present_time = 0;
+ for (uint32_t f = MIN_NUM_FRAMES_AGO; f < frames_ago; f++) {
+ // Obtain timestamps:
+ int ret = native_window_get_frame_timestamps(
+ swapchain.surface.window.get(), f,
+ &desired_present_time, &render_complete_time,
+ &composition_latch_time,
+ NULL, //&first_composition_start_time,
+ NULL, //&last_composition_start_time,
+ NULL, //&composition_finish_time,
+ // TODO(ianelliott): Maybe ask if this one is
+ // supported, at startup time (since it may not be
+ // supported):
+ &actual_present_time,
+ NULL, //&display_retire_time,
+ NULL, //&dequeue_ready_time,
+ NULL /*&reads_done_time*/);
+ if (ret) {
+ break;
+ } else if (!ret) {
+ // We obtained at least one valid timestamp. See if it
+ // is for the present represented by this TimingInfo:
+ if (static_cast<uint64_t>(desired_present_time) ==
+ ti->vals_.desiredPresentTime) {
+ // Record the timestamp(s) we received, and then
+ // see if this TimingInfo is ready to be reported
+ // to the user:
+ ti->timestamp_desired_present_time_ =
+ static_cast<uint64_t>(desired_present_time);
+ ti->timestamp_actual_present_time_ =
+ static_cast<uint64_t>(actual_present_time);
+ ti->timestamp_render_complete_time_ =
+ static_cast<uint64_t>(render_complete_time);
+ ti->timestamp_composition_latch_time_ =
+ static_cast<uint64_t>(composition_latch_time);
+
+ if (ti->ready()) {
+ // The TimingInfo has received enough
+ // timestamps, and should now use those
+ // timestamps to calculate the info that should
+ // be reported to the user:
+ //
+ // FIXME: GET ACTUAL VALUE RATHER THAN HARD-CODE
+ // IT:
+ ti->calculate(16666666);
+ num_ready++;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return num_ready;
+}
+
+// TODO(ianelliott): DEAL WITH RETURN VALUE (e.g. VK_INCOMPLETE)!!!
+void copy_ready_timings(Swapchain& swapchain,
+ uint32_t* count,
+ VkPastPresentationTimingGOOGLE* timings) {
+ uint32_t num_copied = 0;
+ uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
+ if (*count < num_timings) {
+ num_timings = *count;
+ }
+ for (uint32_t i = 0; i < num_timings; i++) {
+ TimingInfo* ti = &swapchain.timing.editItemAt(i);
+ if (ti && ti->ready()) {
+ ti->get_values(&timings[num_copied]);
+ num_copied++;
+ // We only report the values for a given present once, so remove
+ // them from swapchain.timing:
+ //
+ // TODO(ianelliott): SEE WHAT HAPPENS TO THE LOOP WHEN THE
+ // FOLLOWING IS DONE:
+ swapchain.timing.removeAt(i);
+ i--;
+ num_timings--;
+ if (*count == num_copied) {
+ break;
+ }
+ }
+ }
+ *count = num_copied;
}
} // anonymous namespace
@@ -327,7 +510,6 @@
// TODO(jessehall): I think these are right, but haven't thought hard about
// it. Do we need to query the driver for support of any of these?
// Currently not included:
- // - VK_IMAGE_USAGE_GENERAL: maybe? does this imply cpu mappable?
// - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not
// - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not
capabilities->supportedUsageFlags =
@@ -376,6 +558,9 @@
VkPresentModeKHR* modes) {
const VkPresentModeKHR kModes[] = {
VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
+ // TODO(chrisforbes): should only expose this if the driver can.
+ VK_PRESENT_MODE_FRONT_BUFFERED_DEMAND_REFRESH_KHR,
+ VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR,
};
const uint32_t kNumModes = sizeof(kModes) / sizeof(kModes[0]);
@@ -423,7 +608,9 @@
"swapchain preTransform=%#x not supported",
create_info->preTransform);
ALOGV_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
- create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR),
+ create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ||
+ create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_DEMAND_REFRESH_KHR ||
+ create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR),
"swapchain presentMode=%u not supported",
create_info->presentMode);
@@ -477,6 +664,20 @@
return VK_ERROR_INITIALIZATION_FAILED;
}
+ err = native_window_set_shared_buffer_mode(surface.window.get(), false);
+ if (err != 0) {
+ ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ err = native_window_set_auto_refresh(surface.window.get(), false);
+ if (err != 0) {
+ ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
// -- Configure the native window --
const auto& dispatch = GetData(device).driver;
@@ -584,9 +785,36 @@
return VK_ERROR_INITIALIZATION_FAILED;
}
+ VkSwapchainImageUsageFlagsANDROID swapchain_image_usage = 0;
+ if (create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_DEMAND_REFRESH_KHR ||
+ create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR) {
+ swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID;
+
+ err = native_window_set_shared_buffer_mode(surface.window.get(), true);
+ if (err != 0) {
+ ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+ }
+
+ if (create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR) {
+ err = native_window_set_auto_refresh(surface.window.get(), true);
+ if (err != 0) {
+ ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+ }
+
int gralloc_usage = 0;
- // TODO(jessehall): Remove conditional once all drivers have been updated
- if (dispatch.GetSwapchainGrallocUsageANDROID) {
+ if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+ result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+ device, create_info->imageFormat, create_info->imageUsage,
+ swapchain_image_usage, &gralloc_usage);
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
result = dispatch.GetSwapchainGrallocUsageANDROID(
device, create_info->imageFormat, create_info->imageUsage,
&gralloc_usage);
@@ -629,12 +857,20 @@
// -- Dequeue all buffers and create a VkImage for each --
// Any failures during or after this must cancel the dequeued buffers.
+ VkSwapchainImageCreateInfoANDROID swapchain_image_create = {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
+#pragma clang diagnostic pop
+ .pNext = nullptr,
+ .usage = swapchain_image_usage,
+ };
VkNativeBufferANDROID image_native_buffer = {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
.sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
#pragma clang diagnostic pop
- .pNext = nullptr,
+ .pNext = &swapchain_image_create,
};
VkImageCreateInfo image_create = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@@ -728,6 +964,9 @@
bool active = swapchain->surface.swapchain_handle == swapchain_handle;
ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
+ if (swapchain->frame_timestamps_enabled) {
+ native_window_enable_frame_timestamps(window, false);
+ }
for (uint32_t i = 0; i < swapchain->num_images; i++)
ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
if (active)
@@ -868,7 +1107,8 @@
VkResult final_result = VK_SUCCESS;
// Look at the pNext chain for supported extension structs:
- const VkPresentRegionsKHR* present_regions = NULL;
+ const VkPresentRegionsKHR* present_regions = nullptr;
+ const VkPresentTimesInfoGOOGLE* present_times = nullptr;
const VkPresentRegionsKHR* next =
reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext);
while (next) {
@@ -876,6 +1116,10 @@
case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
present_regions = next;
break;
+ case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE:
+ present_times =
+ reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next);
+ break;
default:
ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x",
next->sType);
@@ -887,10 +1131,16 @@
present_regions &&
present_regions->swapchainCount != present_info->swapchainCount,
"VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount");
+ ALOGV_IF(present_times &&
+ present_times->swapchainCount != present_info->swapchainCount,
+ "VkPresentTimesInfoGOOGLE::swapchainCount != "
+ "VkPresentInfo::swapchainCount");
const VkPresentRegionKHR* regions =
- (present_regions) ? present_regions->pRegions : NULL;
+ (present_regions) ? present_regions->pRegions : nullptr;
+ const VkPresentTimeGOOGLE* times =
+ (present_times) ? present_times->pTimes : nullptr;
const VkAllocationCallbacks* allocator = &GetData(device).allocator;
- android_native_rect_t* rects = NULL;
+ android_native_rect_t* rects = nullptr;
uint32_t nrects = 0;
for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
@@ -898,7 +1148,8 @@
*SwapchainFromHandle(present_info->pSwapchains[sc]);
uint32_t image_idx = present_info->pImageIndices[sc];
Swapchain::Image& img = swapchain.images[image_idx];
- const VkPresentRegionKHR* region = (regions) ? ®ions[sc] : NULL;
+ const VkPresentRegionKHR* region = (regions) ? ®ions[sc] : nullptr;
+ const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr;
VkResult swapchain_result = VK_SUCCESS;
VkResult result;
int err;
@@ -955,6 +1206,34 @@
}
native_window_set_surface_damage(window, rects, rcount);
}
+ if (time) {
+ if (!swapchain.frame_timestamps_enabled) {
+ ALOGV(
+ "Calling "
+ "native_window_enable_frame_timestamps(true)");
+ native_window_enable_frame_timestamps(window, true);
+ swapchain.frame_timestamps_enabled = true;
+ }
+ // Record this presentID and desiredPresentTime so it can
+ // be later correlated to this present.
+ TimingInfo timing_record(time);
+ swapchain.timing.add(timing_record);
+ uint32_t num_timings =
+ static_cast<uint32_t>(swapchain.timing.size());
+ if (num_timings > MAX_TIMING_INFOS) {
+ swapchain.timing.removeAt(0);
+ }
+ if (time->desiredPresentTime) {
+ // Set the desiredPresentTime:
+ ALOGV(
+ "Calling "
+ "native_window_set_buffers_timestamp(%" PRId64 ")",
+ time->desiredPresentTime);
+ native_window_set_buffers_timestamp(
+ window,
+ static_cast<int64_t>(time->desiredPresentTime));
+ }
+ }
err = window->queueBuffer(window, img.buffer.get(), fence);
// queueBuffer always closes fence, even on error
if (err != 0) {
@@ -992,5 +1271,61 @@
return final_result;
}
+VKAPI_ATTR
+VkResult GetRefreshCycleDurationGOOGLE(
+ VkDevice,
+ VkSwapchainKHR,
+ VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ VkResult result = VK_SUCCESS;
+
+ // TODO(ianelliott): FULLY IMPLEMENT THIS FUNCTION!!!
+ pDisplayTimingProperties->minRefreshDuration = 16666666;
+ pDisplayTimingProperties->maxRefreshDuration = 16666666;
+
+ return result;
+}
+
+VKAPI_ATTR
+VkResult GetPastPresentationTimingGOOGLE(
+ VkDevice,
+ VkSwapchainKHR swapchain_handle,
+ uint32_t* count,
+ VkPastPresentationTimingGOOGLE* timings) {
+ Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+ ANativeWindow* window = swapchain.surface.window.get();
+ VkResult result = VK_SUCCESS;
+
+ if (!swapchain.frame_timestamps_enabled) {
+ ALOGV("Calling native_window_enable_frame_timestamps(true)");
+ native_window_enable_frame_timestamps(window, true);
+ swapchain.frame_timestamps_enabled = true;
+ }
+
+ if (timings) {
+ // TODO(ianelliott): plumb return value (e.g. VK_INCOMPLETE)
+ copy_ready_timings(swapchain, count, timings);
+ } else {
+ *count = get_num_ready_timings(swapchain);
+ }
+
+ return result;
+}
+
+VKAPI_ATTR
+VkResult GetSwapchainStatusKHR(
+ VkDevice,
+ VkSwapchainKHR swapchain_handle) {
+ Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+ VkResult result = VK_SUCCESS;
+
+ if (swapchain.surface.swapchain_handle != swapchain_handle) {
+ return VK_ERROR_OUT_OF_DATE_KHR;
+ }
+
+ // TODO(chrisforbes): Implement this function properly
+
+ return result;
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h
index 2c60c49..91d7219 100644
--- a/vulkan/libvulkan/swapchain.h
+++ b/vulkan/libvulkan/swapchain.h
@@ -34,6 +34,9 @@
VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint32_t* count, VkImage* images);
VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* image_index);
VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info);
+VKAPI_ATTR VkResult GetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+VKAPI_ATTR VkResult GetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+VKAPI_ATTR VkResult GetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain);
// clang-format on
} // namespace driver
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 4a520ee..89c65af 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -23,7 +23,7 @@
#include <algorithm>
#include <array>
-#include <android/log.h>
+#include <log/log.h>
#include <utils/Errors.h>
#include "null_driver_gen.h"