DO NOT MERGE Remove window obscurement information. am: 5508ca2c19 am: 3847972ad2 am: cd71708eca am: 88855f8de7
am: 41f75ecb79 -s ours
Change-Id: Id698475c3c67bbacecdb92b6d5de50e62ecd135c
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..03af56d
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -4
+AlignOperands: false
+AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakBeforeMultilineStrings: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerIndentWidth: 6
+ContinuationIndentWidth: 8
+IndentWidth: 4
+PenaltyBreakBeforeFirstCallParameter: 100000
+SpacesBeforeTrailingComments: 1
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..cd05b21
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,15 @@
+ndk_headers {
+ name: "libandroid_headers",
+ from: "include/android",
+ to: "android",
+ srcs: ["include/android/**/*.h"],
+ license: "NOTICE",
+}
+
+subdirs = [
+ "cmds/*",
+ "libs/*",
+ "opengl",
+ "services/*",
+ "vulkan",
+]
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
index 674c163..7e89220 100644
--- a/aidl/gui/android/view/Surface.aidl
+++ b/aidl/gui/android/view/Surface.aidl
@@ -17,4 +17,4 @@
package android.view;
-parcelable Surface cpp_header "gui/Surface.h";
+parcelable Surface cpp_header "gui/view/Surface.h";
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
new file mode 100644
index 0000000..6c5869a
--- /dev/null
+++ b/cmds/atrace/Android.bp
@@ -0,0 +1,27 @@
+// Copyright 2012 The Android Open Source Project
+
+cc_binary {
+ name: "atrace",
+ srcs: ["atrace.cpp"],
+
+ shared_libs: [
+ "libbinder",
+ "libhwbinder",
+ "android.hidl.manager@1.0",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libcutils",
+ "libutils",
+ "libz",
+ "libbase",
+ ],
+
+ init_rc: ["atrace.rc"],
+
+ product_variables: {
+ debuggable: {
+ init_rc: ["atrace_userdebug.rc"],
+ },
+ },
+}
diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk
deleted file mode 100644
index a787e95..0000000
--- a/cmds/atrace/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2012 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= atrace.cpp
-
-LOCAL_C_INCLUDES += external/zlib
-
-LOCAL_MODULE:= atrace
-
-LOCAL_MODULE_TAGS:= optional
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libcutils \
- libutils \
- libz \
-
-LOCAL_INIT_RC := atrace.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 5885738..47e04e7 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>
@@ -31,19 +31,26 @@
#include <unistd.h>
#include <zlib.h>
+#include <fstream>
+#include <memory>
+
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
#include <cutils/properties.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
#include <utils/Trace.h>
+#include <android-base/file.h>
using namespace android;
+using std::string;
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#define MAX_SYS_FILES 10
@@ -103,72 +110,85 @@
{ "ss", "System Server", ATRACE_TAG_SYSTEM_SERVER, { } },
{ "database", "Database", ATRACE_TAG_DATABASE, { } },
{ "network", "Network", ATRACE_TAG_NETWORK, { } },
+ { "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_waking/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, "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" },
} },
};
@@ -187,61 +207,65 @@
/* 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_traceCmdlineSizePath =
+ "saved_cmdlines_size";
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.
@@ -250,9 +274,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;
}
@@ -264,9 +288,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;
}
@@ -274,7 +299,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;
}
@@ -300,7 +325,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);
@@ -363,7 +388,7 @@
// Check whether the category would be supported on the device if the user
// were root. This function assumes that root is able to write to any file
// that exists. It performs the same logic as isCategorySupported, but it
-// uses file existance rather than writability in the /sys/ file checks.
+// uses file existence rather than writability in the /sys/ file checks.
static bool isCategorySupportedForRoot(const TracingCategory& category)
{
bool ok = category.tags != 0;
@@ -416,56 +441,40 @@
return writeStr(k_traceBufferSizePath, str);
}
-// Read the trace_clock sysfs file and return true if it matches the requested
-// value. The trace_clock file format is:
-// local [global] counter uptime perf
-static bool isTraceClock(const char *mode)
+// Set the default size of cmdline hashtable
+static bool setCmdlineSize()
{
- int fd = open(k_traceClockPath, O_RDONLY);
- if (fd == -1) {
- fprintf(stderr, "error opening %s: %s (%d)\n", k_traceClockPath,
- strerror(errno), errno);
- return false;
+ if (fileExists(k_traceCmdlineSizePath)) {
+ return writeStr(k_traceCmdlineSizePath, "8192");
}
-
- char buf[4097];
- ssize_t n = read(fd, buf, 4096);
- close(fd);
- if (n == -1) {
- fprintf(stderr, "error reading %s: %s (%d)\n", k_traceClockPath,
- strerror(errno), errno);
- return false;
- }
- buf[n] = '\0';
-
- char *start = strchr(buf, '[');
- if (start == NULL) {
- return false;
- }
- start++;
-
- char *end = strchr(start, ']');
- if (end == NULL) {
- return false;
- }
- *end = '\0';
-
- return strcmp(mode, start) == 0;
+ return true;
}
-// Enable or disable the kernel's use of the global clock. Disabling the global
-// clock will result in the kernel using a per-CPU local clock.
+// Set the clock to the best available option while tracing. Use 'boot' if it's
+// available; otherwise, use 'mono'. If neither are available use 'global'.
// Any write to the trace_clock sysfs file will reset the buffer, so only
// update it if the requested value is not the current value.
-static bool setGlobalClockEnable(bool enable)
+static bool setClock()
{
- const char *clock = enable ? "global" : "local";
+ std::ifstream clockFile((g_traceFolder + k_traceClockPath).c_str(), O_RDONLY);
+ std::string clockStr((std::istreambuf_iterator<char>(clockFile)),
+ std::istreambuf_iterator<char>());
- if (isTraceClock(clock)) {
- return true;
+ std::string newClock;
+ if (clockStr.find("boot") != std::string::npos) {
+ newClock = "boot";
+ } else if (clockStr.find("mono") != std::string::npos) {
+ newClock = "mono";
+ } else {
+ newClock = "global";
}
- return writeStr(k_traceClockPath, clock);
+ size_t begin = clockStr.find("[") + 1;
+ size_t end = clockStr.find("]");
+ if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0) {
+ return true;
+ }
+ return writeStr(k_traceClockPath, newClock.c_str());
}
static bool setPrintTgidEnableIfPresent(bool enable)
@@ -503,6 +512,54 @@
return true;
}
+// Poke all the HAL processes in the system to get them to re-read
+// their system properties.
+static void pokeHalServices()
+{
+ using ::android::hidl::base::V1_0::IBase;
+ using ::android::hidl::manager::V1_0::IServiceManager;
+ using ::android::hardware::hidl_string;
+ using ::android::hardware::Return;
+
+ sp<IServiceManager> sm = ::android::hardware::defaultServiceManager();
+
+ if (sm == nullptr) {
+ fprintf(stderr, "failed to get IServiceManager to poke hal services\n");
+ return;
+ }
+
+ auto listRet = sm->list([&](const auto &interfaces) {
+ for (size_t i = 0; i < interfaces.size(); i++) {
+ string fqInstanceName = interfaces[i];
+ string::size_type n = fqInstanceName.find("/");
+ if (n == std::string::npos || interfaces[i].size() == n+1)
+ continue;
+ hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
+ hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos);
+ Return<sp<IBase>> interfaceRet = sm->get(fqInterfaceName, instanceName);
+ if (!interfaceRet.isOk()) {
+ // ignore
+ continue;
+ }
+
+ sp<IBase> interface = interfaceRet;
+ if (interface == nullptr) {
+ // ignore
+ continue;
+ }
+
+ auto notifyRet = interface->notifySyspropsChanged();
+ if (!notifyRet.isOk()) {
+ // ignore
+ }
+ }
+ });
+ if (!listRet.isOk()) {
+ // TODO(b/34242478) fix this when we determine the correct ACL
+ //fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str());
+ }
+}
+
// Set the trace tags that userland tracing uses, and poke the running
// processes to pick up the new value.
static bool setTagsProperty(uint64_t tags)
@@ -533,11 +590,11 @@
// Set the system property that indicates which apps should perform
// application-level tracing.
-static bool setAppCmdlineProperty(const char* cmdline)
+static bool setAppCmdlineProperty(char* cmdline)
{
char buf[PROPERTY_KEY_MAX];
int i = 0;
- const char* start = cmdline;
+ char* start = cmdline;
while (start != NULL) {
if (i == MAX_PACKAGES) {
fprintf(stderr, "error: only 16 packages could be traced at once\n");
@@ -587,24 +644,14 @@
// kernel.
static bool verifyKernelTraceFuncs(const char* funcs)
{
- int fd = open(k_ftraceFilterPath, O_RDONLY);
- if (fd == -1) {
- fprintf(stderr, "error opening %s: %s (%d)\n", k_ftraceFilterPath,
+ std::string 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;
+ return false;
}
- char buf[4097];
- ssize_t n = read(fd, buf, 4096);
- close(fd);
- if (n == -1) {
- fprintf(stderr, "error reading %s: %s (%d)\n", k_ftraceFilterPath,
- strerror(errno), errno);
- return false;
- }
-
- buf[n] = '\0';
- String8 funcList = String8::format("\n%s", buf);
+ String8 funcList = String8::format("\n%s",buf.c_str());
// Make sure that every function listed in funcs is in the list we just
// read from the kernel, except for wildcard inputs.
@@ -624,7 +671,6 @@
func = strtok(NULL, ",");
}
free(myFuncs);
-
return ok;
}
@@ -724,7 +770,9 @@
ok &= setCategoriesEnableFromFile(g_categoriesFile);
ok &= setTraceOverwriteEnable(g_traceOverwrite);
ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
- ok &= setGlobalClockEnable(true);
+ // TODO: Re-enable after stabilization
+ //ok &= setCmdlineSize();
+ ok &= setClock();
ok &= setPrintTgidEnableIfPresent(true);
ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
@@ -754,8 +802,9 @@
}
packageList += value;
}
- ok &= setAppCmdlineProperty(packageList.data());
+ ok &= setAppCmdlineProperty(&packageList[0]);
ok &= pokeBinderServices();
+ pokeHalServices();
// Disable all the sysfs enables. This is done as a separate loop from
// the enables to allow the same enable to exist in multiple categories.
@@ -797,7 +846,6 @@
// Set the options back to their defaults.
setTraceOverwriteEnable(true);
setTraceBufferSizeKB(1);
- setGlobalClockEnable(false);
setPrintTgidEnableIfPresent(false);
setKernelTraceFuncs(NULL);
}
@@ -819,7 +867,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);
@@ -844,7 +892,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);
@@ -853,30 +901,34 @@
if (g_compress) {
z_stream zs;
- uint8_t *in, *out;
- int result, flush;
-
memset(&zs, 0, sizeof(zs));
- result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
+
+ int result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
if (result != Z_OK) {
fprintf(stderr, "error initializing zlib: %d\n", result);
close(traceFD);
return;
}
- const size_t bufSize = 64*1024;
- in = (uint8_t*)malloc(bufSize);
- out = (uint8_t*)malloc(bufSize);
- flush = Z_NO_FLUSH;
+ constexpr size_t bufSize = 64*1024;
+ std::unique_ptr<uint8_t> in(new uint8_t[bufSize]);
+ std::unique_ptr<uint8_t> out(new uint8_t[bufSize]);
+ if (!in || !out) {
+ fprintf(stderr, "couldn't allocate buffers\n");
+ close(traceFD);
+ return;
+ }
- zs.next_out = out;
+ int flush = Z_NO_FLUSH;
+
+ zs.next_out = reinterpret_cast<Bytef*>(out.get());
zs.avail_out = bufSize;
do {
if (zs.avail_in == 0) {
// More input is needed.
- result = read(traceFD, in, bufSize);
+ result = read(traceFD, in.get(), bufSize);
if (result < 0) {
fprintf(stderr, "error reading trace: %s (%d)\n",
strerror(errno), errno);
@@ -885,14 +937,14 @@
} else if (result == 0) {
flush = Z_FINISH;
} else {
- zs.next_in = in;
+ zs.next_in = reinterpret_cast<Bytef*>(in.get());
zs.avail_in = result;
}
}
if (zs.avail_out == 0) {
// Need to write the output.
- result = write(outFd, out, bufSize);
+ result = write(outFd, out.get(), bufSize);
if ((size_t)result < bufSize) {
fprintf(stderr, "error writing deflated trace: %s (%d)\n",
strerror(errno), errno);
@@ -900,7 +952,7 @@
zs.avail_out = bufSize; // skip the final write
break;
}
- zs.next_out = out;
+ zs.next_out = reinterpret_cast<Bytef*>(out.get());
zs.avail_out = bufSize;
}
@@ -912,7 +964,7 @@
if (zs.avail_out < bufSize) {
size_t bytes = bufSize - zs.avail_out;
- result = write(outFd, out, bytes);
+ result = write(outFd, out.get(), bytes);
if ((size_t)result < bytes) {
fprintf(stderr, "error writing deflated trace: %s (%d)\n",
strerror(errno), errno);
@@ -923,9 +975,6 @@
if (result != Z_OK) {
fprintf(stderr, "error cleaning up zlib: %d\n", result);
}
-
- free(in);
- free(out);
} else {
ssize_t sent = 0;
while ((sent = sendfile(outFd, traceFD, NULL, 64*1024*1024)) > 0);
@@ -981,9 +1030,9 @@
" -k fname,... trace the listed kernel functions\n"
" -n ignore signals\n"
" -s N sleep for N seconds before tracing [default 0]\n"
- " -t N trace for N seconds [defualt 5]\n"
+ " -t N trace for N seconds [default 5]\n"
" -z compress the trace dump\n"
- " --async_start start circular trace and return immediatly\n"
+ " --async_start start circular trace and return immediately\n"
" --async_dump dump the current contents of circular trace buffer\n"
" --async_stop stop tracing and dump the current contents of circular\n"
" trace buffer\n"
@@ -998,6 +1047,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;
@@ -1011,6 +1083,11 @@
exit(0);
}
+ if (!findTraceFiles()) {
+ fprintf(stderr, "No trace folder found\n");
+ exit(-1);
+ }
+
for (;;) {
int ret;
int option_index = 0;
@@ -1158,7 +1235,7 @@
fflush(stdout);
int outFd = STDOUT_FILENO;
if (g_outputFile) {
- outFd = open(g_outputFile, O_WRONLY | O_CREAT);
+ outFd = open(g_outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
if (outFd == -1) {
printf("Failed to open '%s', err=%d", g_outputFile, errno);
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 747cc69..d538145 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -4,63 +4,135 @@
# 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/saved_cmdlines_size
+ chown root shell /sys/kernel/tracing/saved_cmdlines_size
chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable
+ chown root shell /sys/kernel/tracing/events/sched/sched_switch/enable
chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
+ chown root shell /sys/kernel/tracing/events/sched/sched_wakeup/enable
chown root shell /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable
+ chown root shell /sys/kernel/tracing/events/sched/sched_blocked_reason/enable
chown root shell /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable
+ chown root shell /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable
chown root shell /sys/kernel/debug/tracing/events/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/saved_cmdlines_size
+ chmod 0664 /sys/kernel/tracing/saved_cmdlines_size
chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable
+ chmod 0664 /sys/kernel/tracing/events/sched/sched_switch/enable
chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
+ chmod 0664 /sys/kernel/tracing/events/sched/sched_wakeup/enable
chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable
+ chmod 0664 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable
chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable
+ chmod 0664 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable
chmod 0664 /sys/kernel/debug/tracing/events/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/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
new file mode 100644
index 0000000..5fd28e2
--- /dev/null
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -0,0 +1,47 @@
+## Permissions to allow additional system-wide tracing to the kernel trace buffer.
+## The default list of permissions is set in frameworks/native/cmds/atrace/atrace.rc
+
+# Allow the shell group to enable kernel tracepoints:
+
+on post-fs
+ chown root shell /sys/kernel/debug/tracing/events/sync/enable
+ chown root shell /sys/kernel/debug/tracing/events/workqueue/enable
+ chown root shell /sys/kernel/debug/tracing/events/regulator/enable
+ chown root shell /sys/kernel/debug/tracing/events/pagecache/enable
+
+ # irq
+ chown root shell /sys/kernel/debug/tracing/events/irq/enable
+ chown root shell /sys/kernel/debug/tracing/events/ipi/enable
+
+ # disk
+ chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable
+ chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable
+ chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable
+ chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable
+ chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable
+ chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable
+ chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable
+ chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable
+ chown root shell /sys/kernel/debug/tracing/events/block/block_rq_issue/enable
+ chown root shell /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
+
+ chmod 0664 /sys/kernel/debug/tracing/events/sync/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/workqueue/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/regulator/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/pagecache/enable
+
+ # irq
+ chmod 0664 /sys/kernel/debug/tracing/events/irq/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/ipi/enable
+
+ # disk
+ chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/block/block_rq_issue/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
diff --git a/cmds/bugreport/Android.bp b/cmds/bugreport/Android.bp
new file mode 100644
index 0000000..139e4b2
--- /dev/null
+++ b/cmds/bugreport/Android.bp
@@ -0,0 +1,6 @@
+cc_binary {
+ name: "bugreport",
+ srcs: ["bugreport.cpp"],
+ cflags: ["-Wall"],
+ shared_libs: ["libcutils"],
+}
diff --git a/cmds/bugreport/Android.mk b/cmds/bugreport/Android.mk
deleted file mode 100644
index ced5d30..0000000
--- a/cmds/bugreport/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= bugreport.cpp
-
-LOCAL_MODULE:= bugreport
-
-LOCAL_CFLAGS := -Wall
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md
index 2697f09..eb0d898 100644
--- a/cmds/bugreportz/readme.md
+++ b/cmds/bugreportz/readme.md
@@ -17,3 +17,4 @@
- `OK:<path_to_bugreport_file>` in case of success.
- `FAIL:<error message>` in case of failure.
+
diff --git a/cmds/cmd/Android.mk b/cmds/cmd/Android.mk
index ac2f4c0..d565e57 100644
--- a/cmds/cmd/Android.mk
+++ b/cmds/cmd/Android.mk
@@ -7,8 +7,11 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
liblog \
+ libselinux \
libbinder
-
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE)
ifeq ($(TARGET_OS),linux)
LOCAL_CFLAGS += -DXP_UNIX
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index ed740d3..7e05d72 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -21,7 +21,10 @@
#include <binder/ProcessState.h>
#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
#include <binder/TextOutput.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
#include <utils/Vector.h>
#include <getopt.h>
@@ -29,7 +32,16 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <sys/time.h>
+#include <errno.h>
+
+#include "selinux/selinux.h"
+#include "selinux/android.h"
+
+#include <UniquePtr.h>
+
+#define DEBUG 0
using namespace android;
@@ -38,10 +50,72 @@
return lhs->compare(*rhs);
}
+struct SecurityContext_Delete {
+ void operator()(security_context_t p) const {
+ freecon(p);
+ }
+};
+typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext;
+
+class MyShellCallback : public BnShellCallback
+{
+public:
+ bool mActive = true;
+
+ virtual int openOutputFile(const String16& path, const String16& seLinuxContext) {
+ String8 path8(path);
+ char cwd[256];
+ getcwd(cwd, 256);
+ String8 fullPath(cwd);
+ fullPath.appendPath(path8);
+ if (!mActive) {
+ aerr << "Open attempt after active for: " << fullPath << endl;
+ return -EPERM;
+ }
+ int fd = open(fullPath.string(), O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG);
+ if (fd < 0) {
+ return fd;
+ }
+ if (is_selinux_enabled() && seLinuxContext.size() > 0) {
+ String8 seLinuxContext8(seLinuxContext);
+ security_context_t tmp = NULL;
+ int ret = getfilecon(fullPath.string(), &tmp);
+ Unique_SecurityContext context(tmp);
+ int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(),
+ "file", "write", NULL);
+ if (accessGranted != 0) {
+ close(fd);
+ aerr << "System server has no access to file context " << context.get()
+ << " (from path " << fullPath.string() << ", context "
+ << seLinuxContext8.string() << ")" << endl;
+ return -EPERM;
+ }
+ }
+ return fd;
+ }
+};
+
class MyResultReceiver : public BnResultReceiver
{
public:
- virtual void send(int32_t /*resultCode*/) {
+ Mutex mMutex;
+ Condition mCondition;
+ bool mHaveResult = false;
+ int32_t mResult = 0;
+
+ virtual void send(int32_t resultCode) {
+ AutoMutex _l(mMutex);
+ mResult = resultCode;
+ mHaveResult = true;
+ mCondition.signal();
+ }
+
+ int32_t waitForResult() {
+ AutoMutex _l(mMutex);
+ while (!mHaveResult) {
+ mCondition.wait(mMutex);
+ }
+ return mResult;
}
};
@@ -49,18 +123,25 @@
{
signal(SIGPIPE, SIG_IGN);
sp<ProcessState> proc = ProcessState::self();
+ // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
+ // not allowed to spawn any additional threads, but we still spawn
+ // a binder thread from userspace when we call startThreadPool().
+ // This is safe because we only have 2 callbacks, neither of which
+ // block.
+ // See b/36066697 for rationale
+ proc->setThreadPoolMaxThreadCount(0);
proc->startThreadPool();
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == NULL) {
- ALOGE("Unable to get default service manager!");
+ ALOGW("Unable to get default service manager!");
aerr << "cmd: Unable to get default service manager!" << endl;
return 20;
}
if (argc == 1) {
- aout << "cmd: no service specified; use -l to list all services" << endl;
+ aerr << "cmd: No service specified; use -l to list all services" << endl;
return 20;
}
@@ -85,12 +166,41 @@
String16 cmd = String16(argv[1]);
sp<IBinder> service = sm->checkService(cmd);
if (service == NULL) {
- aerr << "Can't find service: " << argv[1] << endl;
+ ALOGW("Can't find service %s", argv[1]);
+ aerr << "cmd: Can't find service: " << argv[1] << endl;
return 20;
}
+ sp<MyShellCallback> cb = new MyShellCallback();
+ sp<MyResultReceiver> result = new MyResultReceiver();
+
+#if DEBUG
+ ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", argv[1], STDIN_FILENO, STDOUT_FILENO,
+ STDERR_FILENO);
+#endif
+
// TODO: block until a result is returned to MyResultReceiver.
- IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args,
- new MyResultReceiver());
- return 0;
+ status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args,
+ cb, result);
+ if (err < 0) {
+ const char* errstr;
+ switch (err) {
+ case BAD_TYPE: errstr = "Bad type"; break;
+ case FAILED_TRANSACTION: errstr = "Failed transaction"; break;
+ case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break;
+ case UNEXPECTED_NULL: errstr = "Unexpected null"; break;
+ default: errstr = strerror(-err); break;
+ }
+ ALOGW("Failure calling service %s: %s (%d)", argv[1], errstr, -err);
+ aout << "cmd: Failure calling service " << argv[1] << ": " << errstr << " ("
+ << (-err) << ")" << endl;
+ return err;
+ }
+
+ cb->mActive = false;
+ status_t res = result->waitForResult();
+#if DEBUG
+ ALOGD("result=%d", (int)res);
+#endif
+ return res;
}
diff --git a/cmds/dumpstate/.clang-format b/cmds/dumpstate/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /dev/null
+++ b/cmds/dumpstate/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+AccessModifierOffset: -2
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 791a7c4..a407ea2 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,24 +1,178 @@
LOCAL_PATH:= $(call my-dir)
+
+# ================#
+# Common settings #
+# ================#
+# ZipArchive support, the order matters here to get all symbols.
+COMMON_ZIP_LIBRARIES := libziparchive libz libcrypto
+
+# TODO: ideally the tests should depend on a shared dumpstate library, but currently libdumpstate
+# is used to define the device-specific HAL library. Instead, both dumpstate and dumpstate_test
+# shares a lot of common settings
+COMMON_LOCAL_CFLAGS := \
+ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+COMMON_SRC_FILES := \
+ DumpstateInternal.cpp \
+ utils.cpp
+COMMON_SHARED_LIBRARIES := \
+ android.hardware.dumpstate@1.0 \
+ android.hidl.manager@1.0 \
+ libhidlbase \
+ libbase \
+ libbinder \
+ libcutils \
+ libdebuggerd_client \
+ libdumpstateaidl \
+ libdumpstateutil \
+ liblog \
+ libselinux \
+ libutils \
+ $(COMMON_ZIP_LIBRARIES)
+
+# ====================#
+# libdumpstateutil #
+# ====================#
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := libdumpstate_default.cpp
-LOCAL_MODULE := libdumpstate.default
+
+LOCAL_MODULE := libdumpstateutil
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_SRC_FILES := \
+ DumpstateInternal.cpp \
+ DumpstateUtil.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ liblog \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ====================#
+# libdumpstateheaders #
+# ====================#
+# TODO: this module is necessary so the device-specific libdumpstate implementations do not
+# need to add any other dependency (like libbase). Should go away once dumpstate HAL changes.
+include $(CLEAR_VARS)
+
+LOCAL_EXPORT_C_INCLUDE_DIRS = $(LOCAL_PATH)
+LOCAL_MODULE := libdumpstateheaders
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+ $(COMMON_SHARED_LIBRARIES)
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
+ $(COMMON_STATIC_LIBRARIES)
+# Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_
+LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS)
+LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS)
+
include $(BUILD_STATIC_LIBRARY)
+# ================ #
+# libdumpstateaidl #
+# =================#
include $(CLEAR_VARS)
-ifdef BOARD_WLAN_DEVICE
-LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
-endif
+LOCAL_MODULE := libdumpstateaidl
-LOCAL_SRC_FILES := dumpstate.cpp utils.cpp
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libutils
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/binder
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_SRC_FILES := \
+ binder/android/os/IDumpstate.aidl \
+ binder/android/os/IDumpstateListener.aidl \
+ binder/android/os/IDumpstateToken.aidl
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ==========#
+# dumpstate #
+# ==========#
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+ DumpstateService.cpp \
+ dumpstate.cpp
LOCAL_MODULE := dumpstate
-LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux libbase
-# ZipArchive support, the order matters here to get all symbols.
-LOCAL_STATIC_LIBRARIES := libziparchive libz libmincrypt
-LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
+LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
+
+LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES)
+
+LOCAL_CFLAGS += $(COMMON_LOCAL_CFLAGS)
+
LOCAL_INIT_RC := dumpstate.rc
include $(BUILD_EXECUTABLE)
+
+# ===============#
+# dumpstate_test #
+# ===============#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dumpstate_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+ DumpstateService.cpp \
+ tests/dumpstate_test.cpp
+
+LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES) \
+ libgmock
+
+LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
+
+include $(BUILD_NATIVE_TEST)
+
+# =======================#
+# dumpstate_test_fixture #
+# =======================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dumpstate_test_fixture
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_SRC_FILES := \
+ tests/dumpstate_test_fixture.cpp
+
+LOCAL_MODULE_CLASS := NATIVE_TESTS
+
+dumpstate_tests_intermediates := $(local-intermediates-dir)/DATA
+dumpstate_tests_subpath_from_data := nativetest/dumpstate_test_fixture
+dumpstate_tests_root_in_device := /data/$(dumpstate_tests_subpath_from_data)
+dumpstate_tests_root_for_test_zip := $(dumpstate_tests_intermediates)/$(dumpstate_tests_subpath_from_data)
+testdata_files := $(call find-subdir-files, testdata/*)
+
+# Copy test data files to intermediates/DATA for use with LOCAL_PICKUP_FILES
+GEN := $(addprefix $(dumpstate_tests_root_for_test_zip)/, $(testdata_files))
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(dumpstate_tests_root_for_test_zip)/testdata/% : $(LOCAL_PATH)/testdata/%
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+# Copy test data files again to $OUT/data so the tests can be run with adb sync
+# TODO: the build system should do this automatically
+GEN := $(addprefix $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/, $(testdata_files))
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/testdata/% : $(LOCAL_PATH)/testdata/%
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+LOCAL_PICKUP_FILES := $(dumpstate_tests_intermediates)
+
+include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
new file mode 100644
index 0000000..0343277
--- /dev/null
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateInternal.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+
+uint64_t Nanotime() {
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec);
+}
+
+// Switches to non-root user and group.
+bool DropRootUser() {
+ if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
+ MYLOGD("drop_root_user(): already running as Shell\n");
+ return true;
+ }
+ /* ensure we will keep capabilities when we drop root */
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ 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;
+ }
+ if (setgid(AID_SHELL) != 0) {
+ MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
+ return false;
+ }
+ if (setuid(AID_SHELL) != 0) {
+ MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
+ return false;
+ }
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ 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;
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ MYLOGE("capset failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
+ bool dry_run) {
+ const char* path = path_string.c_str();
+ if (!title.empty()) {
+ dprintf(out_fd, "------ %s (%s", title.c_str(), path);
+
+ struct stat st;
+ // Only show the modification time of non-device files.
+ size_t path_len = strlen(path);
+ if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
+ (path_len < 5 || memcmp(path, "/sys/", 5)) &&
+ (path_len < 3 || memcmp(path, "/d/", 3)) && !fstat(fd, &st)) {
+ char stamp[80];
+ time_t mtime = st.st_mtime;
+ strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
+ dprintf(out_fd, ": %s", stamp);
+ }
+ dprintf(out_fd, ") ------\n");
+ fsync(out_fd);
+ }
+ if (dry_run) {
+ if (out_fd != STDOUT_FILENO) {
+ // There is no title, but we should still print a dry-run message
+ dprintf(out_fd, "%s: skipped on dry run\n", path);
+ } else if (!title.empty()) {
+ dprintf(out_fd, "\t(skipped on dry run)\n");
+ }
+ fsync(out_fd);
+ return 0;
+ }
+ bool newline = false;
+ fd_set read_set;
+ timeval tm;
+ while (true) {
+ FD_ZERO(&read_set);
+ FD_SET(fd, &read_set);
+ /* Timeout if no data is read for 30 seconds. */
+ tm.tv_sec = 30;
+ tm.tv_usec = 0;
+ uint64_t elapsed = Nanotime();
+ int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, nullptr, nullptr, &tm));
+ if (ret == -1) {
+ dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
+ newline = true;
+ break;
+ } else if (ret == 0) {
+ elapsed = Nanotime() - elapsed;
+ dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
+ newline = true;
+ break;
+ } else {
+ char buffer[65536];
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
+ if (bytes_read > 0) {
+ android::base::WriteFully(out_fd, buffer, bytes_read);
+ newline = (buffer[bytes_read - 1] == '\n');
+ } else {
+ if (bytes_read == -1) {
+ dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno));
+ newline = true;
+ }
+ break;
+ }
+ }
+ }
+ close(fd);
+
+ if (!newline) dprintf(out_fd, "\n");
+ if (!title.empty()) dprintf(out_fd, "\n");
+ return 0;
+}
diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h
new file mode 100644
index 0000000..2f7704d
--- /dev/null
+++ b/cmds/dumpstate/DumpstateInternal.h
@@ -0,0 +1,55 @@
+/*
+ * 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 FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
+
+#include <cstdint>
+#include <string>
+
+// TODO: rename macros to DUMPSTATE_LOGXXX
+#ifndef MYLOGD
+#define MYLOGD(...) \
+ fprintf(stderr, __VA_ARGS__); \
+ ALOGD(__VA_ARGS__);
+#endif
+
+#ifndef MYLOGI
+#define MYLOGI(...) \
+ fprintf(stderr, __VA_ARGS__); \
+ ALOGI(__VA_ARGS__);
+#endif
+
+#ifndef MYLOGE
+#define MYLOGE(...) \
+ fprintf(stderr, __VA_ARGS__); \
+ ALOGE(__VA_ARGS__);
+#endif
+
+// Internal functions used by .cpp files on multiple build targets.
+// TODO: move to android::os::dumpstate::internal namespace
+
+// TODO: use functions from <chrono> instead
+const uint64_t NANOS_PER_SEC = 1000000000;
+uint64_t Nanotime();
+
+// Switches to non-root user and group.
+bool DropRootUser();
+
+// TODO: move to .cpp as static once is not used by utils.cpp anymore.
+int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
+ bool dry_run = false);
+
+#endif // FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
new file mode 100644
index 0000000..efe0466
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -0,0 +1,106 @@
+/**
+ * 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.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateService.h"
+
+#include <android-base/stringprintf.h>
+
+#include "android/os/BnDumpstate.h"
+
+#include "DumpstateInternal.h"
+
+namespace android {
+namespace os {
+
+namespace {
+class DumpstateToken : public BnDumpstateToken {};
+}
+
+DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
+}
+
+char const* DumpstateService::getServiceName() {
+ return "dumpstate";
+}
+
+status_t DumpstateService::Start() {
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ status_t ret = BinderService<DumpstateService>::publish();
+ if (ret != android::OK) {
+ return ret;
+ }
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ return android::OK;
+}
+
+binder::Status DumpstateService::setListener(const std::string& name,
+ const sp<IDumpstateListener>& listener,
+ sp<IDumpstateToken>* returned_token) {
+ *returned_token = nullptr;
+ if (name.empty()) {
+ MYLOGE("setListener(): name not set\n");
+ return binder::Status::ok();
+ }
+ if (listener == nullptr) {
+ MYLOGE("setListener(): listener not set\n");
+ return binder::Status::ok();
+ }
+ std::lock_guard<std::mutex> lock(lock_);
+ if (ds_.listener_ != nullptr) {
+ MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_.listener_name_.c_str());
+ return binder::Status::ok();
+ }
+
+ ds_.listener_name_ = name;
+ ds_.listener_ = listener;
+ *returned_token = new DumpstateToken();
+
+ return binder::Status::ok();
+}
+
+status_t DumpstateService::dump(int fd, const Vector<String16>&) {
+ dprintf(fd, "id: %d\n", ds_.id_);
+ dprintf(fd, "pid: %d\n", ds_.pid_);
+ dprintf(fd, "update_progress: %s\n", ds_.update_progress_ ? "true" : "false");
+ dprintf(fd, "update_progress_threshold: %d\n", ds_.update_progress_threshold_);
+ dprintf(fd, "last_updated_progress: %d\n", ds_.last_updated_progress_);
+ dprintf(fd, "progress:\n");
+ ds_.progress_->Dump(fd, " ");
+ dprintf(fd, "args: %s\n", ds_.args_.c_str());
+ dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
+ dprintf(fd, "version: %s\n", ds_.version_.c_str());
+ dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.c_str());
+ dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str());
+ dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str());
+ dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str());
+ dprintf(fd, "path: %s\n", ds_.path_.c_str());
+ dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
+ dprintf(fd, "base_name: %s\n", ds_.base_name_.c_str());
+ dprintf(fd, "name: %s\n", ds_.name_.c_str());
+ dprintf(fd, "now: %ld\n", ds_.now_);
+ dprintf(fd, "is_zipping: %s\n", ds_.IsZipping() ? "true" : "false");
+ dprintf(fd, "listener: %s\n", ds_.listener_name_.c_str());
+ dprintf(fd, "notification title: %s\n", ds_.notification_title.c_str());
+ dprintf(fd, "notification description: %s\n", ds_.notification_description.c_str());
+
+ return NO_ERROR;
+}
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
new file mode 100644
index 0000000..4352d3d
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.h
@@ -0,0 +1,51 @@
+/**
+ * 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_OS_DUMPSTATE_H_
+#define ANDROID_OS_DUMPSTATE_H_
+
+#include <mutex>
+#include <vector>
+
+#include <binder/BinderService.h>
+
+#include "android/os/BnDumpstate.h"
+#include "android/os/BnDumpstateToken.h"
+#include "dumpstate.h"
+
+namespace android {
+namespace os {
+
+class DumpstateService : public BinderService<DumpstateService>, public BnDumpstate {
+ public:
+ DumpstateService();
+
+ static status_t Start();
+ static char const* getServiceName();
+
+ status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
+ sp<IDumpstateToken>* returned_token) override;
+
+ private:
+ Dumpstate& ds_;
+ std::mutex lock_;
+};
+
+} // namespace os
+} // namespace android
+
+#endif // ANDROID_OS_DUMPSTATE_H_
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
new file mode 100644
index 0000000..26702c4
--- /dev/null
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -0,0 +1,384 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateUtil.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#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"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+namespace {
+
+static constexpr const char* kSuPath = "/system/xbin/su";
+
+static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
+ sigset_t child_mask, old_mask;
+ sigemptyset(&child_mask);
+ sigaddset(&child_mask, SIGCHLD);
+
+ if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
+ printf("*** sigprocmask failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ timespec ts;
+ ts.tv_sec = timeout_seconds;
+ ts.tv_nsec = 0;
+ int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
+ int saved_errno = errno;
+ // Set the signals back the way they were.
+ if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
+ printf("*** sigprocmask failed: %s\n", strerror(errno));
+ if (ret == 0) {
+ return false;
+ }
+ }
+ if (ret == -1) {
+ errno = saved_errno;
+ if (errno == EAGAIN) {
+ errno = ETIMEDOUT;
+ } else {
+ printf("*** sigtimedwait failed: %s\n", strerror(errno));
+ }
+ return false;
+ }
+
+ pid_t child_pid = waitpid(pid, status, WNOHANG);
+ if (child_pid != pid) {
+ if (child_pid != -1) {
+ printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+ } else {
+ printf("*** waitpid failed: %s\n", strerror(errno));
+ }
+ return false;
+ }
+ return true;
+}
+} // unnamed namespace
+
+CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
+CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build();
+
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
+ values.always_ = true;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
+ values.account_mode_ = SU_ROOT;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
+ values.account_mode_ = DROP_ROOT;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
+ values.output_mode_ = REDIRECT_TO_STDERR;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
+ const std::string& message) {
+ values.logging_message_ = message;
+ return *this;
+}
+
+CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
+ return CommandOptions(values);
+}
+
+CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout)
+ : timeout_(timeout),
+ always_(false),
+ account_mode_(DONT_DROP_ROOT),
+ output_mode_(NORMAL_OUTPUT),
+ logging_message_("") {
+}
+
+CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
+}
+
+int64_t CommandOptions::Timeout() const {
+ return values.timeout_;
+}
+
+bool CommandOptions::Always() const {
+ return values.always_;
+}
+
+PrivilegeMode CommandOptions::PrivilegeMode() const {
+ return values.account_mode_;
+}
+
+OutputMode CommandOptions::OutputMode() const {
+ return values.output_mode_;
+}
+
+std::string CommandOptions::LoggingMessage() const {
+ return values.logging_message_;
+}
+
+CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) {
+ return CommandOptions::CommandOptionsBuilder(timeout);
+}
+
+std::string PropertiesHelper::build_type_ = "";
+int PropertiesHelper::dry_run_ = -1;
+
+bool PropertiesHelper::IsUserBuild() {
+ if (build_type_.empty()) {
+ build_type_ = android::base::GetProperty("ro.build.type", "user");
+ }
+ return "user" == build_type_;
+}
+
+bool PropertiesHelper::IsDryRun() {
+ if (dry_run_ == -1) {
+ dry_run_ = android::base::GetBoolProperty("dumpstate.dry_run", false) ? 1 : 0;
+ }
+ return dry_run_ == 1;
+}
+
+int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
+ int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+ if (fd < 0) {
+ int err = errno;
+ if (title.empty()) {
+ dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err));
+ } else {
+ dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(),
+ strerror(err));
+ }
+ fsync(out_fd);
+ return -1;
+ }
+ return DumpFileFromFdToFd(title, path, fd, out_fd, PropertiesHelper::IsDryRun());
+}
+
+int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options) {
+ if (full_command.empty()) {
+ MYLOGE("No arguments on RunCommandToFd(%s)\n", title.c_str());
+ return -1;
+ }
+
+ int size = full_command.size() + 1; // null terminated
+ int starting_index = 0;
+ if (options.PrivilegeMode() == SU_ROOT) {
+ starting_index = 2; // "su" "root"
+ size += starting_index;
+ }
+
+ std::vector<const char*> args;
+ args.resize(size);
+
+ std::string command_string;
+ if (options.PrivilegeMode() == SU_ROOT) {
+ args[0] = kSuPath;
+ command_string += kSuPath;
+ args[1] = "root";
+ command_string += " root ";
+ }
+ for (size_t i = 0; i < full_command.size(); i++) {
+ args[i + starting_index] = full_command[i].data();
+ command_string += args[i + starting_index];
+ if (i != full_command.size() - 1) {
+ command_string += " ";
+ }
+ }
+ args[size - 1] = nullptr;
+
+ const char* command = command_string.c_str();
+
+ if (options.PrivilegeMode() == SU_ROOT && PropertiesHelper::IsUserBuild()) {
+ dprintf(fd, "Skipping '%s' on user build.\n", command);
+ return 0;
+ }
+
+ if (!title.empty()) {
+ dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command);
+ fsync(fd);
+ }
+
+ const std::string& logging_message = options.LoggingMessage();
+ if (!logging_message.empty()) {
+ MYLOGI(logging_message.c_str(), command_string.c_str());
+ }
+
+ bool silent = (options.OutputMode() == REDIRECT_TO_STDERR);
+ bool redirecting_to_fd = STDOUT_FILENO != fd;
+
+ if (PropertiesHelper::IsDryRun() && !options.Always()) {
+ if (!title.empty()) {
+ dprintf(fd, "\t(skipped on dry run)\n");
+ } else if (redirecting_to_fd) {
+ // There is no title, but we should still print a dry-run message
+ dprintf(fd, "%s: skipped on dry run\n", command_string.c_str());
+ }
+ fsync(fd);
+ return 0;
+ }
+
+ const char* path = args[0];
+
+ uint64_t start = Nanotime();
+ pid_t pid = fork();
+
+ /* handle error case */
+ if (pid < 0) {
+ if (!silent) dprintf(fd, "*** fork: %s\n", strerror(errno));
+ MYLOGE("*** fork: %s\n", strerror(errno));
+ return pid;
+ }
+
+ /* handle child case */
+ if (pid == 0) {
+ if (options.PrivilegeMode() == DROP_ROOT && !DropRootUser()) {
+ if (!silent) {
+ dprintf(fd, "*** failed to drop root before running %s: %s\n", command,
+ strerror(errno));
+ }
+ MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
+ return -1;
+ }
+
+ if (silent) {
+ // Redirects stdout to stderr
+ TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO));
+ } else if (redirecting_to_fd) {
+ // Redirect stdout to fd
+ TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
+ close(fd);
+ }
+
+ /* make sure the child dies when dumpstate dies */
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+
+ /* just ignore SIGPIPE, will go down with parent's */
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sigact, NULL);
+
+ execvp(path, (char**)args.data());
+ // execvp's result will be handled after waitpid_with_timeout() below, but
+ // if it failed, it's safer to exit dumpstate.
+ MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
+ // Must call _exit (instead of exit), otherwise it will corrupt the zip
+ // file.
+ _exit(EXIT_FAILURE);
+ }
+
+ /* handle parent case */
+ int status;
+ bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
+ fsync(fd);
+
+ uint64_t elapsed = Nanotime() - start;
+ if (!ret) {
+ if (errno == ETIMEDOUT) {
+ if (!silent)
+ dprintf(fd, "*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+ static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+ MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+ static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+ } else {
+ if (!silent)
+ dprintf(fd, "*** command '%s': Error after %.4fs (killing pid %d)\n", command,
+ static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+ MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
+ static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+ }
+ kill(pid, SIGTERM);
+ if (!waitpid_with_timeout(pid, 5, nullptr)) {
+ kill(pid, SIGKILL);
+ if (!waitpid_with_timeout(pid, 5, nullptr)) {
+ if (!silent)
+ dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n",
+ command, pid);
+ MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
+ }
+ }
+ return -1;
+ }
+
+ if (WIFSIGNALED(status)) {
+ if (!silent)
+ dprintf(fd, "*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
+ MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
+ } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
+ status = WEXITSTATUS(status);
+ if (!silent) dprintf(fd, "*** command '%s' failed: exit code %d\n", command, status);
+ MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
+ }
+
+ 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
new file mode 100644
index 0000000..5a8ce5b
--- /dev/null
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -0,0 +1,186 @@
+/*
+ * 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_OS_DUMPSTATE_UTIL_H_
+#define ANDROID_OS_DUMPSTATE_UTIL_H_
+
+#include <cstdint>
+#include <string>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+/*
+ * Defines the Linux account that should be executing a command.
+ */
+enum PrivilegeMode {
+ /* Explicitly change the `uid` and `gid` to be `shell`.*/
+ DROP_ROOT,
+ /* Don't change the `uid` and `gid`. */
+ DONT_DROP_ROOT,
+ /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */
+ SU_ROOT
+};
+
+/*
+ * Defines what should happen with the main output stream (`stdout` or fd) of a command.
+ */
+enum OutputMode {
+ /* Don't change main output. */
+ NORMAL_OUTPUT,
+ /* Redirect main output to `stderr`. */
+ REDIRECT_TO_STDERR
+};
+
+/*
+ * Value object used to set command options.
+ *
+ * Typically constructed using a builder with chained setters. Examples:
+ *
+ * CommandOptions::WithTimeout(20).AsRoot().Build();
+ * CommandOptions::WithTimeout(10).Always().RedirectStderr().Build();
+ *
+ * Although the builder could be used to dynamically set values. Example:
+ *
+ * CommandOptions::CommandOptionsBuilder options =
+ * CommandOptions::WithTimeout(10);
+ * if (!is_user_build()) {
+ * options.AsRoot();
+ * }
+ * RunCommand("command", {"args"}, options.Build());
+ */
+class CommandOptions {
+ private:
+ class CommandOptionsValues {
+ private:
+ CommandOptionsValues(int64_t timeout);
+
+ int64_t timeout_;
+ bool always_;
+ PrivilegeMode account_mode_;
+ OutputMode output_mode_;
+ std::string logging_message_;
+
+ friend class CommandOptions;
+ friend class CommandOptionsBuilder;
+ };
+
+ CommandOptions(const CommandOptionsValues& values);
+
+ const CommandOptionsValues values;
+
+ public:
+ class CommandOptionsBuilder {
+ public:
+ /* Sets the command to always run, even on `dry-run` mode. */
+ CommandOptionsBuilder& Always();
+ /* Sets the command's PrivilegeMode as `SU_ROOT` */
+ CommandOptionsBuilder& AsRoot();
+ /* Sets the command's PrivilegeMode as `DROP_ROOT` */
+ CommandOptionsBuilder& DropRoot();
+ /* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */
+ CommandOptionsBuilder& RedirectStderr();
+ /* When not empty, logs a message before executing the command.
+ * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */
+ CommandOptionsBuilder& Log(const std::string& message);
+ /* Builds the command options. */
+ CommandOptions Build();
+
+ private:
+ CommandOptionsBuilder(int64_t timeout);
+ CommandOptionsValues values;
+ friend class CommandOptions;
+ };
+
+ /** Gets the command timeout, in seconds. */
+ int64_t Timeout() const;
+ /* Checks whether the command should always be run, even on dry-run mode. */
+ bool Always() const;
+ /** Gets the PrivilegeMode of the command. */
+ PrivilegeMode PrivilegeMode() const;
+ /** Gets the OutputMode of the command. */
+ OutputMode OutputMode() const;
+ /** Gets the logging message header, it any. */
+ std::string LoggingMessage() const;
+
+ /** Creates a builder with the requied timeout. */
+ static CommandOptionsBuilder WithTimeout(int64_t timeout);
+
+ // Common options.
+ static CommandOptions DEFAULT;
+ static CommandOptions AS_ROOT;
+};
+
+/*
+ * System properties helper.
+ */
+class PropertiesHelper {
+ friend class DumpstateBaseTest;
+
+ public:
+ /*
+ * Gets whether device is running a `user` build.
+ */
+ static bool IsUserBuild();
+
+ /*
+ * When running in dry-run mode, skips the real dumps and just print the section headers.
+ *
+ * Useful when debugging dumpstate or other bugreport-related activities.
+ *
+ * Dry-run mode is enabled by setting the system property `dumpstate.dry_run` to true.
+ */
+ static bool IsDryRun();
+
+ private:
+ static std::string build_type_;
+ static int dry_run_;
+};
+
+/*
+ * Forks a command, waits for it to finish, and returns its status.
+ *
+ * |fd| file descriptor that receives the command's 'stdout'.
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |full_command| array containing the command (first entry) and its arguments.
+ * Must contain at least one element.
+ * |options| optional argument defining the command's behavior.
+ */
+int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT);
+
+/*
+ * Dumps the contents of a file into a file descriptor.
+ *
+ * |fd| file descriptor where the file is dumped into.
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |path| location of the file to be dumped.
+ */
+int DumpFileToFd(int fd, const std::string& title, const std::string& path);
+
+/*
+ * 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/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
new file mode 100644
index 0000000..4becccf
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -0,0 +1,35 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+import android.os.IDumpstateListener;
+import android.os.IDumpstateToken;
+
+/**
+ * Binder interface for the currently running dumpstate process.
+ * {@hide}
+ */
+interface IDumpstate {
+
+ /*
+ * Sets the listener for this dumpstate progress.
+ *
+ * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already
+ * set (the listener behaves like a Highlander: There Can be Only One).
+ */
+ IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener);
+}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
new file mode 100644
index 0000000..32717f4
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * Listener for dumpstate events.
+ *
+ * {@hide}
+ */
+interface IDumpstateListener {
+ void onProgressUpdated(int progress);
+ void onMaxProgressUpdated(int maxProgress);
+}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl b/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
new file mode 100644
index 0000000..7f74ceb
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
@@ -0,0 +1,24 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * Token used by the IDumpstateListener to watch for dumpstate death.
+ * {@hide}
+ */
+interface IDumpstateToken {
+}
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index ca7d574..b995b80 100644
--- a/cmds/dumpstate/bugreport-format.md
+++ b/cmds/dumpstate/bugreport-format.md
@@ -22,7 +22,7 @@
file as the `ACTION_SEND_MULTIPLE` attachment.
## Version 1.0 (Android N)
-On _Android N (TBD)_, `dumpstate` generates a zip file directly (unless there
+On _Android N (Nougat)_, `dumpstate` generates a zip file directly (unless there
is a failure, in which case it reverts to the flat file that is zipped by
**Shell** and hence the end result is the _v0_ format).
@@ -55,6 +55,10 @@
- `title.txt`: whose value is a single-line summary of the problem.
- `description.txt`: whose value is a multi-line, detailed description of the problem.
+## Android O versions
+On _Android O (OhMightyAndroidWhatsYourNextReleaseName?)_, the following changes were made:
+- The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-1`).
+
## Intermediate versions
During development, the versions will be suffixed with _-devX_ or
_-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the
@@ -63,8 +67,8 @@
For example, the initial version during _Android N_ development was
**1.0-dev1**. When `dumpsys` was split in 2 sections but not all tools were
ready to parse that format, the version was named **1.0-dev2**,
-which had to be passed do `dumpsys` explicitly (i.e., trhough a
-`-V 1.0-dev2` argument). Once that format became stable and tools
+which had to be passed to `dumpsys` explicitly (by setting the `dumpstate.version` system property).
+Once that format became stable and tools
knew how to parse it, the default version became **1.0-dev2**.
Similarly, if changes in the file format are made after the initial release of
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 0929d9b..f84d86d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "dumpstate"
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -35,38 +37,37 @@
#include <unistd.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
+#include <cutils/native_handle.h>
#include <cutils/properties.h>
+#include <openssl/sha.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
-#include "private/android_filesystem_config.h"
-
-#define LOG_TAG "dumpstate"
-#include <cutils/log.h>
-
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
#include "dumpstate.h"
-#include "ScopedFd.h"
-#include "ziparchive/zip_writer.h"
-#include "mincrypt/sha256.h"
+using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
-using android::base::StringPrintf;
+// 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;
-// TODO: variables below should be part of dumpstate object
-static unsigned long id;
-static char build_type[PROPERTY_VALUE_MAX];
-static time_t now;
-static std::unique_ptr<ZipWriter> zip_writer;
+// TODO: variables and functions below should be part of dumpstate object
+
static std::set<std::string> mount_points;
void add_mountinfo();
-int control_socket_fd = -1;
-/* suffix of the bugreport files - it's typically the date (when invoked with -d),
- * although it could be changed by the user using a system property */
-static std::string suffix;
#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
@@ -91,41 +92,57 @@
static tombstone_data_t tombstone_data[NUM_TOMBSTONES];
-const std::string ZIP_ROOT_DIR = "FS";
-std::string bugreport_dir;
-
-/*
- * List of supported zip format versions.
- *
- * See bugreport-format.txt for more info.
- */
-static std::string VERSION_DEFAULT = "1.0";
-
-bool is_user_build() {
- return 0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1);
+// TODO: temporary variables and functions used during C++ refactoring
+static Dumpstate& ds = Dumpstate::GetInstance();
+static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ return ds.RunCommand(title, fullCommand, options);
+}
+static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+ const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
+ long dumpsysTimeout = 0) {
+ return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout);
+}
+static int DumpFile(const std::string& title, const std::string& path) {
+ return ds.DumpFile(title, path);
}
-/* gets the tombstone data, according to the bugreport type: if zipped gets all tombstones,
- * otherwise gets just those modified in the last half an hour. */
+// 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 const std::string kLsHalDebugPath = "/bugreports/dumpstate_lshal.txt";
+
+static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
+static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
+static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
+static constexpr char PROPERTY_EXTRA_TITLE[] = "dumpstate.options.title";
+static constexpr char PROPERTY_EXTRA_DESCRIPTION[] = "dumpstate.options.description";
+
+static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
+
+/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
+ * otherwise, gets just those modified in the last half an hour. */
static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
- time_t thirty_minutes_ago = now - 60*30;
+ time_t thirty_minutes_ago = ds.now_ - 60 * 30;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
int fd = TEMP_FAILURE_RETRY(open(data[i].name,
O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
struct stat st;
- if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
- (zip_writer || (time_t) st.st_mtime >= thirty_minutes_ago)) {
- data[i].fd = fd;
+ if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
+ (ds.IsZipping() || st.st_mtime >= thirty_minutes_ago)) {
+ data[i].fd = fd;
} else {
- close(fd);
+ close(fd);
data[i].fd = -1;
}
}
}
// for_each_pid() callback to get mount info about a process.
-void do_mountinfo(int pid, const char *name) {
+void do_mountinfo(int pid, const char* name __attribute__((unused))) {
char path[PATH_MAX];
// Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points
@@ -142,7 +159,7 @@
if (mount_points.find(linkname) == mount_points.end()) {
// First time this mount point was found: add it
snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid);
- if (add_zip_entry(ZIP_ROOT_DIR + path, path)) {
+ if (ds.AddZipEntry(ZIP_ROOT_DIR + path, path)) {
mount_points.insert(linkname);
} else {
MYLOGE("Unable to add mountinfo %s to zip file\n", path);
@@ -151,12 +168,12 @@
}
void add_mountinfo() {
- if (!is_zipping()) return;
- const char *title = "MOUNT INFO";
+ if (!ds.IsZipping()) return;
+ std::string title = "MOUNT INFO";
mount_points.clear();
- DurationReporter duration_reporter(title, NULL);
- for_each_pid(do_mountinfo, NULL);
- MYLOGD("%s: %d entries added to zip file\n", title, (int) mount_points.size());
+ DurationReporter duration_reporter(title, true);
+ for_each_pid(do_mountinfo, nullptr);
+ MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size());
}
static void dump_dev_files(const char *title, const char *driverpath, const char *filename)
@@ -175,40 +192,13 @@
continue;
}
snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename);
- dump_file(title, path);
+ DumpFile(title, path);
}
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.
@@ -225,13 +215,13 @@
long long cur_size = 0;
const char *trace_path = "/data/misc/anrd/";
- if (!zip_writer) {
- MYLOGE("Not dumping anrd trace because zip_writer is not set\n");
+ if (!ds.IsZipping()) {
+ MYLOGE("Not dumping anrd trace because it's not a zipped bugreport\n");
return false;
}
// 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) {
@@ -243,7 +233,8 @@
// send SIGUSR1 to the anrd to generate a trace.
sprintf(buf, "%u", pid);
- if (run_command("ANRD_DUMP", 1, "kill", "-SIGUSR1", buf, NULL)) {
+ if (RunCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf},
+ CommandOptions::WithTimeout(1).Build())) {
MYLOGE("anrd signal timed out. Please manually collect trace\n");
return false;
}
@@ -296,7 +287,7 @@
}
}
// Add to the zip file.
- if (!add_zip_entry("anrd_trace.txt", path)) {
+ if (!ds.AddZipEntry("anrd_trace.txt", path)) {
MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
} else {
if (remove(path)) {
@@ -312,11 +303,11 @@
}
static void dump_systrace() {
- if (!is_zipping()) {
- MYLOGD("Not dumping systrace because dumpstate is not zipping\n");
+ if (!ds.IsZipping()) {
+ MYLOGD("Not dumping systrace because it's not a zipped bugreport\n");
return;
}
- std::string systrace_path = bugreport_dir + "/systrace-" + suffix + ".txt";
+ std::string systrace_path = ds.GetPath("-systrace.txt");
if (systrace_path.empty()) {
MYLOGE("Not dumping systrace because path is empty\n");
return;
@@ -333,17 +324,17 @@
MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes",
systrace_path.c_str());
- if (run_command("SYSTRACE", 120, "/system/bin/atrace", "--async_dump", "-o",
- systrace_path.c_str(), NULL)) {
+ if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path},
+ CommandOptions::WithTimeout(120).Build())) {
MYLOGE("systrace timed out, its zip entry will be incomplete\n");
- // TODO: run_command tries to kill the process, but atrace doesn't die peacefully; ideally,
- // we should call strace to stop itself, but there is no such option yet (just a
- // --async_stop, which stops and dump
- // if (run_command("SYSTRACE", 10, "/system/bin/atrace", "--kill", NULL)) {
- // MYLOGE("could not stop systrace ");
- // }
+ // TODO: RunCommand tries to kill the process, but atrace doesn't die
+ // peacefully; ideally, we should call strace to stop itself, but there is no such option
+ // yet (just a --async_stop, which stops and dump
+ // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) {
+ // MYLOGE("could not stop systrace ");
+ // }
}
- if (!add_zip_entry("systrace.txt", systrace_path)) {
+ if (!ds.AddZipEntry("systrace.txt", systrace_path)) {
MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
} else {
if (remove(systrace_path.c_str())) {
@@ -353,13 +344,13 @@
}
static void dump_raft() {
- if (is_user_build()) {
+ if (PropertiesHelper::IsUserBuild()) {
return;
}
- std::string raft_log_path = bugreport_dir + "/raft_log.txt";
- if (raft_log_path.empty()) {
- MYLOGD("raft_log_path is empty\n");
+ std::string raft_path = ds.GetPath("-raft_log.txt");
+ if (raft_path.empty()) {
+ MYLOGD("raft_path is empty\n");
return;
}
@@ -369,29 +360,30 @@
return;
}
- if (!is_zipping()) {
- // Write compressed and encoded raft logs to stdout if not zip_writer.
- run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
+ CommandOptions options = CommandOptions::WithTimeout(600).Build();
+ if (!ds.IsZipping()) {
+ // Write compressed and encoded raft logs to stdout if it's not a zipped bugreport.
+ RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options);
return;
}
- run_command("RAFT LOGS", 600, "logcompressor", "-n", "-r", RAFT_DIR,
- "-o", raft_log_path.c_str(), NULL);
- if (!add_zip_entry("raft_log.txt", raft_log_path)) {
- MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
+ RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_path}, options);
+ if (!ds.AddZipEntry("raft_log.txt", raft_path)) {
+ MYLOGE("Unable to add raft log %s to zip file\n", raft_path.c_str());
} else {
- if (remove(raft_log_path.c_str())) {
- MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
+ if (remove(raft_path.c_str())) {
+ MYLOGE("Error removing raft file %s: %s\n", raft_path.c_str(), strerror(errno));
}
}
}
/**
- * Finds the last modified file in the directory dir whose name starts with file_prefix
+ * Finds the last modified file in the directory dir whose name starts with file_prefix.
+ *
* Function returns empty string when it does not find a file
*/
-static std::string get_last_modified_file_matching_prefix(const std::string& dir,
- const std::string& file_prefix) {
+static std::string GetLastModifiedFileWithPrefix(const std::string& dir,
+ const std::string& file_prefix) {
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dir.c_str()), closedir);
if (d == nullptr) {
MYLOGD("Error %d opening %s\n", errno, dir.c_str());
@@ -400,7 +392,7 @@
// Find the newest file matching the file_prefix in dir
struct dirent *de;
- time_t last_modified = 0;
+ time_t last_modified_time = 0;
std::string last_modified_file = "";
struct stat s;
@@ -412,39 +404,43 @@
file = dir + "/" + file;
int ret = stat(file.c_str(), &s);
- if ((ret == 0) && (s.st_mtime > last_modified)) {
+ if ((ret == 0) && (s.st_mtime > last_modified_time)) {
last_modified_file = file;
- last_modified = s.st_mtime;
+ last_modified_time = s.st_mtime;
}
}
return last_modified_file;
}
-void dump_modem_logs() {
- DurationReporter duration_reporter("dump_modem_logs");
- if (is_user_build()) {
+static void DumpModemLogs() {
+ DurationReporter durationReporter("DUMP MODEM LOGS");
+ if (PropertiesHelper::IsUserBuild()) {
return;
}
- if (!is_zipping()) {
+ if (!ds.IsZipping()) {
MYLOGD("Not dumping modem logs. dumpstate is not generating a zipping bugreport\n");
return;
}
- char property[PROPERTY_VALUE_MAX];
- property_get("ro.radio.log_prefix", property, "");
- std::string file_prefix = std::string(property);
+ std::string file_prefix = android::base::GetProperty("ro.radio.log_prefix", "");
+
if(file_prefix.empty()) {
MYLOGD("No modem log : file_prefix is empty\n");
return;
}
- MYLOGD("dump_modem_logs: directory is %s and file_prefix is %s\n",
- bugreport_dir.c_str(), file_prefix.c_str());
+ // TODO: b/33820081 we need to provide a right way to dump modem logs.
+ std::string radio_bugreport_dir = android::base::GetProperty("ro.radio.log_loc", "");
+ if (radio_bugreport_dir.empty()) {
+ radio_bugreport_dir = dirname(ds.GetPath("").c_str());
+ }
- std::string modem_log_file =
- get_last_modified_file_matching_prefix(bugreport_dir, file_prefix);
+ MYLOGD("DumpModemLogs: directory is %s and file_prefix is %s\n",
+ radio_bugreport_dir.c_str(), file_prefix.c_str());
+
+ std::string modem_log_file = GetLastModifiedFileWithPrefix(radio_bugreport_dir, file_prefix);
struct stat s;
if (modem_log_file.empty() || stat(modem_log_file.c_str(), &s) != 0) {
@@ -453,7 +449,7 @@
}
std::string filename = basename(modem_log_file.c_str());
- if (!add_zip_entry(filename, modem_log_file)) {
+ if (!ds.AddZipEntry(filename, modem_log_file)) {
MYLOGE("Unable to add modem log %s to zip file\n", modem_log_file.c_str());
} else {
MYLOGD("Modem Log %s is added to zip\n", modem_log_file.c_str());
@@ -472,7 +468,7 @@
return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */
}
-static bool skip_none(const char *path) {
+static bool skip_none(const char* path __attribute__((unused))) {
return false;
}
@@ -631,11 +627,10 @@
/ fields[__STAT_IO_TICKS];
if (!write_perf && !write_ios) {
- printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n",
- path, read_perf, read_ios, queue);
+ printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n", path, read_perf, read_ios, queue);
} else {
- printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n",
- path, read_perf, read_ios, write_perf, write_ios, queue);
+ printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n", path, read_perf,
+ read_ios, write_perf, write_ios, queue);
}
/* bugreport timeout factor adjustment */
@@ -646,132 +641,43 @@
return 0;
}
-/* Copied policy from system/core/logd/LogBuffer.cpp */
-
-#define LOG_BUFFER_SIZE (256 * 1024)
-#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
-#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
-
-static bool valid_size(unsigned long value) {
- if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
- return false;
- }
-
- long pages = sysconf(_SC_PHYS_PAGES);
- if (pages < 1) {
- return true;
- }
-
- long pagesize = sysconf(_SC_PAGESIZE);
- if (pagesize <= 1) {
- pagesize = PAGE_SIZE;
- }
-
- // maximum memory impact a somewhat arbitrary ~3%
- pages = (pages + 31) / 32;
- unsigned long maximum = pages * pagesize;
-
- if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
- return true;
- }
-
- return value <= maximum;
-}
-
-static unsigned long property_get_size(const char *key) {
- unsigned long value;
- char *cp, property[PROPERTY_VALUE_MAX];
-
- property_get(key, property, "");
- value = strtoul(property, &cp, 10);
-
- switch(*cp) {
- case 'm':
- case 'M':
- value *= 1024;
- /* FALLTHRU */
- case 'k':
- case 'K':
- value *= 1024;
- /* FALLTHRU */
- case '\0':
- break;
-
- default:
- value = 0;
- }
-
- if (!valid_size(value)) {
- value = 0;
- }
-
- return value;
-}
-
/* timeout in ms */
static unsigned long logcat_timeout(const char *name) {
- static const char global_tuneable[] = "persist.logd.size"; // Settings App
- static const char global_default[] = "ro.logd.size"; // BoardConfig.mk
- char key[PROP_NAME_MAX];
- unsigned long property_size, default_size;
-
- default_size = property_get_size(global_tuneable);
- if (!default_size) {
- default_size = property_get_size(global_default);
- }
-
- snprintf(key, sizeof(key), "%s.%s", global_tuneable, name);
- property_size = property_get_size(key);
-
- if (!property_size) {
- snprintf(key, sizeof(key), "%s.%s", global_default, name);
- property_size = property_get_size(key);
- }
-
- if (!property_size) {
- property_size = default_size;
- }
-
- if (!property_size) {
- property_size = LOG_BUFFER_SIZE;
- }
-
+ log_id_t id = android_name_to_log_id(name);
+ unsigned long property_size = __android_logger_get_buffer_size(id);
/* Engineering margin is ten-fold our guess */
return 10 * (property_size + worst_write_perf) / worst_write_perf;
}
-/* End copy from system/core/logd/LogBuffer.cpp */
+void Dumpstate::PrintHeader() const {
+ std::string build, fingerprint, radio, bootloader, network;
+ char date[80];
-/* dumps the current system state to stdout */
-static void print_header(std::string version) {
- char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
- char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
- char network[PROPERTY_VALUE_MAX], date[80];
-
- property_get("ro.build.display.id", build, "(unknown)");
- property_get("ro.build.fingerprint", fingerprint, "(unknown)");
- property_get("ro.build.type", build_type, "(unknown)");
- property_get("gsm.version.baseband", radio, "(unknown)");
- property_get("ro.bootloader", bootloader, "(unknown)");
- property_get("gsm.operator.alpha", network, "(unknown)");
- strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
+ build = android::base::GetProperty("ro.build.display.id", "(unknown)");
+ fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)");
+ radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
+ bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
+ network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
+ strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));
printf("========================================================\n");
printf("== dumpstate: %s\n", date);
printf("========================================================\n");
printf("\n");
- printf("Build: %s\n", build);
- printf("Build fingerprint: '%s'\n", fingerprint); /* format is important for other tools */
- printf("Bootloader: %s\n", bootloader);
- printf("Radio: %s\n", radio);
- printf("Network: %s\n", network);
+ printf("Build: %s\n", build.c_str());
+ // NOTE: fingerprint entry format is important for other tools.
+ printf("Build fingerprint: '%s'\n", fingerprint.c_str());
+ printf("Bootloader: %s\n", bootloader.c_str());
+ printf("Radio: %s\n", radio.c_str());
+ printf("Network: %s\n", network.c_str());
printf("Kernel: ");
- dump_file(NULL, "/proc/version");
+ DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
- printf("Bugreport format version: %s\n", version.c_str());
- printf("Dumpstate info: id=%lu pid=%d\n", id, getpid());
+ printf("Bugreport format version: %s\n", version_.c_str());
+ printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
+ PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
printf("\n");
}
@@ -783,10 +689,10 @@
".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
};
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
- if (!is_zipping()) {
- MYLOGD("Not adding entry %s from fd because dumpstate is not zipping\n",
- entry_name.c_str());
+bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n",
+ entry_name.c_str());
return false;
}
std::string valid_name = entry_name;
@@ -804,250 +710,305 @@
// Logging statement below is useful to time how long each entry takes, but it's too verbose.
// MYLOGD("Adding zip entry %s\n", entry_name.c_str());
- int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(),
- ZipWriter::kCompress, get_mtime(fd, now));
- if (err) {
- MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
+ get_mtime(fd, ds.now_));
+ if (err != 0) {
+ MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
std::vector<uint8_t> buffer(65536);
while (1) {
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), sizeof(buffer)));
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size()));
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
return false;
}
- err = zip_writer->WriteBytes(buffer.data(), bytes_read);
+ err = zip_writer_->WriteBytes(buffer.data(), bytes_read);
if (err) {
- MYLOGE("zip_writer->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
+ MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
}
- err = zip_writer->FinishEntry();
- if (err) {
- MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->FinishEntry();
+ if (err != 0) {
+ MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
return true;
}
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path) {
- ScopedFd fd(TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
- if (fd.get() == -1) {
+bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
+ if (fd == -1) {
MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno));
return false;
}
- return add_zip_entry_from_fd(entry_name, fd.get());
+ return AddZipEntryFromFd(entry_name, fd.get());
}
/* adds a file to the existing zipped bugreport */
-static int _add_file_from_fd(const char *title, const char *path, int fd) {
- return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
+static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
+ return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
}
-// TODO: move to util.cpp
-void add_dir(const char *dir, bool recursive) {
- if (!is_zipping()) {
- MYLOGD("Not adding dir %s because dumpstate is not zipping\n", dir);
+void Dumpstate::AddDir(const std::string& dir, bool recursive) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str());
return;
}
- MYLOGD("Adding dir %s (recursive: %d)\n", dir, recursive);
- DurationReporter duration_reporter(dir, NULL);
- dump_files(NULL, dir, recursive ? skip_none : is_dir, _add_file_from_fd);
+ MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive);
+ DurationReporter duration_reporter(dir, true);
+ dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
}
-bool is_zipping() {
- return zip_writer != nullptr;
-}
-
-/* adds a text entry entry to the existing zip file. */
-static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
- if (!is_zipping()) {
- MYLOGD("Not adding text entry %s because dumpstate is not zipping\n", entry_name.c_str());
+bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n",
+ entry_name.c_str());
return false;
}
MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
- int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, now);
- if (err) {
- MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
+ if (err != 0) {
+ MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
- err = zip_writer->WriteBytes(content.c_str(), content.length());
- if (err) {
- MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->WriteBytes(content.c_str(), content.length());
+ if (err != 0) {
+ MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
- err = zip_writer->FinishEntry();
- if (err) {
- MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->FinishEntry();
+ if (err != 0) {
+ MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
return true;
}
-static void dump_iptables() {
- run_command("IPTABLES", 10, "iptables", "-L", "-nvx", NULL);
- run_command("IP6TABLES", 10, "ip6tables", "-L", "-nvx", NULL);
- run_command("IPTABLES NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
- /* no ip6 nat */
- run_command("IPTABLES MANGLE", 10, "iptables", "-t", "mangle", "-L", "-nvx", NULL);
- run_command("IP6TABLES MANGLE", 10, "ip6tables", "-t", "mangle", "-L", "-nvx", NULL);
- run_command("IPTABLES RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
- run_command("IP6TABLES RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
-}
-
-static void do_kmsg() {
+static void DoKmsg() {
struct stat st;
if (!stat(PSTORE_LAST_KMSG, &st)) {
/* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
- dump_file("LAST KMSG", PSTORE_LAST_KMSG);
+ DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
} else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
- dump_file("LAST KMSG", ALT_PSTORE_LAST_KMSG);
+ DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG);
} else {
/* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
- dump_file("LAST KMSG", "/proc/last_kmsg");
+ DumpFile("LAST KMSG", "/proc/last_kmsg");
}
}
-static void do_logcat() {
+static void DoLogcat() {
unsigned long timeout;
- // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
+ // 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;
}
- run_command("SYSTEM LOG", timeout / 1000, "logcat", "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
+ RunCommand("SYSTEM LOG",
+ {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid",
+ "-d", "*:v"},
+ CommandOptions::WithTimeout(timeout / 1000).Build());
timeout = logcat_timeout("events");
if (timeout < 20000) {
timeout = 20000;
}
- run_command("EVENT LOG", timeout / 1000, "logcat", "-b", "events",
- "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
+ RunCommand("EVENT LOG",
+ {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid",
+ "-d", "*:v"},
+ CommandOptions::WithTimeout(timeout / 1000).Build());
timeout = logcat_timeout("radio");
if (timeout < 20000) {
timeout = 20000;
}
- run_command("RADIO LOG", timeout / 1000, "logcat", "-b", "radio",
- "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
+ RunCommand("RADIO LOG",
+ {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid",
+ "-d", "*:v"},
+ CommandOptions::WithTimeout(timeout / 1000).Build());
- run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
+ RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
/* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
- run_command("LAST LOGCAT", 10, "logcat", "-L",
- "-b", "all",
- "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
+ RunCommand("LAST LOGCAT",
+ {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid",
+ "-d", "*:v"});
}
-static void dumpstate(const std::string& screenshot_path, const std::string& version) {
+static void DumpIpTables() {
+ RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
+ RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
+ RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
+ /* no ip6 nat */
+ RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
+ RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
+ RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
+ RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
+}
+
+static void AddAnrTraceFiles() {
+ bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+ std::string dump_traces_dir;
+
+ /* show the traces we collected in main(), if that was done */
+ if (dump_traces_path != nullptr) {
+ if (add_to_zip) {
+ dump_traces_dir = dirname(dump_traces_path);
+ MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
+ ds.AddDir(dump_traces_dir, true);
+ } else {
+ MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
+ dump_traces_path);
+ ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
+ }
+ }
+
+ std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ std::string anr_traces_dir = dirname(anr_traces_path.c_str());
+
+ // Make sure directory is not added twice.
+ // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
+ // generated by dump_traces() - and anr_traces_path - which is retrieved from a system
+ // property - but in reality they're the same path (although the former could be nullptr).
+ // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
+ // be revisited.
+ bool already_dumped = anr_traces_dir == dump_traces_dir;
+
+ MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
+ dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
+
+ if (anr_traces_path.empty()) {
+ printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
+ } else {
+ int fd = TEMP_FAILURE_RETRY(
+ open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+ if (fd < 0) {
+ printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
+ strerror(errno));
+ } else {
+ if (add_to_zip) {
+ if (!already_dumped) {
+ MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
+ anr_traces_dir.c_str());
+ ds.AddDir(anr_traces_dir, true);
+ already_dumped = true;
+ }
+ } else {
+ MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
+ anr_traces_path.c_str());
+ dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
+ }
+ }
+ }
+
+ if (add_to_zip && already_dumped) {
+ MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
+ return;
+ }
+
+ /* slow traces for slow operations */
+ struct stat st;
+ if (!anr_traces_path.empty()) {
+ int tail = anr_traces_path.size() - 1;
+ while (tail > 0 && anr_traces_path.at(tail) != '/') {
+ tail--;
+ }
+ int i = 0;
+ while (1) {
+ anr_traces_path = anr_traces_path.substr(0, tail + 1) +
+ android::base::StringPrintf("slow%02d.txt", i);
+ if (stat(anr_traces_path.c_str(), &st)) {
+ // No traces file at this index, done with the files.
+ break;
+ }
+ ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
+ i++;
+ }
+ }
+}
+
+static void dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
- run_command("UPTIME", 10, "uptime", NULL);
+ RunCommand("UPTIME", {"uptime"});
dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
- dump_file("MEMORY INFO", "/proc/meminfo");
- run_command("CPU INFO", 10, "top", "-n", "1", "-d", "1", "-m", "30", "-H", NULL);
- run_command("PROCRANK", 20, SU_PATH, "root", "procrank", NULL);
- dump_file("VIRTUAL MEMORY STATS", "/proc/vmstat");
- dump_file("VMALLOC INFO", "/proc/vmallocinfo");
- dump_file("SLAB INFO", "/proc/slabinfo");
- dump_file("ZONEINFO", "/proc/zoneinfo");
- dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
- dump_file("BUDDYINFO", "/proc/buddyinfo");
- dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
+ DumpFile("MEMORY INFO", "/proc/meminfo");
+ RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
+ "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
+ RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20);
+ DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
+ DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
+ DumpFile("SLAB INFO", "/proc/slabinfo");
+ DumpFile("ZONEINFO", "/proc/zoneinfo");
+ DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo");
+ DumpFile("BUDDYINFO", "/proc/buddyinfo");
+ DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
- dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
- dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
- dump_file("KERNEL SYNC", "/d/sync");
+ DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources");
+ DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
+ DumpFile("KERNEL SYNC", "/d/sync");
- run_command("PROCESSES AND THREADS", 10, "ps", "-Z", "-t", "-p", "-P", NULL);
- run_command("LIBRANK", 10, SU_PATH, "root", "librank", NULL);
+ RunCommand("PROCESSES AND THREADS",
+ {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
+ RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT);
- run_command("PRINTENV", 10, "printenv", NULL);
- run_command("NETSTAT", 10, "netstat", "-n", NULL);
- run_command("LSMOD", 10, "lsmod", NULL);
+ if (ds.IsZipping()) {
+ RunCommand(
+ "HARDWARE HALS",
+ {"lshal", std::string("--debug=") + kLsHalDebugPath},
+ CommandOptions::AS_ROOT);
+
+ ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath);
+
+ unlink(kLsHalDebugPath.c_str());
+ } else {
+ RunCommand(
+ "HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::AS_ROOT);
+ }
+
+ RunCommand("PRINTENV", {"printenv"});
+ RunCommand("NETSTAT", {"netstat", "-nW"});
+ struct stat s;
+ if (stat("/proc/modules", &s) != 0) {
+ MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
+ } else {
+ RunCommand("LSMOD", {"lsmod"});
+ }
do_dmesg();
- run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
+ RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
/* Dump Bluetooth HCI logs */
- add_dir("/data/misc/bluetooth/logs", true);
+ ds.AddDir("/data/misc/bluetooth/logs", true);
- if (!screenshot_path.empty()) {
+ if (!ds.do_early_screenshot_) {
MYLOGI("taking late screenshot\n");
- take_screenshot(screenshot_path);
- MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
+ ds.TakeScreenshot();
}
- do_logcat();
+ DoLogcat();
- /* show the traces we collected in main(), if that was done */
- if (dump_traces_path != NULL) {
- dump_file("VM TRACES JUST NOW", dump_traces_path);
- }
-
- /* only show ANR traces if they're less than 15 minutes old */
- struct stat st;
- char anr_traces_path[PATH_MAX];
- property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
- if (!anr_traces_path[0]) {
- printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
- } else {
- int fd = TEMP_FAILURE_RETRY(open(anr_traces_path,
- O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
- if (fd < 0) {
- printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
- } else {
- dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
- }
- }
-
- /* slow traces for slow operations */
- if (anr_traces_path[0] != 0) {
- int tail = strlen(anr_traces_path)-1;
- while (tail > 0 && anr_traces_path[tail] != '/') {
- tail--;
- }
- int i = 0;
- while (1) {
- sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
- if (stat(anr_traces_path, &st)) {
- // No traces file at this index, done with the files.
- break;
- }
- dump_file("VM TRACES WHEN SLOW", anr_traces_path);
- i++;
- }
- }
+ AddAnrTraceFiles();
int dumped = 0;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -1055,8 +1016,8 @@
const char *name = tombstone_data[i].name;
int fd = tombstone_data[i].fd;
dumped = 1;
- if (zip_writer) {
- if (!add_zip_entry_from_fd(ZIP_ROOT_DIR + name, fd)) {
+ if (ds.IsZipping()) {
+ if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
MYLOGE("Unable to add tombstone %s to zip file\n", name);
}
} else {
@@ -1070,229 +1031,294 @@
printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
}
- dump_file("NETWORK DEV INFO", "/proc/net/dev");
- dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
- dump_file("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
- dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
- dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
+ DumpFile("NETWORK DEV INFO", "/proc/net/dev");
+ DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
+ DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
+ DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
+ DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
- do_kmsg();
+ DoKmsg();
/* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
- run_command("NETWORK INTERFACES", 10, "ip", "link", NULL);
+ RunCommand("NETWORK INTERFACES", {"ip", "link"});
- run_command("IPv4 ADDRESSES", 10, "ip", "-4", "addr", "show", NULL);
- run_command("IPv6 ADDRESSES", 10, "ip", "-6", "addr", "show", NULL);
+ RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
+ RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
- run_command("IP RULES", 10, "ip", "rule", "show", NULL);
- run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
+ RunCommand("IP RULES", {"ip", "rule", "show"});
+ RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
dump_route_tables();
- run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL);
- run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL);
- run_command("MULTICAST ADDRESSES", 10, "ip", "maddr", NULL);
- run_command("WIFI NETWORKS", 20, "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
+ RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"});
+ RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
+ RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
+ RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
+ CommandOptions::WithTimeout(20).Build());
-#ifdef FWDUMP_bcmdhd
- run_command("ND OFFLOAD TABLE", 5,
- SU_PATH, "root", WLUTIL, "nd_hostip", NULL);
+ RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
+ CommandOptions::WithTimeout(10).Build());
- run_command("DUMP WIFI INTERNAL COUNTERS (1)", 20,
- SU_PATH, "root", WLUTIL, "counters", NULL);
+ RunCommand("SYSTEM PROPERTIES", {"getprop"});
- run_command("ND OFFLOAD STATUS (1)", 5,
- SU_PATH, "root", WLUTIL, "nd_status", NULL);
+ RunCommand("VOLD DUMP", {"vdc", "dump"});
+ RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
-#endif
- dump_file("INTERRUPTS (1)", "/proc/interrupts");
+ RunCommand("STORAGED TASKIOINFO", {"storaged", "-u"}, CommandOptions::WithTimeout(10).Build());
- run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "-t", "10", "connectivity", "--diag", NULL);
+ RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
-#ifdef FWDUMP_bcmdhd
- run_command("DUMP WIFI STATUS", 20,
- SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", NULL);
-
- run_command("DUMP WIFI INTERNAL COUNTERS (2)", 20,
- SU_PATH, "root", WLUTIL, "counters", NULL);
-
- run_command("ND OFFLOAD STATUS (2)", 5,
- SU_PATH, "root", WLUTIL, "nd_status", NULL);
-#endif
- dump_file("INTERRUPTS (2)", "/proc/interrupts");
-
- print_properties();
-
- run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
- run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
-
- run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
-
- run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
+ RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
printf("------ BACKLIGHTS ------\n");
printf("LCD brightness=");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");
+ DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
printf("Button brightness=");
- dump_file(NULL, "/sys/class/leds/button-backlight/brightness");
+ DumpFile("", "/sys/class/leds/button-backlight/brightness");
printf("Keyboard brightness=");
- dump_file(NULL, "/sys/class/leds/keyboard-backlight/brightness");
+ DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
printf("ALS mode=");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/als");
+ DumpFile("", "/sys/class/leds/lcd-backlight/als");
printf("LCD driver registers:\n");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/registers");
+ DumpFile("", "/sys/class/leds/lcd-backlight/registers");
printf("\n");
/* Binder state is expensive to look at as it uses a lot of memory. */
- dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
- dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
- dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
- dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
- dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
+ DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
+ DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
+ DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
+ DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
+ DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
- printf("========================================================\n");
- printf("== Board\n");
- printf("========================================================\n");
+ ds.DumpstateBoard();
- dumpstate_board();
- printf("\n");
-
- /* Migrate the ril_dumpstate to a dumpstate_board()? */
- char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0};
- property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30");
- if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) {
- if (is_user_build()) {
- // su does not exist on user builds, so try running without it.
- // This way any implementations of vril-dump that do not require
- // root can run on user builds.
- run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
- "vril-dump", NULL);
- } else {
- run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
- SU_PATH, "root", "vril-dump", NULL);
+ /* 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.
+ // This way any implementations of vril-dump that do not require
+ // root can run on user builds.
+ CommandOptions::CommandOptionsBuilder options =
+ CommandOptions::WithTimeout(rilDumpstateTimeout);
+ if (!PropertiesHelper::IsUserBuild()) {
+ options.AsRoot();
}
+ RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
}
printf("========================================================\n");
printf("== Android Framework Services\n");
printf("========================================================\n");
- run_command("DUMPSYS", 60, "dumpsys", "-t", "60", "--skip", "meminfo", "cpuinfo", NULL);
+ RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(),
+ 10);
printf("========================================================\n");
printf("== Checkins\n");
printf("========================================================\n");
- run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "-t", "30", "batterystats", "-c", NULL);
- run_command("CHECKIN MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "--checkin", NULL);
- run_command("CHECKIN NETSTATS", 30, "dumpsys", "-t", "30", "netstats", "--checkin", NULL);
- run_command("CHECKIN PROCSTATS", 30, "dumpsys", "-t", "30", "procstats", "-c", NULL);
- run_command("CHECKIN USAGESTATS", 30, "dumpsys", "-t", "30", "usagestats", "-c", NULL);
- run_command("CHECKIN PACKAGE", 30, "dumpsys", "-t", "30", "package", "--checkin", NULL);
+ RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+ RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
+ RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
+ RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
+ RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
+ RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
printf("========================================================\n");
printf("== Running Application Activities\n");
printf("========================================================\n");
- run_command("APP ACTIVITIES", 30, "dumpsys", "-t", "30", "activity", "all", NULL);
+ RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"});
printf("========================================================\n");
printf("== Running Application Services\n");
printf("========================================================\n");
- run_command("APP SERVICES", 30, "dumpsys", "-t", "30", "activity", "service", "all", NULL);
+ RunDumpsys("APP SERVICES", {"activity", "service", "all"});
printf("========================================================\n");
printf("== Running Application Providers\n");
printf("========================================================\n");
- run_command("APP PROVIDERS", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
+ RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
- // dump_modem_logs adds the modem logs if available to the bugreport.
+ printf("========================================================\n");
+ printf("== Dropbox crashes\n");
+ printf("========================================================\n");
+
+ RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"});
+ RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"});
+
+ // DumpModemLogs adds the modem logs if available to the bugreport.
// Do this at the end to allow for sufficient time for the modem logs to be
// collected.
- dump_modem_logs();
+ DumpModemLogs();
printf("========================================================\n");
- printf("== Final progress (pid %d): %d/%d (originally %d)\n",
- getpid(), progress, weight_total, WEIGHT_TOTAL);
+ printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
+ ds.progress_->GetMax(), ds.progress_->GetInitialMax());
printf("========================================================\n");
- printf("== dumpstate: done\n");
+ printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
}
-static void usage() {
- fprintf(stderr,
- "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-t]"
- "[-z] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
- " -h: display this help message\n"
- " -b: play sound file instead of vibrate, at beginning of job\n"
- " -e: play sound file instead of vibrate, at end of job\n"
- " -o: write to file (instead of stdout)\n"
- " -d: append date to filename (requires -o)\n"
- " -p: capture screenshot to filename.png (requires -o)\n"
- " -t: only captures telephony sections\n"
- " -z: generate zipped file (requires -o)\n"
- " -s: write output to control socket (for init)\n"
- " -S: write file location to control socket (for init; requires -o and -z)"
- " -q: disable vibrate\n"
- " -B: send broadcast when finished (requires -o)\n"
- " -P: send broadcast when started and update system properties on "
- "progress (requires -o and -B)\n"
- " -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
- "shouldn't be used with -P)\n"
- " -V: sets the bugreport format version (valid values: %s)\n",
- VERSION_DEFAULT.c_str());
+void Dumpstate::DumpstateBoard() {
+ DurationReporter duration_reporter("dumpstate_board()");
+ printf("========================================================\n");
+ printf("== Board\n");
+ printf("========================================================\n");
+
+ ::android::sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService());
+ if (dumpstate_device == nullptr) {
+ MYLOGE("No IDumpstateDevice implementation\n");
+ return;
+ }
+
+ if (!IsZipping()) {
+ MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
+ return;
+ }
+
+ std::string path = kDumpstateBoardPath;
+ MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path.c_str());
+
+ int fd =
+ TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+ if (fd < 0) {
+ MYLOGE("Could not open file %s: %s\n", path.c_str(), strerror(errno));
+ return;
+ }
+
+ native_handle_t* handle = native_handle_create(1, 0);
+ if (handle == nullptr) {
+ MYLOGE("Could not create native_handle\n");
+ return;
+ }
+ handle->data[0] = fd;
+
+ // TODO: need a timeout mechanism so dumpstate does not hang on device implementation call.
+ 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");
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+
+ if (remove(path.c_str()) != 0) {
+ MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno));
+ }
}
-static void sigpipe_handler(int n) {
- // don't complain to stderr or stdout
+static void ShowUsageAndExit(int exitCode = 1) {
+ fprintf(stderr,
+ "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file] [-d] [-p] "
+ "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
+ " -h: display this help message\n"
+ " -b: play sound file instead of vibrate, at beginning of job\n"
+ " -e: play sound file instead of vibrate, at end of job\n"
+ " -o: write to file (instead of stdout)\n"
+ " -d: append date to filename (requires -o)\n"
+ " -p: capture screenshot to filename.png (requires -o)\n"
+ " -z: generate zipped file (requires -o)\n"
+ " -s: write output to control socket (for init)\n"
+ " -S: write file location to control socket (for init; requires -o and -z)"
+ " -q: disable vibrate\n"
+ " -B: send broadcast when finished (requires -o)\n"
+ " -P: send broadcast when started and update system properties on "
+ "progress (requires -o and -B)\n"
+ " -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
+ "shouldn't be used with -P)\n"
+ " -v: prints the dumpstate header and exit\n");
+ exit(exitCode);
+}
+
+static void ExitOnInvalidArgs() {
+ fprintf(stderr, "invalid combination of args\n");
+ ShowUsageAndExit();
+}
+
+static void sig_handler(int) {
_exit(EXIT_FAILURE);
}
-/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
- temporary file.
- */
-static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path,
- time_t now) {
- if (!add_zip_entry(bugreport_name, bugreport_path)) {
+static void register_sig_handler() {
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = sig_handler;
+ sigaction(SIGPIPE, &sa, NULL); // broken pipe
+ sigaction(SIGSEGV, &sa, NULL); // segment fault
+ sigaction(SIGINT, &sa, NULL); // ctrl-c
+ sigaction(SIGTERM, &sa, NULL); // killed
+ sigaction(SIGQUIT, &sa, NULL); // quit
+}
+
+bool Dumpstate::FinishZipFile() {
+ std::string entry_name = base_name_ + "-" + name_ + ".txt";
+ MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
+ tmp_path_.c_str());
+ // Final timestamp
+ char date[80];
+ time_t the_real_now_please_stand_up = time(nullptr);
+ strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up));
+ MYLOGD("dumpstate id %d finished around %s (%ld s)\n", ds.id_, date,
+ the_real_now_please_stand_up - ds.now_);
+
+ if (!ds.AddZipEntry(entry_name, tmp_path_)) {
MYLOGE("Failed to add text entry to .zip file\n");
return false;
}
- if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
+ if (!AddTextZipEntry("main_entry.txt", entry_name)) {
MYLOGE("Failed to add main_entry.txt to .zip file\n");
return false;
}
- int32_t err = zip_writer->Finish();
- if (err) {
- MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
+ // Add log file (which contains stderr output) to zip...
+ fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
+ if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) {
+ MYLOGE("Failed to add dumpstate log to .zip file\n");
+ return false;
+ }
+ // ... and re-opens it for further logging.
+ redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+ fprintf(stderr, "\n");
+
+ int32_t err = zip_writer_->Finish();
+ if (err != 0) {
+ MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
- if (is_user_build()) {
- MYLOGD("Removing temporary file %s\n", bugreport_path.c_str())
- if (remove(bugreport_path.c_str())) {
- ALOGW("remove(%s): %s\n", bugreport_path.c_str(), strerror(errno));
- }
- } else {
- MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str())
+ // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
+ ds.zip_file.reset(nullptr);
+
+ MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
+ if (remove(tmp_path_.c_str()) != 0) {
+ MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno));
}
return true;
}
static std::string SHA256_file_hash(std::string filepath) {
- ScopedFd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC
- | O_NOFOLLOW)));
- if (fd.get() == -1) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK
+ | O_CLOEXEC | O_NOFOLLOW)));
+ if (fd == -1) {
MYLOGE("open(%s): %s\n", filepath.c_str(), strerror(errno));
return NULL;
}
SHA256_CTX ctx;
- SHA256_init(&ctx);
+ SHA256_Init(&ctx);
std::vector<uint8_t> buffer(65536);
while (1) {
@@ -1304,21 +1330,48 @@
return NULL;
}
- SHA256_update(&ctx, buffer.data(), bytes_read);
+ SHA256_Update(&ctx, buffer.data(), bytes_read);
}
- uint8_t hash[SHA256_DIGEST_SIZE];
- memcpy(hash, SHA256_final(&ctx), SHA256_DIGEST_SIZE);
- char hash_buffer[SHA256_DIGEST_SIZE * 2 + 1];
- for(size_t i = 0; i < SHA256_DIGEST_SIZE; i++) {
+ uint8_t hash[SHA256_DIGEST_LENGTH];
+ SHA256_Final(hash, &ctx);
+
+ char hash_buffer[SHA256_DIGEST_LENGTH * 2 + 1];
+ for(size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) {
sprintf(hash_buffer + (i * 2), "%02x", hash[i]);
}
hash_buffer[sizeof(hash_buffer) - 1] = 0;
return std::string(hash_buffer);
}
+static void SendBroadcast(const std::string& action, const std::vector<std::string>& args) {
+ // clang-format off
+ std::vector<std::string> am = {"/system/bin/cmd", "activity", "broadcast", "--user", "0",
+ "--receiver-foreground", "--receiver-include-background", "-a", action};
+ // clang-format on
+
+ am.insert(am.end(), args.begin(), args.end());
+
+ RunCommand("", am,
+ CommandOptions::WithTimeout(20)
+ .Log("Sending broadcast: '%s'\n")
+ .Always()
+ .DropRoot()
+ .RedirectStderr()
+ .Build());
+}
+
+static void Vibrate(int duration_ms) {
+ // clang-format off
+ RunCommand("", {"cmd", "vibrator", "vibrate", std::to_string(duration_ms), "dumpstate"},
+ CommandOptions::WithTimeout(10)
+ .Log("Vibrate: '%s'\n")
+ .Always()
+ .Build());
+ // clang-format on
+}
+
int main(int argc, char *argv[]) {
- struct sigaction sigact;
int do_add_date = 0;
int do_zip_file = 0;
int do_vibrate = 1;
@@ -1327,33 +1380,15 @@
int use_control_socket = 0;
int do_fb = 0;
int do_broadcast = 0;
- int do_early_screenshot = 0;
int is_remote_mode = 0;
+ bool show_header_only = false;
+ bool do_start_service = false;
bool telephony_only = false;
- std::string version = VERSION_DEFAULT;
-
- now = time(NULL);
-
- MYLOGI("begin\n");
-
- /* gets the sequential id */
- char last_id[PROPERTY_VALUE_MAX];
- property_get("dumpstate.last_id", last_id, "0");
- id = strtoul(last_id, NULL, 10) + 1;
- snprintf(last_id, sizeof(last_id), "%lu", id);
- property_set("dumpstate.last_id", last_id);
- MYLOGI("dumpstate id: %lu\n", id);
-
- /* clear SIGPIPE handler */
- memset(&sigact, 0, sizeof(sigact));
- sigact.sa_handler = sigpipe_handler;
- sigaction(SIGPIPE, &sigact, NULL);
-
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
- FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
+ FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we");
if (oom_adj) {
fputs("-1000", oom_adj);
fclose(oom_adj);
@@ -1367,60 +1402,146 @@
}
/* parse arguments */
- std::string args;
- format_args(argc, const_cast<const char **>(argv), &args);
- MYLOGD("Dumpstate command line: %s\n", args.c_str());
int c;
- while ((c = getopt(argc, argv, "dho:svqzptPBRSV:")) != -1) {
+ while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
switch (c) {
- case 'd': do_add_date = 1; break;
- case 't': telephony_only = true; break;
- case 'z': do_zip_file = 1; break;
- case 'o': use_outfile = optarg; break;
- case 's': use_socket = 1; break;
- case 'S': use_control_socket = 1; break;
- case 'v': break; // compatibility no-op
- case 'q': do_vibrate = 0; break;
- case 'p': do_fb = 1; break;
- case 'P': do_update_progress = 1; break;
- case 'R': is_remote_mode = 1; break;
- case 'B': do_broadcast = 1; break;
- case 'V': version = optarg; break;
- case '?': printf("\n");
+ // clang-format off
+ case 'd': do_add_date = 1; break;
+ case 'z': do_zip_file = 1; break;
+ case 'o': use_outfile = optarg; break;
+ case 's': use_socket = 1; break;
+ case 'S': use_control_socket = 1; break;
+ case 'v': show_header_only = true; break;
+ case 'q': do_vibrate = 0; break;
+ case 'p': do_fb = 1; break;
+ case 'P': ds.update_progress_ = true; break;
+ case 'R': is_remote_mode = 1; break;
+ case 'B': do_broadcast = 1; break;
+ case 'V': break; // compatibility no-op
case 'h':
- usage();
- exit(1);
+ ShowUsageAndExit(0);
+ break;
+ default:
+ fprintf(stderr, "Invalid option: %c\n", c);
+ ShowUsageAndExit();
+ // clang-format on
}
}
- if ((do_zip_file || do_add_date || do_update_progress || do_broadcast) && !use_outfile) {
- usage();
- exit(1);
+ // TODO: use helper function to convert argv into a string
+ for (int i = 0; i < argc; i++) {
+ ds.args_ += argv[i];
+ if (i < argc - 1) {
+ ds.args_ += " ";
+ }
+ }
+
+ ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
+ if (!ds.extra_options_.empty()) {
+ // Framework uses a system property to override some command-line args.
+ // Currently, it contains the type of the requested bugreport.
+ if (ds.extra_options_ == "bugreportplus") {
+ // Currently, the dumpstate binder is only used by Shell to update progress.
+ do_start_service = true;
+ ds.update_progress_ = true;
+ do_fb = 0;
+ } else if (ds.extra_options_ == "bugreportremote") {
+ do_vibrate = 0;
+ is_remote_mode = 1;
+ 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());
+ }
+ // Reset the property
+ android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
+ }
+
+ ds.notification_title = android::base::GetProperty(PROPERTY_EXTRA_TITLE, "");
+ if (!ds.notification_title.empty()) {
+ // Reset the property
+ android::base::SetProperty(PROPERTY_EXTRA_TITLE, "");
+
+ ds.notification_description = android::base::GetProperty(PROPERTY_EXTRA_DESCRIPTION, "");
+ if (!ds.notification_description.empty()) {
+ // Reset the property
+ android::base::SetProperty(PROPERTY_EXTRA_DESCRIPTION, "");
+ }
+ MYLOGD("notification (title: %s, description: %s)\n",
+ ds.notification_title.c_str(), ds.notification_description.c_str());
+ }
+
+ if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) {
+ ExitOnInvalidArgs();
}
if (use_control_socket && !do_zip_file) {
- usage();
+ ExitOnInvalidArgs();
+ }
+
+ if (ds.update_progress_ && !do_broadcast) {
+ ExitOnInvalidArgs();
+ }
+
+ if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
+ ExitOnInvalidArgs();
+ }
+
+ if (ds.version_ == VERSION_DEFAULT) {
+ ds.version_ = VERSION_CURRENT;
+ }
+
+ if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) {
+ MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
+ ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
+ VERSION_SPLIT_ANR.c_str());
exit(1);
}
- if (do_update_progress && !do_broadcast) {
- usage();
- exit(1);
+ if (show_header_only) {
+ ds.PrintHeader();
+ exit(0);
}
- if (is_remote_mode && (do_update_progress || !do_broadcast || !do_zip_file || !do_add_date)) {
- usage();
- exit(1);
+ /* redirect output if needed */
+ bool is_redirecting = !use_socket && use_outfile;
+
+ // TODO: temporarily set progress until it's part of the Dumpstate constructor
+ std::string stats_path =
+ is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", dirname(use_outfile))
+ : "";
+ ds.progress_.reset(new Progress(stats_path));
+
+ /* gets the sequential id */
+ uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
+ ds.id_ = ++last_id;
+ android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
+
+ MYLOGI("begin\n");
+
+ register_sig_handler();
+
+ if (do_start_service) {
+ MYLOGI("Starting 'dumpstate' service\n");
+ android::status_t ret;
+ if ((ret = android::os::DumpstateService::Start()) != android::OK) {
+ MYLOGE("Unable to start DumpstateService: %d\n", ret);
+ }
}
- if (version != VERSION_DEFAULT) {
- usage();
- exit(1);
+ if (PropertiesHelper::IsDryRun()) {
+ MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
- MYLOGI("bugreport format version: %s\n", version.c_str());
+ MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
+ ds.extra_options_.c_str());
- do_early_screenshot = do_update_progress;
+ MYLOGI("bugreport format version: %s\n", ds.version_.c_str());
+
+ ds.do_early_screenshot_ = ds.update_progress_;
// If we are going to use a socket, do it as early as possible
// to avoid timeouts from bugreport.
@@ -1430,98 +1551,74 @@
if (use_control_socket) {
MYLOGD("Opening control socket\n");
- control_socket_fd = open_socket("dumpstate");
- do_update_progress = 1;
+ ds.control_socket_fd_ = open_socket("dumpstate");
+ ds.update_progress_ = 1;
}
- /* full path of the temporary file containing the bugreport */
- std::string tmp_path;
-
- /* full path of the file containing the dumpstate logs*/
- std::string log_path;
-
- /* full path of the systrace file, when enabled */
- std::string systrace_path;
-
- /* full path of the temporary file containing the screenshot (when requested) */
- std::string screenshot_path;
-
- /* base name (without suffix or extensions) of the bugreport files */
- std::string base_name;
-
- /* pointer to the actual path, be it zip or text */
- std::string path;
-
- /* pointer to the zipped file */
- std::unique_ptr<FILE, int(*)(FILE*)> zip_file(NULL, fclose);
-
- /* redirect output if needed */
- bool is_redirecting = !use_socket && use_outfile;
-
if (is_redirecting) {
- bugreport_dir = dirname(use_outfile);
- base_name = basename(use_outfile);
+ ds.bugreport_dir_ = dirname(use_outfile);
+ std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
+ std::string device_name = android::base::GetProperty("ro.product.name", "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(&now));
- suffix = date;
+ strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
+ ds.name_ = date;
} else {
- suffix = "undated";
+ ds.name_ = "undated";
}
- char build_id[PROPERTY_VALUE_MAX];
- property_get("ro.build.id", build_id, "UNKNOWN_BUILD");
- base_name = base_name + "-" + build_id;
- if (telephony_only) {
- base_name = base_name + "-telephony";
- }
- if (do_fb) {
- // TODO: if dumpstate was an object, the paths could be internal variables and then
- // we could have a function to calculate the derived values, such as:
- // screenshot_path = GetPath(".png");
- screenshot_path = bugreport_dir + "/" + base_name + "-" + suffix + ".png";
- }
- tmp_path = bugreport_dir + "/" + base_name + "-" + suffix + ".tmp";
- log_path = bugreport_dir + "/dumpstate_log-" + suffix + "-"
- + std::to_string(getpid()) + ".txt";
- MYLOGD("Bugreport dir: %s\n"
- "Base name: %s\n"
- "Suffix: %s\n"
- "Log path: %s\n"
- "Temporary path: %s\n"
- "Screenshot path: %s\n",
- bugreport_dir.c_str(), base_name.c_str(), suffix.c_str(),
- log_path.c_str(), tmp_path.c_str(), screenshot_path.c_str());
+ if (telephony_only) {
+ ds.base_name_ += "-telephony";
+ }
+
+ if (do_fb) {
+ ds.screenshot_path_ = ds.GetPath(".png");
+ }
+ ds.tmp_path_ = ds.GetPath(".tmp");
+ ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
+
+ MYLOGD(
+ "Bugreport dir: %s\n"
+ "Base name: %s\n"
+ "Suffix: %s\n"
+ "Log path: %s\n"
+ "Temporary path: %s\n"
+ "Screenshot path: %s\n",
+ ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(),
+ ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
if (do_zip_file) {
- path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
- MYLOGD("Creating initial .zip file (%s)\n", path.c_str());
- create_parent_dirs(path.c_str());
- zip_file.reset(fopen(path.c_str(), "wb"));
- if (!zip_file) {
- MYLOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno));
+ ds.path_ = ds.GetPath(".zip");
+ MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
+ create_parent_dirs(ds.path_.c_str());
+ ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
+ if (ds.zip_file == nullptr) {
+ MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
do_zip_file = 0;
} else {
- zip_writer.reset(new ZipWriter(zip_file.get()));
+ ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
}
- add_text_zip_entry("version.txt", version);
+ ds.AddTextZipEntry("version.txt", ds.version_);
}
- if (do_update_progress) {
+ if (ds.update_progress_) {
if (do_broadcast) {
// clang-format off
+
std::vector<std::string> am_args = {
- "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
- "--es", "android.intent.extra.NAME", suffix,
- "--ei", "android.intent.extra.ID", std::to_string(id),
- "--ei", "android.intent.extra.PID", std::to_string(getpid()),
- "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
+ "--receiver-permission", "android.permission.DUMP",
+ "--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_),
+ "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
};
// clang-format on
- send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
+ SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
}
if (use_control_socket) {
- dprintf(control_socket_fd, "BEGIN:%s\n", path.c_str());
+ dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str());
}
}
}
@@ -1533,66 +1630,61 @@
fclose(cmdline);
}
- /* open the vibrator before dropping root */
- std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
if (do_vibrate) {
- vibrator.reset(fopen("/sys/class/timed_output/vibrator/enable", "we"));
- if (vibrator) {
- vibrate(vibrator.get(), 150);
- }
+ Vibrate(150);
}
- if (do_fb && do_early_screenshot) {
- if (screenshot_path.empty()) {
+ if (do_fb && ds.do_early_screenshot_) {
+ if (ds.screenshot_path_.empty()) {
// should not have happened
MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
} else {
MYLOGI("taking early screenshot\n");
- take_screenshot(screenshot_path);
- MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
- if (chown(screenshot_path.c_str(), AID_SHELL, AID_SHELL)) {
- MYLOGE("Unable to change ownership of screenshot file %s: %s\n",
- screenshot_path.c_str(), strerror(errno));
- }
+ ds.TakeScreenshot();
}
}
if (do_zip_file) {
- if (chown(path.c_str(), AID_SHELL, AID_SHELL)) {
- MYLOGE("Unable to change ownership of zip file %s: %s\n", path.c_str(), strerror(errno));
+ if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) {
+ MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(),
+ strerror(errno));
}
}
if (is_redirecting) {
- redirect_to_file(stderr, const_cast<char*>(log_path.c_str()));
- if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) {
+ redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+ if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
- log_path.c_str(), strerror(errno));
+ ds.log_path_.c_str(), strerror(errno));
}
/* TODO: rather than generating a text file now and zipping it later,
it would be more efficient to redirect stdout to the zip entry
directly, but the libziparchive doesn't support that option yet. */
- redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
- if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) {
+ redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str()));
+ if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
- tmp_path.c_str(), strerror(errno));
+ ds.tmp_path_.c_str(), strerror(errno));
}
}
+
+ // Don't buffer stdout
+ setvbuf(stdout, nullptr, _IONBF, 0);
+
// NOTE: there should be no stdout output until now, otherwise it would break the header.
// In particular, DurationReport objects should be created passing 'title, NULL', so their
// duration is logged into MYLOG instead.
- print_header(version);
+ ds.PrintHeader();
if (telephony_only) {
- dump_iptables();
- if (!drop_root_user()) {
+ DumpIpTables();
+ if (!DropRootUser()) {
return -1;
}
do_dmesg();
- do_logcat();
- do_kmsg();
- dumpstate_board();
- dump_modem_logs();
+ 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
@@ -1601,40 +1693,44 @@
dump_systrace();
}
- // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
- dump_raft();
-
// Invoking the following dumpsys calls before dump_traces() to try and
// keep the system stats as close to its initial state as possible.
- run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL);
- run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "-t", "10", "cpuinfo", "-a", NULL);
+ 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);
- add_dir(RECOVERY_DIR, true);
- add_dir(RECOVERY_DATA_DIR, true);
- add_dir(LOGPERSIST_DATA_DIR, false);
- if (!is_user_build()) {
- add_dir(PROFILE_DATA_DIR_CUR, true);
- add_dir(PROFILE_DATA_DIR_REF, true);
+ 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();
+ DumpIpTables();
// Capture any IPSec policies in play. No keys are exposed here.
- run_command("IP XFRM POLICY", 10, "ip", "xfrm", "policy", nullptr);
+ RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
+ CommandOptions::WithTimeout(10).Build());
// Run ss as root so we can see socket marks.
- run_command("DETAILED SOCKET STATE", 10, "ss", "-eionptu", NULL);
+ RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"},
+ CommandOptions::WithTimeout(10).Build());
- if (!drop_root_user()) {
+ if (!DropRootUser()) {
return -1;
}
- dumpstate(do_early_screenshot ? "": screenshot_path, version);
+ dumpstate();
}
/* close output if needed */
@@ -1646,125 +1742,136 @@
if (use_outfile) {
/* check if user changed the suffix using system properties */
- char key[PROPERTY_KEY_MAX];
- char value[PROPERTY_VALUE_MAX];
- snprintf(key, sizeof(key), "dumpstate.%d.name", getpid());
- property_get(key, value, "");
+ std::string name = android::base::GetProperty(
+ android::base::StringPrintf("dumpstate.%d.name", ds.pid_), "");
bool change_suffix= false;
- if (value[0]) {
+ if (!name.empty()) {
/* must whitelist which characters are allowed, otherwise it could cross directories */
std::regex valid_regex("^[-_a-zA-Z0-9]+$");
- if (std::regex_match(value, valid_regex)) {
+ if (std::regex_match(name.c_str(), valid_regex)) {
change_suffix = true;
} else {
- MYLOGE("invalid suffix provided by user: %s\n", value);
+ MYLOGE("invalid suffix provided by user: %s\n", name.c_str());
}
}
if (change_suffix) {
- MYLOGI("changing suffix from %s to %s\n", suffix.c_str(), value);
- suffix = value;
- if (!screenshot_path.empty()) {
- std::string new_screenshot_path =
- bugreport_dir + "/" + base_name + "-" + suffix + ".png";
- if (rename(screenshot_path.c_str(), new_screenshot_path.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", screenshot_path.c_str(),
- new_screenshot_path.c_str(), strerror(errno));
+ MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
+ ds.name_ = name;
+ if (!ds.screenshot_path_.empty()) {
+ std::string new_screenshot_path = ds.GetPath(".png");
+ if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
+ new_screenshot_path.c_str(), strerror(errno));
} else {
- screenshot_path = new_screenshot_path;
+ ds.screenshot_path_ = new_screenshot_path;
}
}
}
bool do_text_file = true;
if (do_zip_file) {
- std::string entry_name = base_name + "-" + suffix + ".txt";
- MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str());
- if (!finish_zip_file(entry_name, tmp_path, now)) {
+ if (!ds.FinishZipFile()) {
MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
do_text_file = true;
} else {
do_text_file = false;
// Since zip file is already created, it needs to be renamed.
- std::string new_path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
- if (path != new_path) {
- MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str());
- if (rename(path.c_str(), new_path.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", path.c_str(),
- new_path.c_str(), strerror(errno));
+ std::string new_path = ds.GetPath(".zip");
+ if (ds.path_ != new_path) {
+ MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
+ if (rename(ds.path_.c_str(), new_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
+ strerror(errno));
} else {
- path = new_path;
+ ds.path_ = new_path;
}
}
}
}
if (do_text_file) {
- path = bugreport_dir + "/" + base_name + "-" + suffix + ".txt";
- MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str());
- if (rename(tmp_path.c_str(), path.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno));
- path.clear();
+ ds.path_ = ds.GetPath(".txt");
+ MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(),
+ ds.tmp_path_.c_str());
+ if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(),
+ strerror(errno));
+ ds.path_.clear();
}
}
if (use_control_socket) {
if (do_text_file) {
- dprintf(control_socket_fd, "FAIL:could not create zip file, check %s "
- "for more details\n", log_path.c_str());
+ dprintf(ds.control_socket_fd_,
+ "FAIL:could not create zip file, check %s "
+ "for more details\n",
+ ds.log_path_.c_str());
} else {
- dprintf(control_socket_fd, "OK:%s\n", path.c_str());
+ dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
}
}
}
/* vibrate a few but shortly times to let user know it's finished */
- if (vibrator) {
- for (int i = 0; i < 3; i++) {
- vibrate(vibrator.get(), 75);
- usleep((75 + 50) * 1000);
- }
+ for (int i = 0; i < 3; i++) {
+ Vibrate(75);
+ usleep((75 + 50) * 1000);
}
/* tell activity manager we're done */
if (do_broadcast) {
- if (!path.empty()) {
- MYLOGI("Final bugreport path: %s\n", path.c_str());
+ if (!ds.path_.empty()) {
+ MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
// clang-format off
+
std::vector<std::string> am_args = {
- "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
- "--ei", "android.intent.extra.ID", std::to_string(id),
- "--ei", "android.intent.extra.PID", std::to_string(getpid()),
- "--ei", "android.intent.extra.MAX", std::to_string(weight_total),
- "--es", "android.intent.extra.BUGREPORT", path,
- "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
+ "--receiver-permission", "android.permission.DUMP",
+ "--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()),
+ "--es", "android.intent.extra.BUGREPORT", ds.path_,
+ "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
};
// clang-format on
if (do_fb) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.SCREENSHOT");
- am_args.push_back(screenshot_path);
+ am_args.push_back(ds.screenshot_path_);
+ }
+ if (!ds.notification_title.empty()) {
+ am_args.push_back("--es");
+ am_args.push_back("android.intent.extra.TITLE");
+ am_args.push_back(ds.notification_title);
+ if (!ds.notification_description.empty()) {
+ am_args.push_back("--es");
+ am_args.push_back("android.intent.extra.DESCRIPTION");
+ am_args.push_back(ds.notification_description);
+ }
}
if (is_remote_mode) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
- am_args.push_back(SHA256_file_hash(path));
- send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
+ am_args.push_back(SHA256_file_hash(ds.path_));
+ SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED",
+ am_args);
} else {
- send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
+ SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args);
}
} else {
MYLOGE("Skipping finished broadcast because bugreport could not be generated\n");
}
}
- MYLOGD("Final progress: %d/%d (originally %d)\n", progress, weight_total, WEIGHT_TOTAL);
- MYLOGI("done\n");
+ MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(),
+ ds.progress_->GetInitialMax());
+ ds.progress_->Save();
+ MYLOGI("done (id %d)\n", ds.id_);
if (is_redirecting) {
fclose(stderr);
}
- if (use_control_socket && control_socket_fd != -1) {
- MYLOGD("Closing control socket\n");
- close(control_socket_fd);
+ if (use_control_socket && ds.control_socket_fd_ != -1) {
+ MYLOGD("Closing control socket\n");
+ close(ds.control_socket_fd_);
}
return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 514af59..f02303b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -14,93 +14,341 @@
* limitations under the License.
*/
-#ifndef _DUMPSTATE_H_
-#define _DUMPSTATE_H_
-
-/* When defined, skips the real dumps and just print the section headers.
- Useful when debugging dumpstate itself. */
-//#define _DUMPSTATE_DRY_RUN_
-
-#ifdef _DUMPSTATE_DRY_RUN_
-#define ON_DRY_RUN_RETURN(X) return X
-#define ON_DRY_RUN(code) code
-#else
-#define ON_DRY_RUN_RETURN(X)
-#define ON_DRY_RUN(code)
-#endif
-
-#ifndef MYLOGD
-#define MYLOGD(...) fprintf(stderr, __VA_ARGS__); ALOGD(__VA_ARGS__);
-#endif
-
-#ifndef MYLOGI
-#define MYLOGI(...) fprintf(stderr, __VA_ARGS__); ALOGI(__VA_ARGS__);
-#endif
-
-#ifndef MYLOGE
-#define MYLOGE(...) fprintf(stderr, __VA_ARGS__); ALOGE(__VA_ARGS__);
-#endif
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
#include <time.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
+
+#include <string>
#include <vector>
-#define SU_PATH "/system/xbin/su"
+#include <android-base/macros.h>
+#include <ziparchive/zip_writer.h>
+#include "DumpstateUtil.h"
+#include "android/os/BnDumpstate.h"
+
+// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
+// std::vector<std::string>
+// 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" {
#endif
-typedef void (for_each_pid_func)(int, const char *);
-typedef void (for_each_tid_func)(int, int, const char *);
-
-/* Estimated total weight of bugreport generation.
+/*
+ * Helper class used to report how long it takes for a section to finish.
*
- * Each section contributes to the total weight by an individual weight, so the overall progress
- * can be calculated by dividing the all completed weight by the total weight.
+ * Typical usage:
*
- * This value is defined empirically and it need to be adjusted as more sections are added.
+ * DurationReporter duration_reporter(title);
*
- * It does not need to match the exact sum of all sections, but ideally it should to be slight more
- * than such sum: a value too high will cause the bugreport to finish before the user expected (for
- * example, jumping from 70% to 100%), while a value too low will cause the progress to get stuck
- * at an almost-finished value (like 99%) for a while.
*/
-static const int WEIGHT_TOTAL = 6500;
+class DurationReporter {
+ public:
+ DurationReporter(const std::string& title, bool log_only = false);
-/* Most simple commands have 10 as timeout, so 5 is a good estimate */
-static const int WEIGHT_FILE = 5;
+ ~DurationReporter();
+
+ private:
+ std::string title_;
+ bool log_only_;
+ uint64_t started_;
+
+ DISALLOW_COPY_AND_ASSIGN(DurationReporter);
+};
/*
- * TODO: the dumpstate internal state is getting fragile; for example, this variable is defined
- * here, declared at utils.cpp, and used on utils.cpp and dumpstate.cpp.
- * It would be better to take advantage of the C++ migration and encapsulate the state in an object,
- * but that will be better handled in a major C++ refactoring, which would also get rid of other C
- * idioms (like using std::string instead of char*, removing varargs, etc...) */
-extern int do_update_progress, progress, weight_total, control_socket_fd;
+ * Keeps track of current progress and estimated max, saving stats on file to tune up future runs.
+ *
+ * Each `dumpstate` section contributes to the total weight by an individual weight, so the overall
+ * progress can be calculated by dividing the estimate max progress by the current progress.
+ *
+ * The estimated max progress is initially set to a value (`kDefaultMax) defined empirically, but
+ * it's adjusted after each dumpstate run by storing the average duration in a file.
+ *
+ */
+class Progress {
+ friend class android::os::dumpstate::ProgressTest;
+ friend class android::os::dumpstate::DumpstateTest;
-/* full path of the directory where the bugreport files will be written */
-extern std::string bugreport_dir;
+ public:
+ /*
+ * Default estimation of the max duration of a bugreport generation.
+ *
+ * It does not need to match the exact sum of all sections, but ideally it should to be slight
+ * more than such sum: a value too high will cause the bugreport to finish before the user
+ * expected (for example, jumping from 70% to 100%), while a value too low will cause the
+ * progress to get stuck at an almost-finished value (like 99%) for a while.
+ *
+ * This constant is only used when the average duration from previous runs cannot be used.
+ */
+ static const int kDefaultMax;
-/* root dir for all files copied as-is into the bugreport. */
-extern const std::string ZIP_ROOT_DIR;
+ Progress(const std::string& path = "");
-/* Checkes whether dumpstate is generating a zipped bugreport. */
-bool is_zipping();
+ // Gets the current progress.
+ int32_t Get() const;
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
+ // Gets the current estimated max progress.
+ int32_t GetMax() const;
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
+ // Gets the initial estimated max progress.
+ int32_t GetInitialMax() const;
-/* adds all files from a directory to the zipped bugreport file */
-void add_dir(const char *dir, bool recursive);
+ // Increments progress (ignored if not positive).
+ // Returns `true` if the max progress increased as well.
+ bool Inc(int32_t delta);
-/* prints the contents of a file */
-int dump_file(const char *title, const char *path);
+ // Persist the stats.
+ void Save();
+
+ void Dump(int fd, const std::string& prefix) const;
+
+ private:
+ Progress(int32_t initial_max, float growth_factor,
+ const std::string& path = ""); // Used by test cases.
+ Progress(int32_t initial_max, int32_t progress, float growth_factor); // Used by test cases.
+ void Load();
+ int32_t initial_max_;
+ int32_t progress_;
+ int32_t max_;
+ float growth_factor_;
+ int32_t n_runs_;
+ int32_t average_max_;
+ const std::string& path_;
+};
+
+/*
+ * List of supported zip format versions.
+ *
+ * See bugreport-format.md for more info.
+ */
+static std::string VERSION_CURRENT = "1.0";
+
+/*
+ * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version
+ * will be bumped to 2.0-dev-1.
+ */
+static std::string VERSION_SPLIT_ANR = "2.0-dev-1";
+
+/*
+ * "Alias" for the current version.
+ */
+static std::string VERSION_DEFAULT = "default";
+
+/*
+ * Main class driving a bugreport generation.
+ *
+ * Currently, it only contains variables that are accessed externally, but gradually the functions
+ * that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
+ */
+class Dumpstate {
+ friend class DumpstateTest;
+
+ public:
+ static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
+
+ static Dumpstate& GetInstance();
+
+ /* Checkes whether dumpstate is generating a zipped bugreport. */
+ bool IsZipping() const;
+
+ /*
+ * Forks a command, waits for it to finish, and returns its status.
+ *
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |full_command| array containing the command (first entry) and its arguments.
+ * Must contain at least one element.
+ * |options| optional argument defining the command's behavior.
+ */
+ int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+ const android::os::dumpstate::CommandOptions& options =
+ android::os::dumpstate::CommandOptions::DEFAULT);
+
+ /*
+ * Runs `dumpsys` with the given arguments, automatically setting its timeout
+ * (`-t` argument)
+ * according to the command options.
+ *
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |dumpsys_args| `dumpsys` arguments (except `-t`).
+ * |options| optional argument defining the command's behavior.
+ * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -t` (otherwise it uses the
+ * timeout from `options`)
+ */
+ void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
+ const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
+ long dumpsys_timeout = 0);
+
+ /*
+ * Prints the contents of a file.
+ *
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |path| location of the file to be dumped.
+ */
+ int DumpFile(const std::string& title, const std::string& path);
+
+ /*
+ * Adds a new entry to the existing zip file.
+ * */
+ bool AddZipEntry(const std::string& entry_name, const std::string& entry_path);
+
+ /*
+ * Adds a new entry to the existing zip file.
+ */
+ bool AddZipEntryFromFd(const std::string& entry_name, int fd);
+
+ /*
+ * Adds a text entry entry to the existing zip file.
+ */
+ bool AddTextZipEntry(const std::string& entry_name, const std::string& content);
+
+ /*
+ * Adds all files from a directory to the zipped bugreport file.
+ */
+ void AddDir(const std::string& dir, bool recursive);
+
+ /*
+ * Takes a screenshot and save it to the given `path`.
+ *
+ * If `path` is empty, uses a standard path based on the bugreport name.
+ */
+ void TakeScreenshot(const std::string& path = "");
+
+ /////////////////////////////////////////////////////////////////////
+ // TODO: members below should be private once refactor is finished //
+ /////////////////////////////////////////////////////////////////////
+
+ // TODO: temporary method until Dumpstate object is properly set
+ void SetProgress(std::unique_ptr<Progress> progress);
+
+ void DumpstateBoard();
+
+ /*
+ * Updates the overall progress of the bugreport generation by the given weight increment.
+ */
+ void UpdateProgress(int32_t delta);
+
+ /* Prints the dumpstate header on `stdout`. */
+ void PrintHeader() const;
+
+ /*
+ * Adds the temporary report to the existing .zip file, closes the .zip file, and removes the
+ * temporary file.
+ */
+ bool FinishZipFile();
+
+ /* Gets the path of a bugreport file with the given suffix. */
+ std::string GetPath(const std::string& suffix) const;
+
+ // TODO: initialize fields on constructor
+
+ // dumpstate id - unique after each device reboot.
+ uint32_t id_;
+
+ // dumpstate pid
+ pid_t pid_;
+
+ // Whether progress updates should be published.
+ bool update_progress_ = false;
+
+ // How frequently the progess should be updated;the listener will only be notificated when the
+ // delta from the previous update is more than the threshold.
+ int32_t update_progress_threshold_ = 100;
+
+ // Last progress that triggered a listener updated
+ int32_t last_updated_progress_;
+
+ // Whether it should take an screenshot earlier in the process.
+ bool do_early_screenshot_ = false;
+
+ std::unique_ptr<Progress> progress_;
+
+ // When set, defines a socket file-descriptor use to report progress to bugreportz.
+ int control_socket_fd_ = -1;
+
+ // Bugreport format version;
+ std::string version_ = VERSION_CURRENT;
+
+ // Command-line arguments as string
+ std::string args_;
+
+ // Extra options passed as system property.
+ std::string extra_options_;
+
+ // Full path of the directory where the bugreport files will be written.
+ std::string bugreport_dir_;
+
+ // Full path of the temporary file containing the screenshot (when requested).
+ std::string screenshot_path_;
+
+ time_t now_;
+
+ // Base name (without suffix or extensions) of the bugreport files, typically
+ // `bugreport-BUILD_ID`.
+ std::string base_name_;
+
+ // Name is the suffix part of the bugreport files - it's typically the date (when invoked with
+ // `-d`), but it could be changed by the user..
+ std::string name_;
+
+ // Full path of the temporary file containing the bugreport.
+ std::string tmp_path_;
+
+ // Full path of the file containing the dumpstate logs.
+ std::string log_path_;
+
+ // Pointer to the actual path, be it zip or text.
+ std::string path_;
+
+ // Pointer to the zipped file.
+ std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose};
+
+ // Pointer to the zip structure.
+ std::unique_ptr<ZipWriter> zip_writer_;
+
+ // Binder object listing to progress.
+ android::sp<android::os::IDumpstateListener> listener_;
+ std::string listener_name_;
+
+ // Notification title and description
+ std::string notification_title;
+ std::string notification_description;
+
+ private:
+ // Used by GetInstance() only.
+ Dumpstate(const std::string& version = VERSION_CURRENT);
+
+ DISALLOW_COPY_AND_ASSIGN(Dumpstate);
+};
+
+// for_each_pid_func = void (*)(int, const char*);
+// for_each_tid_func = void (*)(int, int, const char*);
+
+typedef void(for_each_pid_func)(int, const char*);
+typedef void(for_each_tid_func)(int, int, const char*);
/* saves the the contents of a file as a long */
int read_file_as_long(const char *path, long int *output);
@@ -116,37 +364,8 @@
* to false when set to NULL. dump_from_fd will always be
* called with title NULL.
*/
-int dump_files(const char *title, const char *dir,
- bool (*skip)(const char *path),
- int (*dump_from_fd)(const char *title, const char *path, int fd));
-
-// TODO: need to refactor all those run_command variations; there shold be just one, receiving an
-// optional CommandOptions objects with values such as run_always, drop_root, etc...
-
-/* forks a command and waits for it to finish -- terminate args with NULL */
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...);
-int run_command(const char *title, int timeout_seconds, const char *command, ...);
-
-enum RootMode { DROP_ROOT, DONT_DROP_ROOT };
-enum StdoutMode { NORMAL_STDOUT, REDIRECT_TO_STDERR };
-
-/* forks a command and waits for it to finish
- first element of args is the command, and last must be NULL.
- command is always ran, even when _DUMPSTATE_DRY_RUN_ is defined. */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
- int timeout_seconds, const char *args[]);
-
-/* switch to non-root user and group */
-bool drop_root_user();
-
-/* sends a broadcast using Activity Manager */
-void send_broadcast(const std::string& action, const std::vector<std::string>& args);
-
-/* updates the overall progress of dumpstate by the given weight increment */
-void update_progress(int weight);
-
-/* prints all the system properties */
-void print_properties();
+int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
+ int (*dump_from_fd)(const char* title, const char* path, int fd));
/** opens a socket and returns its file descriptor */
int open_socket(const char *service);
@@ -154,9 +373,12 @@
/* redirect output to a service control socket */
void redirect_to_socket(FILE *redirect, const char *service);
-/* redirect output to a file */
+/* redirect output to a new file */
void redirect_to_file(FILE *redirect, char *path);
+/* redirect output to an existing file */
+void redirect_to_existing_file(FILE *redirect, char *path);
+
/* create leading directories, if necessary */
void create_parent_dirs(const char *path);
@@ -187,15 +409,6 @@
/* Play a sound via Stagefright */
void play_sound(const char *path);
-/* Implemented by libdumpstate_board to dump board-specific info */
-void dumpstate_board();
-
-/* Takes a screenshot and save it to the given file */
-void take_screenshot(const std::string& path);
-
-/* Vibrates for a given durating (in milliseconds). */
-void vibrate(FILE* vibrator, int ms);
-
/* Checks if a given path is a directory. */
bool is_dir(const char* pathname);
@@ -208,34 +421,8 @@
/** Gets command-line arguments. */
void format_args(int argc, const char *argv[], std::string *args);
-/** Tells if the device is running a user build. */
-bool is_user_build();
-
-/*
- * Helper class used to report how long it takes for a section to finish.
- *
- * Typical usage:
- *
- * DurationReporter duration_reporter(title);
- *
- */
-class DurationReporter {
-public:
- DurationReporter(const char *title);
- DurationReporter(const char *title, FILE* out);
-
- ~DurationReporter();
-
- static uint64_t nanotime();
-
-private:
- const char* title_;
- FILE* out_;
- uint64_t started_;
-};
-
#ifdef __cplusplus
}
#endif
-#endif /* _DUMPSTATE_H_ */
+#endif /* FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ */
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 336db9f..2e72574 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -17,40 +17,3 @@
class main
disabled
oneshot
-
-# bugreportplus is an enhanced version of bugreport that provides a better
-# user interface (like displaying progress and allowing user to enter details).
-# It's typically triggered by the power button or developer settings.
-service bugreportplus /system/bin/dumpstate -d -B -P -z \
- -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
- class main
- disabled
- oneshot
-
-# bugreportremote is an altered version of bugreport that is supposed to be
-# called not by human user of the device, but by DevicePolicyManagerService only when the
-# Device Owner explicitly requests it, and shared with the Device Policy Controller (DPC) app only
-# if the user consents
-# it will disable vibrations, screenshot taking and will not track progress or
-# allow user to enter any details
-service bugreportremote /system/bin/dumpstate -d -q -B -R -z \
- -o /data/user_de/0/com.android.shell/files/bugreports/remote/bugreport
- class main
- disabled
- oneshot
-
-# bugreportwear is a wearable version of bugreport that displays progress and takes early
-# screenshot.
-service bugreportwear /system/bin/dumpstate -d -B -P -p -z \
- -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
- class main
- disabled
- oneshot
-
-# bugreportelefony is a lightweight version of bugreport that only includes a few, urgent
-# sections used to report telephony bugs.
-service bugreportelefony /system/bin/dumpstate -t -d -B -z \
- -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
- class main
- disabled
- oneshot
diff --git a/cmds/dumpstate/libdumpstate_default.cpp b/cmds/dumpstate/libdumpstate_default.cpp
deleted file mode 100644
index fd840df..0000000
--- a/cmds/dumpstate/libdumpstate_default.cpp
+++ /dev/null
@@ -1,22 +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/testdata/empty-file.txt b/cmds/dumpstate/testdata/empty-file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/dumpstate/testdata/empty-file.txt
diff --git a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
new file mode 100644
index 0000000..7b7a187
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
diff --git a/cmds/dumpstate/testdata/multiple-lines.txt b/cmds/dumpstate/testdata/multiple-lines.txt
new file mode 100644
index 0000000..bead103
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/single-line-with-newline.txt b/cmds/dumpstate/testdata/single-line-with-newline.txt
new file mode 100644
index 0000000..cb48c82
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line-with-newline.txt
@@ -0,0 +1 @@
+I AM LINE1
diff --git a/cmds/dumpstate/testdata/single-line.txt b/cmds/dumpstate/testdata/single-line.txt
new file mode 100644
index 0000000..2f64046
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line.txt
@@ -0,0 +1 @@
+I AM LINE1
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
new file mode 100644
index 0000000..dad9fe8
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
@@ -0,0 +1 @@
+SIX_SIX_SIX 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
new file mode 100644
index 0000000..4facef9
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
@@ -0,0 +1 @@
+-666 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
new file mode 100644
index 0000000..42508f1
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
@@ -0,0 +1 @@
+4815162342 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
new file mode 100644
index 0000000..a23ba2c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
@@ -0,0 +1 @@
+666 FORTY_TWO
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
new file mode 100644
index 0000000..dd529b4
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
@@ -0,0 +1 @@
+666 -42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
new file mode 100644
index 0000000..b148b46
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
@@ -0,0 +1 @@
+666 4815162342
diff --git a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
new file mode 100644
index 0000000..4a9466d
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
@@ -0,0 +1 @@
+N_RUNS AVERAGE
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
new file mode 100644
index 0000000..0aef60c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
@@ -0,0 +1 @@
+1 10
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-two-runs.txt b/cmds/dumpstate/testdata/stats-two-runs.txt
new file mode 100644
index 0000000..9af1233
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-two-runs.txt
@@ -0,0 +1 @@
+2 15
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
new file mode 100644
index 0000000..1c19268
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -0,0 +1,1157 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
+#include "android/os/BnDumpstate.h"
+#include "dumpstate.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+using ::testing::EndsWith;
+using ::testing::HasSubstr;
+using ::testing::IsNull;
+using ::testing::IsEmpty;
+using ::testing::NotNull;
+using ::testing::StrEq;
+using ::testing::StartsWith;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class DumpstateListenerMock : public IDumpstateListener {
+ public:
+ MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
+ MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
+
+ protected:
+ MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
+static int calls_;
+
+// Base class for all tests in this file
+class DumpstateBaseTest : public Test {
+ public:
+ virtual void SetUp() override {
+ calls_++;
+ SetDryRun(false);
+ }
+
+ void SetDryRun(bool dry_run) const {
+ PropertiesHelper::dry_run_ = dry_run;
+ }
+
+ void SetBuildType(const std::string& build_type) const {
+ PropertiesHelper::build_type_ = build_type;
+ }
+
+ bool IsStandalone() const {
+ return calls_ == 1;
+ }
+
+ void DropRoot() const {
+ DropRootUser();
+ uid_t uid = getuid();
+ ASSERT_EQ(2000, (int)uid);
+ }
+
+ protected:
+ const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str());
+ const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/";
+ const std::string kTestDataPath = kFixturesPath + "/testdata/";
+ const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture";
+ const std::string kEchoCommand = "/system/bin/echo";
+
+ /*
+ * Copies a text file fixture to a temporary file, returning it's path.
+ *
+ * Useful in cases where the test case changes the content of the tile.
+ */
+ std::string CopyTextFileFixture(const std::string& relative_name) {
+ std::string from = kTestDataPath + relative_name;
+ // Not using TemporaryFile because it's deleted at the end, and it's useful to keep it
+ // around for poking when the test fails.
+ std::string to = kTestDataPath + relative_name + ".tmp";
+ ALOGD("CopyTextFileFixture: from %s to %s\n", from.c_str(), to.c_str());
+ android::base::RemoveFileIfExists(to);
+ CopyTextFile(from, to);
+ return to.c_str();
+ }
+
+ // Need functions that returns void to use assertions -
+ // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#assertion-placement
+ void ReadFileToString(const std::string& path, std::string* content) {
+ ASSERT_TRUE(android::base::ReadFileToString(path, content))
+ << "could not read contents from " << path;
+ }
+ void WriteStringToFile(const std::string& content, const std::string& path) {
+ ASSERT_TRUE(android::base::WriteStringToFile(content, path))
+ << "could not write contents to " << path;
+ }
+
+ private:
+ void CopyTextFile(const std::string& from, const std::string& to) {
+ std::string content;
+ ReadFileToString(from, &content);
+ WriteStringToFile(content, to);
+ }
+};
+
+class DumpstateTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ SetDryRun(false);
+ SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
+ ds.progress_.reset(new Progress());
+ ds.update_progress_ = false;
+ ds.update_progress_threshold_ = 0;
+ }
+
+ // Runs a command and capture `stdout` and `stderr`.
+ int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ CaptureStdout();
+ CaptureStderr();
+ int status = ds.RunCommand(title, full_command, options);
+ out = GetCapturedStdout();
+ err = GetCapturedStderr();
+ return status;
+ }
+
+ // Dumps a file and capture `stdout` and `stderr`.
+ int DumpFile(const std::string& title, const std::string& path) {
+ CaptureStdout();
+ CaptureStderr();
+ int status = ds.DumpFile(title, path);
+ out = GetCapturedStdout();
+ err = GetCapturedStderr();
+ return status;
+ }
+
+ void SetProgress(long progress, long initial_max, long threshold = 0) {
+ ds.update_progress_ = true;
+ ds.update_progress_threshold_ = threshold;
+ ds.last_updated_progress_ = 0;
+ ds.progress_.reset(new Progress(initial_max, progress, 1.2));
+ }
+
+ std::string GetProgressMessage(const std::string& listener_name, int progress, int max,
+ int old_max = 0, bool update_progress = true) {
+ EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
+ EXPECT_EQ(max, ds.progress_->GetMax()) << "invalid max";
+
+ bool max_increased = old_max > 0;
+
+ std::string message = "";
+ if (max_increased) {
+ message =
+ android::base::StringPrintf("Adjusting max progress from %d to %d\n", old_max, max);
+ }
+
+ if (update_progress) {
+ message += android::base::StringPrintf("Setting progress (%s): %d/%d\n",
+ listener_name.c_str(), progress, max);
+ }
+
+ return message;
+ }
+
+ // `stdout` and `stderr` from the last command ran.
+ std::string out, err;
+
+ Dumpstate& ds = Dumpstate::GetInstance();
+};
+
+TEST_F(DumpstateTest, RunCommandNoArgs) {
+ EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateTest, RunCommandNoTitle) {
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithTitle) {
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+ // We don't know the exact duration, so we check the prefix and suffix
+ EXPECT_THAT(out,
+ StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
+ EXPECT_EQ(
+ 0, RunCommand("", {kSimpleCommand},
+ CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandRedirectStderr) {
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
+ CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithOneArg) {
+ EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
+ EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandDryRun) {
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+ // We don't know the exact duration, so we check the prefix and suffix
+ EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand +
+ ") ------\n\t(skipped on dry run)\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunAlways) {
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandNotFound) {
+ EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+ EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+ EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateTest, RunCommandFails) {
+ EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+ EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
+ " --exit 42' failed: exit code 42\n"));
+ EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
+ " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandCrashes) {
+ EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
+ // We don't know the exit code, so check just the prefix.
+ EXPECT_THAT(
+ out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+ EXPECT_THAT(
+ err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateTest, RunCommandTimesout) {
+ EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
+ CommandOptions::WithTimeout(1).Build()));
+ EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
+ " --sleep 2' timed out after 1"));
+ EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
+ " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateTest, RunCommandIsKilled) {
+ CaptureStdout();
+ CaptureStderr();
+
+ std::thread t([=]() {
+ EXPECT_EQ(SIGTERM, ds.RunCommand("", {kSimpleCommand, "--pid", "--sleep", "20"},
+ CommandOptions::WithTimeout(100).Always().Build()));
+ });
+
+ // Capture pid and pre-sleep output.
+ sleep(1); // Wait a little bit to make sure pid and 1st line were printed.
+ std::string err = GetCapturedStderr();
+ EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+ std::string out = GetCapturedStdout();
+ std::vector<std::string> lines = android::base::Split(out, "\n");
+ ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+ int pid = atoi(lines[0].c_str());
+ EXPECT_THAT(lines[1], StrEq("stdout line1"));
+ EXPECT_THAT(lines[2], IsEmpty()); // \n
+
+ // Then kill the process.
+ CaptureStdout();
+ CaptureStderr();
+ ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+ t.join();
+
+ // Finally, check output after murder.
+ out = GetCapturedStdout();
+ err = GetCapturedStderr();
+
+ EXPECT_THAT(out, StrEq("*** command '" + kSimpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+ EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandProgress) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ ds.listener_ = listener;
+ ds.listener_name_ = "FoxMulder";
+ SetProgress(0, 30);
+
+ EXPECT_CALL(*listener, onProgressUpdated(20));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build()));
+ std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ EXPECT_CALL(*listener, onProgressUpdated(30));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 30, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Run a command that will increase maximum timeout.
+ EXPECT_CALL(*listener, onProgressUpdated(31));
+ EXPECT_CALL(*listener, onMaxProgressUpdated(37));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30); // 20% increase
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Make sure command ran while in dry_run is counted.
+ SetDryRun(true);
+ EXPECT_CALL(*listener, onProgressUpdated(35));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 35, 37);
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq(progress_message));
+
+ ds.listener_.clear();
+}
+
+TEST_F(DumpstateTest, RunCommandProgressIgnoreThreshold) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ ds.listener_ = listener;
+ ds.listener_name_ = "FoxMulder";
+ SetProgress(0, 8, 5); // 8 max, 5 threshold
+
+ // First update should always be sent.
+ EXPECT_CALL(*listener, onProgressUpdated(1));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+ std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Fourth update should be ignored because it's between the threshold (5 -1 = 4 < 5).
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+
+ // Third update should be sent because it reaches threshold (6 - 1 = 5).
+ EXPECT_CALL(*listener, onProgressUpdated(6));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 6, 8);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Fourth update should be ignored because it's between the threshold (9 - 6 = 3 < 5).
+ // But max update should be sent.
+ EXPECT_CALL(*listener, onMaxProgressUpdated(10)); // 9 * 120% = 10.8 = 10
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 9, 10, 8, false);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ ds.listener_.clear();
+}
+
+TEST_F(DumpstateTest, RunCommandDropRoot) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateTest.RunCommandDropRoot() on test suite\n")
+ return;
+ }
+ // First check root case - only available when running with 'adb root'.
+ uid_t uid = getuid();
+ if (uid == 0) {
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+ return;
+ }
+ // Then run dropping root.
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).DropRoot().Build()));
+ EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+ EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateTest.RunCommandAsRootUserBuild() on test suite\n")
+ return;
+ }
+ if (!PropertiesHelper::IsUserBuild()) {
+ // Emulates user build if necessarily.
+ SetBuildType("user");
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ // We don't know the exact path of su, so we just check for the 'root ...' commands
+ EXPECT_THAT(out, StartsWith("Skipping"));
+ EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateTest.RunCommandAsRootNonUserBuild() on test suite\n")
+ return;
+ }
+ if (PropertiesHelper::IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+ return;
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
+ EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+ EXPECT_THAT(out,
+ StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundWithTitle) {
+ EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+ EXPECT_THAT(err, IsEmpty());
+ // We don't know the exact duration, so we check the prefix and suffix
+ EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No "
+ "such file or directory\n"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'Y U NO EXIST?' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLine) {
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLineWithNewLine) {
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLines) {
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLinesWithNewLine) {
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRunNoTitle) {
+ SetDryRun(true);
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRun) {
+ SetDryRun(true);
+ EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(
+ out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
+ EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileUpdateProgress) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ ds.listener_ = listener;
+ ds.listener_name_ = "FoxMulder";
+ SetProgress(0, 30);
+
+ EXPECT_CALL(*listener, onProgressUpdated(5));
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+
+ std::string progress_message =
+ GetProgressMessage(ds.listener_name_, 5, 30); // TODO: unhardcode WEIGHT_FILE (5)?
+ EXPECT_THAT(err, StrEq(progress_message));
+ EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
+
+ ds.listener_.clear();
+}
+
+class DumpstateServiceTest : public DumpstateBaseTest {
+ public:
+ DumpstateService dss;
+};
+
+TEST_F(DumpstateServiceTest, SetListenerNoName) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ sp<IDumpstateToken> token;
+ EXPECT_TRUE(dss.setListener("", listener, &token).isOk());
+ ASSERT_THAT(token, IsNull());
+}
+
+TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
+ sp<IDumpstateToken> token;
+ EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk());
+ ASSERT_THAT(token, IsNull());
+}
+
+TEST_F(DumpstateServiceTest, SetListenerTwice) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ sp<IDumpstateToken> token;
+ EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk());
+ ASSERT_THAT(token, NotNull());
+ EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+
+ token.clear();
+ EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk());
+ ASSERT_THAT(token, IsNull());
+ EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+}
+
+class ProgressTest : public DumpstateBaseTest {
+ public:
+ Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {
+ return Progress(max, growth_factor, path);
+ }
+
+ void AssertStats(const std::string& path, int32_t expected_runs, int32_t expected_average) {
+ std::string expected_content =
+ android::base::StringPrintf("%d %d\n", expected_runs, expected_average);
+ std::string actual_content;
+ ReadFileToString(path, &actual_content);
+ ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path;
+ }
+};
+
+TEST_F(ProgressTest, SimpleTest) {
+ Progress progress;
+ EXPECT_EQ(0, progress.Get());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+
+ bool max_increased = progress.Inc(1);
+ EXPECT_EQ(1, progress.Get());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ // Ignore negative increase.
+ max_increased = progress.Inc(-1);
+ EXPECT_EQ(1, progress.Get());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+ EXPECT_FALSE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsInsideNewRange) {
+ Progress progress = GetInstance(10, 1.2); // 20% growth factor
+ EXPECT_EQ(0, progress.Get());
+ EXPECT_EQ(10, progress.GetInitialMax());
+ EXPECT_EQ(10, progress.GetMax());
+
+ // No increase
+ bool max_increased = progress.Inc(10);
+ EXPECT_EQ(10, progress.Get());
+ EXPECT_EQ(10, progress.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ // Increase, with new value < max*20%
+ max_increased = progress.Inc(1);
+ EXPECT_EQ(11, progress.Get());
+ EXPECT_EQ(13, progress.GetMax()); // 11 average * 20% growth = 13.2 = 13
+ EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsOutsideNewRange) {
+ Progress progress = GetInstance(10, 1.2); // 20% growth factor
+ EXPECT_EQ(0, progress.Get());
+ EXPECT_EQ(10, progress.GetInitialMax());
+ EXPECT_EQ(10, progress.GetMax());
+
+ // No increase
+ bool max_increased = progress.Inc(10);
+ EXPECT_EQ(10, progress.Get());
+ EXPECT_EQ(10, progress.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ // Increase, with new value > max*20%
+ max_increased = progress.Inc(5);
+ EXPECT_EQ(15, progress.Get());
+ EXPECT_EQ(18, progress.GetMax()); // 15 average * 20% growth = 18
+ EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, InvalidPath) {
+ Progress progress("/devil/null");
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, EmptyFile) {
+ Progress progress(CopyTextFileFixture("empty-file.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNAN) {
+ Progress progress(CopyTextFileFixture("stats-invalid-1st-NAN.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNAN) {
+ Progress progress(CopyTextFileFixture("stats-invalid-2nd-NAN.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLineBothNAN) {
+ Progress progress(CopyTextFileFixture("stats-invalid-both-NAN.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNegative) {
+ Progress progress(CopyTextFileFixture("stats-invalid-1st-negative.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNegative) {
+ Progress progress(CopyTextFileFixture("stats-invalid-2nd-negative.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryTooBig) {
+ Progress progress(CopyTextFileFixture("stats-invalid-1st-too-big.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryTooBig) {
+ Progress progress(CopyTextFileFixture("stats-invalid-2nd-too-big.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+// Tests stats are properly saved when the file does not exists.
+TEST_F(ProgressTest, FirstTime) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it's failing when running as suite
+ MYLOGE("Skipping ProgressTest.FirstTime() on test suite\n")
+ return;
+ }
+
+ std::string path = kTestDataPath + "FirstTime.txt";
+ android::base::RemoveFileIfExists(path);
+
+ Progress run1(path);
+ EXPECT_EQ(0, run1.Get());
+ EXPECT_EQ(Progress::kDefaultMax, run1.GetInitialMax());
+ EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+
+ bool max_increased = run1.Inc(20);
+ EXPECT_EQ(20, run1.Get());
+ EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ run1.Save();
+ AssertStats(path, 1, 20);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 1 run.
+// Data on file is 1 run and 109 average.
+TEST_F(ProgressTest, SecondTime) {
+ std::string path = CopyTextFileFixture("stats-one-run-no-newline.txt");
+
+ Progress run1 = GetInstance(-42, 1.2, path);
+ EXPECT_EQ(0, run1.Get());
+ EXPECT_EQ(10, run1.GetInitialMax());
+ EXPECT_EQ(10, run1.GetMax());
+
+ bool max_increased = run1.Inc(20);
+ EXPECT_EQ(20, run1.Get());
+ EXPECT_EQ(24, run1.GetMax());
+ EXPECT_TRUE(max_increased);
+
+ // Average now is 2 runs and (10 + 20)/ 2 = 15
+ run1.Save();
+ AssertStats(path, 2, 15);
+
+ Progress run2 = GetInstance(-42, 1.2, path);
+ EXPECT_EQ(0, run2.Get());
+ EXPECT_EQ(15, run2.GetInitialMax());
+ EXPECT_EQ(15, run2.GetMax());
+
+ max_increased = run2.Inc(25);
+ EXPECT_EQ(25, run2.Get());
+ EXPECT_EQ(30, run2.GetMax());
+ EXPECT_TRUE(max_increased);
+
+ // Average now is 3 runs and (15 * 2 + 25)/ 3 = 18.33 = 18
+ run2.Save();
+ AssertStats(path, 3, 18);
+
+ Progress run3 = GetInstance(-42, 1.2, path);
+ EXPECT_EQ(0, run3.Get());
+ EXPECT_EQ(18, run3.GetInitialMax());
+ EXPECT_EQ(18, run3.GetMax());
+
+ // Make sure average decreases as well
+ max_increased = run3.Inc(5);
+ EXPECT_EQ(5, run3.Get());
+ EXPECT_EQ(18, run3.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ // Average now is 4 runs and (18 * 3 + 5)/ 4 = 14.75 = 14
+ run3.Save();
+ AssertStats(path, 4, 14);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 2 runs.
+// Data on file is 2 runs and 15 average.
+TEST_F(ProgressTest, ThirdTime) {
+ std::string path = CopyTextFileFixture("stats-two-runs.txt");
+ AssertStats(path, 2, 15); // Sanity check
+
+ Progress run1 = GetInstance(-42, 1.2, path);
+ EXPECT_EQ(0, run1.Get());
+ EXPECT_EQ(15, run1.GetInitialMax());
+ EXPECT_EQ(15, run1.GetMax());
+
+ bool max_increased = run1.Inc(20);
+ EXPECT_EQ(20, run1.Get());
+ EXPECT_EQ(24, run1.GetMax());
+ EXPECT_TRUE(max_increased);
+
+ // Average now is 3 runs and (15 * 2 + 20)/ 3 = 16.66 = 16
+ run1.Save();
+ AssertStats(path, 3, 16);
+}
+
+class DumpstateUtilTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ SetDryRun(false);
+ }
+
+ void CaptureFdOut() {
+ ReadFileToString(path_, &out);
+ }
+
+ void CreateFd(const std::string& name) {
+ path_ = kTestDataPath + name;
+ MYLOGD("Creating fd for file %s\n", path_.c_str());
+
+ fd = TEMP_FAILURE_RETRY(open(path_.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+ ASSERT_GE(fd, 0) << "could not create FD for path " << path_;
+ }
+
+ // Runs a command into the `fd` and capture `stderr`.
+ int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ CaptureStderr();
+ int status = RunCommandToFd(fd, title, full_command, options);
+ close(fd);
+
+ CaptureFdOut();
+ err = GetCapturedStderr();
+ return status;
+ }
+
+ // Dumps a file and into the `fd` and `stderr`.
+ int DumpFile(const std::string& title, const std::string& path) {
+ CaptureStderr();
+ int status = DumpFileToFd(fd, title, path);
+ close(fd);
+
+ CaptureFdOut();
+ err = GetCapturedStderr();
+ 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.
+ std::string out, err;
+
+ private:
+ std::string path_;
+};
+
+TEST_F(DumpstateUtilTest, RunCommandNoArgs) {
+ CreateFd("RunCommandNoArgs.txt");
+ EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandNoTitle) {
+ CreateFd("RunCommandWithNoArgs.txt");
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithTitle) {
+ CreateFd("RunCommandWithNoArgs.txt");
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+ EXPECT_THAT(out, StrEq("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithOneArg) {
+ CreateFd("RunCommandWithOneArg.txt");
+ EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithMultipleArgs) {
+ CreateFd("RunCommandWithMultipleArgs.txt");
+ EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithLoggingMessage) {
+ CreateFd("RunCommandWithLoggingMessage.txt");
+ EXPECT_EQ(
+ 0, RunCommand("", {kSimpleCommand},
+ CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandRedirectStderr) {
+ CreateFd("RunCommandRedirectStderr.txt");
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
+ CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRun) {
+ CreateFd("RunCommandDryRun.txt");
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+ EXPECT_THAT(out, StrEq(android::base::StringPrintf(
+ "------ I AM GROOT (%s) ------\n\t(skipped on dry run)\n",
+ kSimpleCommand.c_str())));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRunNoTitle) {
+ CreateFd("RunCommandDryRun.txt");
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+ EXPECT_THAT(
+ out, StrEq(android::base::StringPrintf("%s: skipped on dry run\n", kSimpleCommand.c_str())));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRunAlways) {
+ CreateFd("RunCommandDryRunAlways.txt");
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandNotFound) {
+ CreateFd("RunCommandNotFound.txt");
+ EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+ EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+ EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandFails) {
+ CreateFd("RunCommandFails.txt");
+ EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+ EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
+ " --exit 42' failed: exit code 42\n"));
+ EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
+ " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandCrashes) {
+ CreateFd("RunCommandCrashes.txt");
+ EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
+ // We don't know the exit code, so check just the prefix.
+ EXPECT_THAT(
+ out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+ EXPECT_THAT(
+ err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandTimesout) {
+ CreateFd("RunCommandTimesout.txt");
+ EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
+ CommandOptions::WithTimeout(1).Build()));
+ EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
+ " --sleep 2' timed out after 1"));
+ EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
+ " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandIsKilled) {
+ CreateFd("RunCommandIsKilled.txt");
+ CaptureStderr();
+
+ std::thread t([=]() {
+ EXPECT_EQ(SIGTERM, RunCommandToFd(fd, "", {kSimpleCommand, "--pid", "--sleep", "20"},
+ CommandOptions::WithTimeout(100).Always().Build()));
+ });
+
+ // Capture pid and pre-sleep output.
+ sleep(1); // Wait a little bit to make sure pid and 1st line were printed.
+ std::string err = GetCapturedStderr();
+ EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+ CaptureFdOut();
+ std::vector<std::string> lines = android::base::Split(out, "\n");
+ ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+ int pid = atoi(lines[0].c_str());
+ EXPECT_THAT(lines[1], StrEq("stdout line1"));
+ EXPECT_THAT(lines[2], IsEmpty()); // \n
+
+ // Then kill the process.
+ CaptureFdOut();
+ CaptureStderr();
+ ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+ t.join();
+
+ // Finally, check output after murder.
+ CaptureFdOut();
+ err = GetCapturedStderr();
+
+ // out starts with the pid, which is an unknown
+ EXPECT_THAT(out, EndsWith("stdout line1\n*** command '" + kSimpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+ EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootUserBuild() on test suite\n")
+ return;
+ }
+ CreateFd("RunCommandAsRootUserBuild.txt");
+ if (!PropertiesHelper::IsUserBuild()) {
+ // Emulates user build if necessarily.
+ SetBuildType("user");
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ // We don't know the exact path of su, so we just check for the 'root ...' commands
+ EXPECT_THAT(out, StartsWith("Skipping"));
+ EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootNonUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootNonUserBuild() on test suite\n")
+ return;
+ }
+ CreateFd("RunCommandAsRootNonUserBuild.txt");
+ if (PropertiesHelper::IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+ return;
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDropRoot) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateUtilTest.RunCommandDropRoot() on test suite\n")
+ return;
+ }
+ CreateFd("RunCommandDropRoot.txt");
+ // First check root case - only available when running with 'adb root'.
+ uid_t uid = getuid();
+ if (uid == 0) {
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+ return;
+ }
+ // Then run dropping root.
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).DropRoot().Build()));
+ EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+ EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileNotFoundNoTitle) {
+ CreateFd("DumpFileNotFound.txt");
+ EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+ EXPECT_THAT(out,
+ StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, DumpFileNotFoundWithTitle) {
+ CreateFd("DumpFileNotFound.txt");
+ EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+ EXPECT_THAT(out, StrEq("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No such "
+ "file or directory\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, DumpFileSingleLine) {
+ CreateFd("DumpFileSingleLine.txt");
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateUtilTest, DumpFileSingleLineWithNewLine) {
+ CreateFd("DumpFileSingleLineWithNewLine.txt");
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileMultipleLines) {
+ CreateFd("DumpFileMultipleLines.txt");
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileMultipleLinesWithNewLine) {
+ CreateFd("DumpFileMultipleLinesWithNewLine.txt");
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileOnDryRunNoTitle) {
+ CreateFd("DumpFileOnDryRun.txt");
+ SetDryRun(true);
+ std::string path = kTestDataPath + "single-line.txt";
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq(path + ": skipped on dry run\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileOnDryRun) {
+ CreateFd("DumpFileOnDryRun.txt");
+ SetDryRun(true);
+ std::string path = kTestDataPath + "single-line.txt";
+ EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(
+ 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/tests/dumpstate_test_fixture.cpp b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
new file mode 100644
index 0000000..5be4719
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+void PrintDefaultOutput() {
+ fprintf(stdout, "stdout\n");
+ fflush(stdout);
+ fprintf(stderr, "stderr\n");
+ fflush(stderr);
+}
+
+/*
+ * Binary used to on RunCommand tests.
+ *
+ * Usage:
+ *
+ * - Unless stated otherwise this command:
+ *
+ * 1.Prints `stdout\n` on `stdout` and flushes it.
+ * 2.Prints `stderr\n` on `stderr` and flushes it.
+ * 3.Exit with status 0.
+ *
+ * - If 1st argument is '--pid', it first prints its pid on `stdout`.
+ *
+ * - If 1st argument is '--uid', it first prints its uid on `stdout`.
+ *
+ * - If 1st argument is '--crash', it uses ALOGF to crash and returns 666.
+ *
+ * - With argument '--exit' 'CODE', returns CODE;
+ *
+ * - With argument '--sleep 'TIME':
+ *
+ * 1.Prints `stdout line1\n` on `stdout` and `sleeping TIME s\n` on `stderr`
+ * 2.Sleeps for TIME s
+ * 3.Prints `stdout line2\n` on `stdout` and `woke up\n` on `stderr`
+ */
+int main(int argc, char* const argv[]) {
+ if (argc == 2) {
+ if (strcmp(argv[1], "--crash") == 0) {
+ PrintDefaultOutput();
+ LOG_FATAL("D'OH\n");
+ return 666;
+ }
+ }
+ if (argc == 3) {
+ if (strcmp(argv[1], "--exit") == 0) {
+ PrintDefaultOutput();
+ return atoi(argv[2]);
+ }
+ }
+
+ if (argc > 1) {
+ int index = 1;
+
+ // First check arguments that can shift the index.
+ if (strcmp(argv[1], "--pid") == 0) {
+ index++;
+ fprintf(stdout, "%d\n", getpid());
+ fflush(stdout);
+ } else if (strcmp(argv[1], "--uid") == 0) {
+ index++;
+ fprintf(stdout, "%d\n", getuid());
+ fflush(stdout);
+ }
+
+ // Then the "common" arguments, if any.
+ if (argc > index + 1) {
+ if (strcmp(argv[index], "--sleep") == 0) {
+ int napTime = atoi(argv[index + 1]);
+ fprintf(stdout, "stdout line1\n");
+ fflush(stdout);
+ fprintf(stderr, "sleeping for %ds\n", napTime);
+ fflush(stderr);
+ sleep(napTime);
+ fprintf(stdout, "stdout line2\n");
+ fflush(stdout);
+ fprintf(stderr, "woke up\n");
+ fflush(stderr);
+ return 0;
+ }
+ }
+ }
+
+ PrintDefaultOutput();
+ return 0;
+}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index af6c666..eefdcbd 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -14,43 +14,65 @@
* limitations under the License.
*/
+#define LOG_TAG "dumpstate"
+
+#include "dumpstate.h"
+
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
+#include <libgen.h>
+#include <math.h>
#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string>
#include <string.h>
#include <sys/capability.h>
#include <sys/inotify.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
-#include <sys/sysconf.h>
#include <sys/time.h>
#include <sys/wait.h>
-#include <sys/klog.h>
#include <time.h>
#include <unistd.h>
-#include <vector>
-#include <sys/prctl.h>
-#define LOG_TAG "dumpstate"
+#include <set>
+#include <string>
+#include <vector>
#include <android-base/file.h>
-#include <cutils/debugger.h>
-#include <cutils/log.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
+#include <debuggerd/client.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
+#include "DumpstateInternal.h"
-#include "dumpstate.h"
+// TODO: remove once moved to namespace
+using android::os::dumpstate::CommandOptions;
+using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::PropertiesHelper;
-static const int64_t NANOS_PER_SEC = 1000000000;
+// Keep in sync with
+// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
+
+/* Most simple commands have 10 as timeout, so 5 is a good estimate */
+static const int32_t WEIGHT_FILE = 5;
+
+// TODO: temporary variables and functions used during C++ refactoring
+static Dumpstate& ds = Dumpstate::GetInstance();
+static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ return ds.RunCommand(title, full_command, options);
+}
/* list of native processes to include in the native dumps */
// This matches the /proc/pid/exe link instead of /proc/pid/cmdline.
@@ -58,47 +80,190 @@
"/system/bin/audioserver",
"/system/bin/cameraserver",
"/system/bin/drmserver",
- "/system/bin/mediacodec", // media.codec
"/system/bin/mediadrmserver",
"/system/bin/mediaextractor", // media.extractor
"/system/bin/mediaserver",
"/system/bin/sdcard",
"/system/bin/surfaceflinger",
"/system/bin/vehicle_network_service",
+ "/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec
NULL,
};
-DurationReporter::DurationReporter(const char *title) : DurationReporter(title, stdout) {}
+/* list of hal interface to dump containing process during native dumps */
+static const char* hal_interfaces_to_dump[] {
+ "android.hardware.audio@2.0::IDevicesFactory",
+ "android.hardware.bluetooth@1.0::IBluetoothHci",
+ "android.hardware.camera.provider@2.4::ICameraProvider",
+ "android.hardware.graphics.composer@2.1::IComposer",
+ "android.hardware.vr@1.0::IVr",
+ "android.hardware.media.omx@1.0::IOmx",
+ NULL,
+};
-DurationReporter::DurationReporter(const char *title, FILE *out) {
- title_ = title;
- if (title) {
- started_ = DurationReporter::nanotime();
+// Reasonable value for max stats.
+static const int STATS_MAX_N_RUNS = 1000;
+static const long STATS_MAX_AVERAGE = 100000;
+
+CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
+
+Dumpstate::Dumpstate(const std::string& version)
+ : pid_(getpid()), version_(version), now_(time(nullptr)) {
+}
+
+Dumpstate& Dumpstate::GetInstance() {
+ static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT));
+ return singleton_;
+}
+
+DurationReporter::DurationReporter(const std::string& title, bool log_only)
+ : title_(title), log_only_(log_only) {
+ if (!title_.empty()) {
+ started_ = Nanotime();
}
- out_ = out;
}
DurationReporter::~DurationReporter() {
- if (title_) {
- uint64_t elapsed = DurationReporter::nanotime() - started_;
- // Use "Yoda grammar" to make it easier to grep|sort sections.
- if (out_) {
- fprintf(out_, "------ %.3fs was the duration of '%s' ------\n",
- (float) elapsed / NANOS_PER_SEC, title_);
+ if (!title_.empty()) {
+ uint64_t elapsed = Nanotime() - started_;
+ if (log_only_) {
+ MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
} else {
- MYLOGD("Duration of '%s': %.3fs\n", title_, (float) elapsed / NANOS_PER_SEC);
+ // Use "Yoda grammar" to make it easier to grep|sort sections.
+ printf("------ %.3fs was the duration of '%s' ------\n", (float)elapsed / NANOS_PER_SEC,
+ title_.c_str());
}
}
}
-uint64_t DurationReporter::DurationReporter::nanotime() {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return (uint64_t) ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
+const int32_t Progress::kDefaultMax = 5000;
+
+Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) {
+}
+
+Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor)
+ : Progress(initial_max, growth_factor, "") {
+ progress_ = progress;
+}
+
+Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path)
+ : initial_max_(initial_max),
+ progress_(0),
+ max_(initial_max),
+ growth_factor_(growth_factor),
+ n_runs_(0),
+ average_max_(0),
+ path_(path) {
+ if (!path_.empty()) {
+ Load();
+ }
+}
+
+void Progress::Load() {
+ MYLOGD("Loading stats from %s\n", path_.c_str());
+ std::string content;
+ if (!android::base::ReadFileToString(path_, &content)) {
+ MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_);
+ return;
+ }
+ if (content.empty()) {
+ MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_);
+ return;
+ }
+ std::vector<std::string> lines = android::base::Split(content, "\n");
+
+ if (lines.size() < 1) {
+ MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(),
+ (int)lines.size(), max_);
+ return;
+ }
+ char* ptr;
+ n_runs_ = strtol(lines[0].c_str(), &ptr, 10);
+ average_max_ = strtol(ptr, nullptr, 10);
+ if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS ||
+ average_max_ > STATS_MAX_AVERAGE) {
+ MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str());
+ initial_max_ = Progress::kDefaultMax;
+ } else {
+ initial_max_ = average_max_;
+ }
+ max_ = initial_max_;
+
+ MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_);
+}
+
+void Progress::Save() {
+ int32_t total = n_runs_ * average_max_ + progress_;
+ int32_t runs = n_runs_ + 1;
+ int32_t average = floor(((float)total) / runs);
+ MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average,
+ path_.c_str());
+ if (path_.empty()) {
+ return;
+ }
+
+ std::string content = android::base::StringPrintf("%d %d\n", runs, average);
+ if (!android::base::WriteStringToFile(content, path_)) {
+ MYLOGE("Could not save stats on %s\n", path_.c_str());
+ }
+}
+
+int32_t Progress::Get() const {
+ return progress_;
+}
+
+bool Progress::Inc(int32_t delta) {
+ bool changed = false;
+ if (delta >= 0) {
+ progress_ += delta;
+ if (progress_ > max_) {
+ int32_t old_max = max_;
+ max_ = floor((float)progress_ * growth_factor_);
+ MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_);
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+int32_t Progress::GetMax() const {
+ return max_;
+}
+
+int32_t Progress::GetInitialMax() const {
+ return initial_max_;
+}
+
+void Progress::Dump(int fd, const std::string& prefix) const {
+ const char* pr = prefix.c_str();
+ dprintf(fd, "%sprogress: %d\n", pr, progress_);
+ dprintf(fd, "%smax: %d\n", pr, max_);
+ dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_);
+ dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_);
+ dprintf(fd, "%spath: %s\n", pr, path_.c_str());
+ dprintf(fd, "%sn_runs: %d\n", pr, n_runs_);
+ dprintf(fd, "%saverage_max: %d\n", pr, average_max_);
+}
+
+bool Dumpstate::IsZipping() const {
+ return zip_writer_ != nullptr;
+}
+
+std::string Dumpstate::GetPath(const std::string& suffix) const {
+ return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(),
+ name_.c_str(), suffix.c_str());
+}
+
+void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) {
+ progress_ = std::move(progress);
}
void for_each_userid(void (*func)(int), const char *header) {
- ON_DRY_RUN_RETURN();
+ std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf(
+ "for_each_userid(%s)", header);
+ DurationReporter duration_reporter(title);
+ if (PropertiesHelper::IsDryRun()) return;
+
DIR *d;
struct dirent *de;
@@ -180,8 +345,12 @@
}
void for_each_pid(for_each_pid_func func, const char *header) {
- ON_DRY_RUN_RETURN();
- __for_each_pid(for_each_pid_helper, header, (void *)func);
+ std::string title = header == nullptr ? "for_each_pid"
+ : android::base::StringPrintf("for_each_pid(%s)", header);
+ DurationReporter duration_reporter(title);
+ if (PropertiesHelper::IsDryRun()) return;
+
+ __for_each_pid(for_each_pid_helper, header, (void *) func);
}
static void for_each_tid_helper(int pid, const char *cmdline, void *arg) {
@@ -233,12 +402,18 @@
}
void for_each_tid(for_each_tid_func func, const char *header) {
- ON_DRY_RUN_RETURN();
+ std::string title = header == nullptr ? "for_each_tid"
+ : android::base::StringPrintf("for_each_tid(%s)", header);
+ DurationReporter duration_reporter(title);
+
+ if (PropertiesHelper::IsDryRun()) return;
+
__for_each_pid(for_each_tid_helper, header, (void *) func);
}
void show_wchan(int pid, int tid, const char *name) {
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
+
char path[255];
char buffer[255];
int fd, ret, save_errno;
@@ -304,7 +479,8 @@
}
void show_showtime(int pid, const char *name) {
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
+
char path[255];
char buffer[1023];
int fd, ret, save_errno;
@@ -361,7 +537,7 @@
if (iotime) {
snprdec(buffer, sizeof(buffer), 79, permille);
}
- puts(buffer); // adds a trailing newline
+ puts(buffer); // adds a trailing newline
return;
}
@@ -371,7 +547,8 @@
DurationReporter duration_reporter(title);
printf("------ %s ------\n", title);
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
+
/* Get size of kernel buffer */
int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
if (size <= 0) {
@@ -401,84 +578,17 @@
snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name);
snprintf(arg, sizeof(arg), "%d", pid);
- run_command(title, 10, SU_PATH, "root", "showmap", "-q", arg, NULL);
+ RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT);
}
-static int _dump_file_from_fd(const char *title, const char *path, int fd) {
- if (title) {
- printf("------ %s (%s", title, path);
-
- struct stat st;
- // Only show the modification time of non-device files.
- size_t path_len = strlen(path);
- if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
- (path_len < 5 || memcmp(path, "/sys/", 5)) &&
- (path_len < 3 || memcmp(path, "/d/", 3)) &&
- !fstat(fd, &st)) {
- char stamp[80];
- time_t mtime = st.st_mtime;
- strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
- printf(": %s", stamp);
- }
- printf(") ------\n");
- }
- ON_DRY_RUN({ update_progress(WEIGHT_FILE); close(fd); return 0; });
-
- bool newline = false;
- fd_set read_set;
- struct timeval tm;
- while (1) {
- FD_ZERO(&read_set);
- FD_SET(fd, &read_set);
- /* Timeout if no data is read for 30 seconds. */
- tm.tv_sec = 30;
- tm.tv_usec = 0;
- uint64_t elapsed = DurationReporter::nanotime();
- int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, NULL, NULL, &tm));
- if (ret == -1) {
- printf("*** %s: select failed: %s\n", path, strerror(errno));
- newline = true;
- break;
- } else if (ret == 0) {
- elapsed = DurationReporter::nanotime() - elapsed;
- printf("*** %s: Timed out after %.3fs\n", path,
- (float) elapsed / NANOS_PER_SEC);
- newline = true;
- break;
- } else {
- char buffer[65536];
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
- if (bytes_read > 0) {
- fwrite(buffer, bytes_read, 1, stdout);
- newline = (buffer[bytes_read-1] == '\n');
- } else {
- if (bytes_read == -1) {
- printf("*** %s: Failed to read from fd: %s", path, strerror(errno));
- newline = true;
- }
- break;
- }
- }
- }
- update_progress(WEIGHT_FILE);
- close(fd);
-
- if (!newline) printf("\n");
- if (title) printf("\n");
- return 0;
-}
-
-/* prints the contents of a file */
-int dump_file(const char *title, const char *path) {
+int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
DurationReporter duration_reporter(title);
- int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
- if (fd < 0) {
- int err = errno;
- printf("*** %s: %s\n", path, strerror(err));
- if (title) printf("\n");
- return -1;
- }
- return _dump_file_from_fd(title, path, fd);
+
+ int status = DumpFileToFd(STDOUT_FILENO, title, path);
+
+ UpdateProgress(WEIGHT_FILE);
+
+ return status;
}
int read_file_as_long(const char *path, long int *output) {
@@ -508,9 +618,8 @@
* to false when set to NULL. dump_from_fd will always be
* called with title NULL.
*/
-int dump_files(const char *title, const char *dir,
- bool (*skip)(const char *path),
- int (*dump_from_fd)(const char *title, const char *path, int fd)) {
+int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
+ int (*dump_from_fd)(const char* title, const char* path, int fd)) {
DurationReporter duration_reporter(title);
DIR *dirp;
struct dirent *d;
@@ -518,10 +627,10 @@
const char *slash = "/";
int fd, retval = 0;
- if (title) {
- printf("------ %s (%s) ------\n", title, dir);
+ if (!title.empty()) {
+ printf("------ %s (%s) ------\n", title.c_str(), dir);
}
- ON_DRY_RUN_RETURN(0);
+ if (PropertiesHelper::IsDryRun()) return 0;
if (dir[strlen(dir) - 1] == '/') {
++slash;
@@ -552,7 +661,7 @@
continue;
}
if (d->d_type == DT_DIR) {
- int ret = dump_files(NULL, newpath, skip, dump_from_fd);
+ int ret = dump_files("", newpath, skip, dump_from_fd);
if (ret < 0) {
retval = ret;
}
@@ -567,7 +676,7 @@
(*dump_from_fd)(NULL, newpath, fd);
}
closedir(dirp);
- if (title) {
+ if (!title.empty()) {
printf("\n");
}
return retval;
@@ -578,6 +687,8 @@
* stuck.
*/
int dump_file_from_fd(const char *title, const char *path, int fd) {
+ if (PropertiesHelper::IsDryRun()) return 0;
+
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno));
@@ -588,332 +699,30 @@
close(fd);
return -1;
}
- return _dump_file_from_fd(title, path, fd);
+ return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun());
}
-bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
- sigset_t child_mask, old_mask;
- sigemptyset(&child_mask);
- sigaddset(&child_mask, SIGCHLD);
-
- if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
- printf("*** sigprocmask failed: %s\n", strerror(errno));
- return false;
- }
-
- struct timespec ts;
- ts.tv_sec = timeout_seconds;
- ts.tv_nsec = 0;
- int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
- int saved_errno = errno;
- // Set the signals back the way they were.
- if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
- printf("*** sigprocmask failed: %s\n", strerror(errno));
- if (ret == 0) {
- return false;
- }
- }
- if (ret == -1) {
- errno = saved_errno;
- if (errno == EAGAIN) {
- errno = ETIMEDOUT;
- } else {
- printf("*** sigtimedwait failed: %s\n", strerror(errno));
- }
- return false;
- }
-
- pid_t child_pid = waitpid(pid, status, WNOHANG);
- if (child_pid != pid) {
- if (child_pid != -1) {
- printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
- } else {
- printf("*** waitpid failed: %s\n", strerror(errno));
- }
- return false;
- }
- return true;
-}
-
-// TODO: refactor all those commands that convert args
-void format_args(const char* command, const char *args[], std::string *string);
-
-int run_command(const char *title, int timeout_seconds, const char *command, ...) {
+int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options) {
DurationReporter duration_reporter(title);
- fflush(stdout);
- const char *args[1024] = {command};
- size_t arg;
- va_list ap;
- va_start(ap, command);
- if (title) printf("------ %s (%s", title, command);
- bool null_terminated = false;
- for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
- args[arg] = va_arg(ap, const char *);
- if (args[arg] == nullptr) {
- null_terminated = true;
- break;
- }
- // TODO: null_terminated check is not really working; line below would crash dumpstate if
- // nullptr is missing
- if (title) printf(" %s", args[arg]);
- }
- if (title) printf(") ------\n");
- fflush(stdout);
- if (!null_terminated) {
- // Fail now, otherwise execvp() call on run_command_always() might hang.
- std::string cmd;
- format_args(command, args, &cmd);
- MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
- return -1;
- }
+ int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
- ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
+ /* TODO: for now we're simplifying the progress calculation by using the
+ * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
+ * where its weight should be much higher proportionally to its timeout.
+ * Ideally, it should use a options.EstimatedDuration() instead...*/
+ UpdateProgress(options.Timeout());
- int status = run_command_always(title, DONT_DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
- va_end(ap);
return status;
}
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...) {
- DurationReporter duration_reporter(title);
- fflush(stdout);
-
- const char *args[1024] = {command};
- size_t arg;
- va_list ap;
- va_start(ap, command);
- if (title) printf("------ %s (%s", title, command);
- bool null_terminated = false;
- for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
- args[arg] = va_arg(ap, const char *);
- if (args[arg] == nullptr) {
- null_terminated = true;
- break;
- }
- // TODO: null_terminated check is not really working; line below would crash dumpstate if
- // nullptr is missing
- if (title) printf(" %s", args[arg]);
- }
- if (title) printf(") ------\n");
- fflush(stdout);
- if (!null_terminated) {
- // Fail now, otherwise execvp() call on run_command_always() might hang.
- std::string cmd;
- format_args(command, args, &cmd);
- MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
- return -1;
- }
-
- ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
-
- int status = run_command_always(title, DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
- va_end(ap);
- return status;
-}
-
-/* forks a command and waits for it to finish */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
- int timeout_seconds, const char *args[]) {
- bool silent = (stdout_mode == REDIRECT_TO_STDERR);
- // TODO: need to check if args is null-terminated, otherwise execvp will crash dumpstate
-
- /* TODO: for now we're simplifying the progress calculation by using the timeout as the weight.
- * It's a good approximation for most cases, except when calling dumpsys, where its weight
- * should be much higher proportionally to its timeout. */
- int weight = timeout_seconds;
-
- const char *command = args[0];
- uint64_t start = DurationReporter::nanotime();
- pid_t pid = fork();
-
- /* handle error case */
- if (pid < 0) {
- if (!silent) printf("*** fork: %s\n", strerror(errno));
- MYLOGE("*** fork: %s\n", strerror(errno));
- return pid;
- }
-
- /* handle child case */
- if (pid == 0) {
- if (root_mode == DROP_ROOT && !drop_root_user()) {
- if (!silent) printf("*** fail todrop root before running %s: %s\n", command,
- strerror(errno));
- MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
- return -1;
- }
-
- if (silent) {
- // Redirect stderr to stdout
- dup2(STDERR_FILENO, STDOUT_FILENO);
- }
-
- /* make sure the child dies when dumpstate dies */
- prctl(PR_SET_PDEATHSIG, SIGKILL);
-
- /* just ignore SIGPIPE, will go down with parent's */
- struct sigaction sigact;
- memset(&sigact, 0, sizeof(sigact));
- sigact.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sigact, NULL);
-
- execvp(command, (char**) args);
- // execvp's result will be handled after waitpid_with_timeout() below, but if it failed,
- // it's safer to exit dumpstate.
- MYLOGD("execvp on command '%s' failed (error: %s)", command, strerror(errno));
- fflush(stdout);
- // Must call _exit (instead of exit), otherwise it will corrupt the zip file.
- _exit(EXIT_FAILURE);
- }
-
- /* handle parent case */
- int status;
- bool ret = waitpid_with_timeout(pid, timeout_seconds, &status);
- uint64_t elapsed = DurationReporter::nanotime() - start;
- std::string cmd; // used to log command and its args
- if (!ret) {
- if (errno == ETIMEDOUT) {
- format_args(command, args, &cmd);
- if (!silent) printf("*** command '%s' timed out after %.3fs (killing pid %d)\n",
- cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
- MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
- (float) elapsed / NANOS_PER_SEC, pid);
- } else {
- format_args(command, args, &cmd);
- if (!silent) printf("*** command '%s': Error after %.4fs (killing pid %d)\n",
- cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
- MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
- (float) elapsed / NANOS_PER_SEC, pid);
- }
- kill(pid, SIGTERM);
- if (!waitpid_with_timeout(pid, 5, NULL)) {
- kill(pid, SIGKILL);
- if (!waitpid_with_timeout(pid, 5, NULL)) {
- if (!silent) printf("could not kill command '%s' (pid %d) even with SIGKILL.\n",
- command, pid);
- MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
- }
- }
- return -1;
- } else if (status) {
- format_args(command, args, &cmd);
- if (!silent) printf("*** command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
- MYLOGE("command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
- return -2;
- }
-
- if (WIFSIGNALED(status)) {
- if (!silent) printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
- MYLOGE("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
- } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
- if (!silent) printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
- MYLOGE("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
- }
-
- if (weight > 0) {
- update_progress(weight);
- }
- return status;
-}
-
-bool drop_root_user() {
- if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
- MYLOGD("drop_root_user(): already running as Shell");
- return true;
- }
- /* ensure we will keep capabilities when we drop root */
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
- return false;
- }
-
- 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;
- }
- if (setgid(AID_SHELL) != 0) {
- MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
- return false;
- }
- if (setuid(AID_SHELL) != 0) {
- MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
- return false;
- }
-
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- 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;
-
- if (capset(&capheader, &capdata[0]) < 0) {
- MYLOGE("capset failed: %s\n", strerror(errno));
- return false;
- }
-
- return true;
-}
-
-void send_broadcast(const std::string& action, const std::vector<std::string>& args) {
- if (args.size() > 1000) {
- MYLOGE("send_broadcast: too many arguments (%d)\n", (int) args.size());
- return;
- }
- const char *am_args[1024] = { "/system/bin/am", "broadcast", "--user", "0", "-a",
- action.c_str() };
- size_t am_index = 5; // Starts at the index of last initial value above.
- for (const std::string& arg : args) {
- am_args[++am_index] = arg.c_str();
- }
- // Always terminate with NULL.
- am_args[am_index + 1] = NULL;
- std::string args_string;
- format_args(am_index + 1, am_args, &args_string);
- MYLOGD("send_broadcast command: %s\n", args_string.c_str());
- run_command_always(NULL, DROP_ROOT, REDIRECT_TO_STDERR, 20, am_args);
-}
-
-size_t num_props = 0;
-static char* props[2000];
-
-static void print_prop(const char *key, const char *name, void *user) {
- (void) user;
- if (num_props < sizeof(props) / sizeof(props[0])) {
- char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
- snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
- props[num_props++] = strdup(buf);
- }
-}
-
-static int compare_prop(const void *a, const void *b) {
- return strcmp(*(char * const *) a, *(char * const *) b);
-}
-
-/* prints all the system properties */
-void print_properties() {
- const char* title = "SYSTEM PROPERTIES";
- DurationReporter duration_reporter(title);
- printf("------ %s ------\n", title);
- ON_DRY_RUN_RETURN();
- size_t i;
- num_props = 0;
- property_list(print_prop, NULL);
- qsort(&props, num_props, sizeof(props[0]), compare_prop);
-
- for (i = 0; i < num_props; ++i) {
- fputs(props[i], stdout);
- free(props[i]);
- }
- printf("\n");
+void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
+ const CommandOptions& options, long dumpsysTimeout) {
+ long timeout = dumpsysTimeout > 0 ? dumpsysTimeout : options.Timeout();
+ std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-t", std::to_string(timeout)};
+ dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
+ RunCommand(title, dumpsys, options);
}
int open_socket(const char *service) {
@@ -974,11 +783,11 @@
}
}
-/* redirect output to a file */
-void redirect_to_file(FILE *redirect, char *path) {
+void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) {
create_parent_dirs(path);
- int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ int fd = TEMP_FAILURE_RETRY(open(path,
+ O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if (fd < 0) {
MYLOGE("%s: %s\n", path, strerror(errno));
@@ -989,6 +798,23 @@
close(fd);
}
+void redirect_to_file(FILE *redirect, char *path) {
+ _redirect_to_file(redirect, path, O_TRUNC);
+}
+
+void redirect_to_existing_file(FILE *redirect, char *path) {
+ _redirect_to_file(redirect, path, O_APPEND);
+}
+
+static bool should_dump_hal_interface(const char* interface) {
+ for (const char** i = hal_interfaces_to_dump; *i; i++) {
+ if (!strcmp(*i, interface)) {
+ return true;
+ }
+ }
+ return false;
+}
+
static bool should_dump_native_traces(const char* path) {
for (const char** p = native_processes_to_dump; *p; p++) {
if (!strcmp(*p, path)) {
@@ -998,42 +824,70 @@
return false;
}
+std::set<int> get_interesting_hal_pids() {
+ using android::hidl::manager::V1_0::IServiceManager;
+ using android::sp;
+ using android::hardware::Return;
+
+ sp<IServiceManager> manager = IServiceManager::getService();
+ std::set<int> pids;
+
+ Return<void> ret = manager->debugDump([&](auto& hals) {
+ for (const auto &info : hals) {
+ if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) {
+ continue;
+ }
+
+ if (!should_dump_hal_interface(info.interfaceName.c_str())) {
+ continue;
+ }
+
+ pids.insert(info.pid);
+ }
+ });
+
+ if (!ret.isOk()) {
+ MYLOGE("Could not get list of HAL PIDs: %s\n", ret.description().c_str());
+ }
+
+ return pids; // whether it was okay or not
+}
+
/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
const char *dump_traces() {
- DurationReporter duration_reporter("DUMP TRACES", NULL);
- ON_DRY_RUN_RETURN(NULL);
- const char* result = NULL;
+ DurationReporter duration_reporter("DUMP TRACES");
- char traces_path[PROPERTY_VALUE_MAX] = "";
- property_get("dalvik.vm.stack-trace-file", traces_path, "");
- if (!traces_path[0]) return NULL;
+ const char* result = nullptr;
+
+ std::string traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ if (traces_path.empty()) return nullptr;
/* move the old traces.txt (if any) out of the way temporarily */
- char anr_traces_path[PATH_MAX];
- strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
- strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
- if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
- MYLOGE("rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
- return NULL; // Can't rename old traces.txt -- no permission? -- leave it alone instead
+ std::string anrtraces_path = traces_path + ".anr";
+ if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) {
+ MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), anrtraces_path.c_str(), strerror(errno));
+ return nullptr; // Can't rename old traces.txt -- no permission? -- leave it alone instead
}
/* create a new, empty traces.txt file to receive stack dumps */
- int fd = TEMP_FAILURE_RETRY(open(traces_path, 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, strerror(errno));
- return NULL;
+ MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno));
+ return nullptr;
}
int chmod_ret = fchmod(fd, 0666);
if (chmod_ret < 0) {
- MYLOGE("fchmod on %s failed: %s\n", traces_path, strerror(errno));
+ MYLOGE("fchmod on %s failed: %s\n", traces_path.c_str(), strerror(errno));
close(fd);
- return NULL;
+ return nullptr;
}
/* Variables below must be initialized before 'goto' statements */
int dalvik_found = 0;
int ifd, wfd = -1;
+ std::set<int> hal_pids = get_interesting_hal_pids();
/* walk /proc and kill -QUIT all Dalvik processes */
DIR *proc = opendir("/proc");
@@ -1049,9 +903,9 @@
goto error_close_fd;
}
- wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
+ wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE);
if (wfd < 0) {
- MYLOGE("inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
+ MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno));
goto error_close_ifd;
}
@@ -1084,7 +938,7 @@
}
++dalvik_found;
- uint64_t start = DurationReporter::nanotime();
+ uint64_t start = Nanotime();
if (kill(pid, SIGQUIT)) {
MYLOGE("kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
continue;
@@ -1092,7 +946,7 @@
/* wait for the writable-close notification from inotify */
struct pollfd pfd = { ifd, POLLIN, 0 };
- int ret = poll(&pfd, 1, 5000); /* 5 sec timeout */
+ int ret = poll(&pfd, 1, TRACE_DUMP_TIMEOUT_MS);
if (ret < 0) {
MYLOGE("poll: %s\n", strerror(errno));
} else if (ret == 0) {
@@ -1105,16 +959,17 @@
if (lseek(fd, 0, SEEK_END) < 0) {
MYLOGE("lseek: %s\n", strerror(errno));
} else {
- dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n",
- pid, (float)(DurationReporter::nanotime() - start) / NANOS_PER_SEC);
+ dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n", pid,
+ (float)(Nanotime() - start) / NANOS_PER_SEC);
}
- } else if (should_dump_native_traces(data)) {
+ } else if (should_dump_native_traces(data) ||
+ hal_pids.find(pid) != hal_pids.end()) {
/* dump native process if appropriate */
if (lseek(fd, 0, SEEK_END) < 0) {
MYLOGE("lseek: %s\n", strerror(errno));
} else {
static uint16_t timeout_failures = 0;
- uint64_t start = DurationReporter::nanotime();
+ uint64_t start = Nanotime();
/* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
if (timeout_failures == 3) {
@@ -1125,8 +980,8 @@
} else {
timeout_failures = 0;
}
- dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n",
- pid, (float)(DurationReporter::nanotime() - start) / NANOS_PER_SEC);
+ dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n", pid,
+ (float)(Nanotime() - start) / NANOS_PER_SEC);
}
}
}
@@ -1135,17 +990,17 @@
MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
}
- static char dump_traces_path[PATH_MAX];
- strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
- strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
- if (rename(traces_path, dump_traces_path)) {
- MYLOGE("rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
+ static std::string dumptraces_path = android::base::StringPrintf(
+ "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str()));
+ if (rename(traces_path.c_str(), dumptraces_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(),
+ strerror(errno));
goto error_close_ifd;
}
- result = dump_traces_path;
+ result = dumptraces_path.c_str();
/* replace the saved [ANR] traces.txt file */
- rename(anr_traces_path, traces_path);
+ rename(anrtraces_path.c_str(), traces_path.c_str());
error_close_ifd:
close(ifd);
@@ -1156,9 +1011,9 @@
void dump_route_tables() {
DurationReporter duration_reporter("DUMP ROUTE TABLES");
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
- dump_file("RT_TABLES", RT_TABLES_PATH);
+ ds.DumpFile("RT_TABLES", RT_TABLES_PATH);
FILE* fp = fopen(RT_TABLES_PATH, "re");
if (!fp) {
printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno));
@@ -1169,72 +1024,67 @@
// need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name.
// Add a fixed max limit so this doesn't go awry.
for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) {
- run_command("ROUTE TABLE IPv4", 10, "ip", "-4", "route", "show", "table", table, NULL);
- run_command("ROUTE TABLE IPv6", 10, "ip", "-6", "route", "show", "table", table, NULL);
+ RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table});
+ RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table});
}
fclose(fp);
}
-/* overall progress */
-int progress = 0;
-int do_update_progress = 0; // Set by dumpstate.cpp
-int weight_total = WEIGHT_TOTAL;
-
// TODO: make this function thread safe if sections are generated in parallel.
-void update_progress(int delta) {
- if (!do_update_progress) return;
+void Dumpstate::UpdateProgress(int32_t delta) {
+ if (progress_ == nullptr) {
+ MYLOGE("UpdateProgress: progress_ not set\n");
+ return;
+ }
- progress += delta;
+ // Always update progess so stats can be tuned...
+ bool max_changed = progress_->Inc(delta);
- char key[PROPERTY_KEY_MAX];
- char value[PROPERTY_VALUE_MAX];
+ // ...but only notifiy listeners when necessary.
+ if (!update_progress_) return;
+
+ int progress = progress_->Get();
+ int max = progress_->GetMax();
// adjusts max on the fly
- if (progress > weight_total) {
- int new_total = weight_total * 1.2;
- MYLOGD("Adjusting total weight from %d to %d\n", weight_total, new_total);
- weight_total = new_total;
- snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
- snprintf(value, sizeof(value), "%d", weight_total);
- int status = property_set(key, value);
- if (status) {
- MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
- key, value, status);
+ if (max_changed && listener_ != nullptr) {
+ listener_->onMaxProgressUpdated(max);
+ }
+
+ int32_t last_update_delta = progress - last_updated_progress_;
+ if (last_updated_progress_ > 0 && last_update_delta < update_progress_threshold_) {
+ return;
+ }
+ last_updated_progress_ = progress;
+
+ if (control_socket_fd_ >= 0) {
+ dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max);
+ fsync(control_socket_fd_);
+ }
+
+ if (listener_ != nullptr) {
+ if (progress % 100 == 0) {
+ // We don't want to spam logcat, so only log multiples of 100.
+ MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
+ } else {
+ // stderr is ignored on normal invocations, but useful when calling
+ // /system/bin/dumpstate directly for debuggging.
+ fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
}
+ listener_->onProgressUpdated(progress);
}
+}
- snprintf(key, sizeof(key), "dumpstate.%d.progress", getpid());
- snprintf(value, sizeof(value), "%d", progress);
-
- if (progress % 100 == 0) {
- // We don't want to spam logcat, so only log multiples of 100.
- MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total);
+void Dumpstate::TakeScreenshot(const std::string& path) {
+ const std::string& real_path = path.empty() ? screenshot_path_ : path;
+ int status =
+ RunCommand("", {"/system/bin/screencap", "-p", real_path},
+ CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+ if (status == 0) {
+ MYLOGD("Screenshot saved on %s\n", real_path.c_str());
} else {
- // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
- // directly for debuggging.
- fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total);
+ MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
}
-
- if (control_socket_fd >= 0) {
- dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total);
- fsync(control_socket_fd);
- }
-
- int status = property_set(key, value);
- if (status) {
- MYLOGE("Could not update progress by setting system property %s to %s: %d\n",
- key, value, status);
- }
-}
-
-void take_screenshot(const std::string& path) {
- const char *args[] = { "/system/bin/screencap", "-p", path.c_str(), NULL };
- run_command_always(NULL, DONT_DROP_ROOT, REDIRECT_TO_STDERR, 10, args);
-}
-
-void vibrate(FILE* vibrator, int ms) {
- fprintf(vibrator, "%d\n", ms);
- fflush(vibrator);
}
bool is_dir(const char* pathname) {
@@ -1278,19 +1128,16 @@
int ext_csd_rev = 0;
std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex));
if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) {
- printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n",
- ext_csd_path, sub.c_str());
+ printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path, sub.c_str());
return;
}
static const char *ver_str[] = {
"4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
};
- printf("rev 1.%d (MMC %s)\n",
- ext_csd_rev,
- (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
- ver_str[ext_csd_rev] :
- "Unknown");
+ printf("rev 1.%d (MMC %s)\n", ext_csd_rev,
+ (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev]
+ : "Unknown");
if (ext_csd_rev < 7) {
printf("\n");
return;
@@ -1304,8 +1151,7 @@
int ext_pre_eol_info = 0;
sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex));
if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) {
- printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n",
- ext_csd_path, sub.c_str());
+ printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path, sub.c_str());
return;
}
@@ -1315,11 +1161,10 @@
"Warning (consumed 80% of reserve)",
"Urgent (consumed 90% of reserve)"
};
- printf("PRE_EOL_INFO %d (MMC %s)\n",
- ext_pre_eol_info,
- eol_str[(ext_pre_eol_info < (int)
- (sizeof(eol_str) / sizeof(eol_str[0]))) ?
- ext_pre_eol_info : 0]);
+ printf(
+ "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info,
+ eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info
+ : 0]);
for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A;
lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B;
@@ -1348,48 +1193,18 @@
ext_device_life_time_est = 0;
sub = buffer.substr(lifetime, sizeof(hex));
if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) {
- printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n",
- ext_csd_path,
- (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) /
- sizeof(hex)) + 'A',
+ printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n", ext_csd_path,
+ (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A',
sub.c_str());
continue;
}
printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n",
- (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) /
- sizeof(hex)) + 'A',
+ (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A',
ext_device_life_time_est,
- est_str[(ext_device_life_time_est < (int)
- (sizeof(est_str) / sizeof(est_str[0]))) ?
- ext_device_life_time_est : 0]);
+ est_str[(ext_device_life_time_est < (int)(sizeof(est_str) / sizeof(est_str[0])))
+ ? ext_device_life_time_est
+ : 0]);
}
printf("\n");
}
-
-// TODO: refactor all those commands that convert args
-void format_args(int argc, const char *argv[], std::string *args) {
- LOG_ALWAYS_FATAL_IF(args == nullptr);
- for (int i = 0; i < argc; i++) {
- args->append(argv[i]);
- if (i < argc -1) {
- args->append(" ");
- }
- }
-}
-void format_args(const char* command, const char *args[], std::string *string) {
- LOG_ALWAYS_FATAL_IF(args == nullptr || command == nullptr);
- string->append(command);
- if (args[0] == nullptr) return;
- string->append(" ");
-
- for (int arg = 1; arg <= 1000; ++arg) {
- if (args[arg] == nullptr) return;
- string->append(args[arg]);
- if (args[arg+1] != nullptr) {
- string->append(" ");
- }
- }
- // TODO: not really working: if NULL is missing, it will crash dumpstate.
- MYLOGE("internal error: missing NULL entry on %s", string->c_str());
-}
diff --git a/cmds/dumpsys/.clang-format b/cmds/dumpsys/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /dev/null
+++ b/cmds/dumpsys/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+AccessModifierOffset: -2
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp
new file mode 100644
index 0000000..3476964
--- /dev/null
+++ b/cmds/dumpsys/Android.bp
@@ -0,0 +1,50 @@
+cc_defaults {
+ name: "dumpsys_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "dumpsys.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libutils",
+ "liblog",
+ "libbinder",
+ ],
+
+ clang: true,
+}
+
+//
+// Static library used in testing and executable
+//
+
+cc_library_static {
+ name: "libdumpsys",
+
+ defaults: ["dumpsys_defaults"],
+
+ export_include_dirs: ["."],
+}
+
+
+//
+// Executable
+//
+
+cc_binary {
+ name: "dumpsys",
+
+ defaults: ["dumpsys_defaults"],
+
+ srcs: [
+ "main.cpp",
+ ],
+}
+
+subdirs = ["tests"]
diff --git a/cmds/dumpsys/Android.mk b/cmds/dumpsys/Android.mk
deleted file mode 100644
index 8335c14..0000000
--- a/cmds/dumpsys/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- dumpsys.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libutils \
- liblog \
- libbinder
-
-
-ifeq ($(TARGET_OS),linux)
- LOCAL_CFLAGS += -DXP_UNIX
- #LOCAL_SHARED_LIBRARIES += librt
-endif
-
-LOCAL_MODULE:= dumpsys
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index d19e98a..f0e7200 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -1,10 +1,19 @@
/*
- * Command that dumps interesting system state to the log.
+ * Copyright (C) 2009 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 "dumpsys"
-
#include <algorithm>
#include <chrono>
#include <thread>
@@ -12,7 +21,6 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
-#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <binder/TextOutput.h>
@@ -30,6 +38,8 @@
#include <sys/types.h>
#include <unistd.h>
+#include "dumpsys.h"
+
using namespace android;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -53,7 +63,7 @@
" SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
}
-bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
+static bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
for (const auto& candidate : skipped) {
if (candidate == service) {
return true;
@@ -62,17 +72,7 @@
return false;
}
-int main(int argc, char* const argv[])
-{
- signal(SIGPIPE, SIG_IGN);
- sp<IServiceManager> sm = defaultServiceManager();
- fflush(stdout);
- if (sm == NULL) {
- ALOGE("Unable to get default service manager!");
- aerr << "dumpsys: Unable to get default service manager!" << endl;
- return 20;
- }
-
+int Dumpsys::main(int argc, char* const argv[]) {
Vector<String16> services;
Vector<String16> args;
Vector<String16> skippedServices;
@@ -85,6 +85,9 @@
{ 0, 0, 0, 0 }
};
+ // Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but
+ // happens on test cases).
+ optind = 1;
while (1) {
int c;
int optionIndex = 0;
@@ -147,7 +150,7 @@
if (services.empty() || showListOnly) {
// gets all services
- services = sm->listServices();
+ services = sm_->listServices();
services.sort(sort_func);
args.add(String16("-a"));
}
@@ -159,8 +162,9 @@
aout << "Currently running services:" << endl;
for (size_t i=0; i<N; i++) {
- sp<IBinder> service = sm->checkService(services[i]);
- if (service != NULL) {
+ sp<IBinder> service = sm_->checkService(services[i]);
+
+ if (service != nullptr) {
bool skipped = IsSkipped(skippedServices, services[i]);
aout << " " << services[i] << (skipped ? " (skipped)" : "") << endl;
}
@@ -175,8 +179,8 @@
String16 service_name = std::move(services[i]);
if (IsSkipped(skippedServices, service_name)) continue;
- sp<IBinder> service = sm->checkService(service_name);
- if (service != NULL) {
+ sp<IBinder> service = sm_->checkService(service_name);
+ if (service != nullptr) {
int sfd[2];
if (pipe(sfd) != 0) {
@@ -203,7 +207,7 @@
// call returns, to terminate our reads if the other end closes their copy of the
// file descriptor, but then hangs for some reason. There doesn't seem to be a good
// way to do this, though.
- remote_end.clear();
+ remote_end.reset();
if (err != 0) {
aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
@@ -262,7 +266,10 @@
}
if (timed_out) {
- aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
+ aout << endl
+ << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArg
+ << "s) EXPIRED ***" << endl
+ << endl;
}
if (timed_out || error) {
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
new file mode 100644
index 0000000..2534dde
--- /dev/null
+++ b/cmds/dumpsys/dumpsys.h
@@ -0,0 +1,35 @@
+/*
+ * 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 FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
+
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+class Dumpsys {
+ public:
+ Dumpsys(android::IServiceManager* sm) : sm_(sm) {
+ }
+ int main(int argc, char* const argv[]);
+
+ private:
+ android::IServiceManager* sm_;
+};
+}
+
+#endif // FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp
new file mode 100644
index 0000000..8ba0eba
--- /dev/null
+++ b/cmds/dumpsys/main.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Command that dumps interesting system state to the log.
+ */
+
+#include "dumpsys.h"
+
+#include <binder/IServiceManager.h>
+#include <binder/TextOutput.h>
+
+#include <signal.h>
+#include <stdio.h>
+
+using namespace android;
+
+int main(int argc, char* const argv[]) {
+ signal(SIGPIPE, SIG_IGN);
+ sp<IServiceManager> sm = defaultServiceManager();
+ fflush(stdout);
+ if (sm == nullptr) {
+ ALOGE("Unable to get default service manager!");
+ aerr << "dumpsys: Unable to get default service manager!" << endl;
+ return 20;
+ }
+
+ Dumpsys dumpsys(sm.get());
+ return dumpsys.main(argc, argv);
+}
diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp
new file mode 100644
index 0000000..7698ed5
--- /dev/null
+++ b/cmds/dumpsys/tests/Android.bp
@@ -0,0 +1,19 @@
+// Build the unit tests for dumpsys
+cc_test {
+ name: "dumpsys_test",
+
+ srcs: ["dumpsys_test.cpp"],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libdumpsys",
+ "libgmock",
+ ],
+
+ clang: true,
+}
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
new file mode 100644
index 0000000..66beb6d
--- /dev/null
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -0,0 +1,300 @@
+/*
+ * 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 "../dumpsys.h"
+
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+using namespace android;
+
+using ::testing::_;
+using ::testing::Action;
+using ::testing::ActionInterface;
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::MakeAction;
+using ::testing::Not;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::WithArg;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class ServiceManagerMock : public IServiceManager {
+ public:
+ MOCK_CONST_METHOD1(getService, sp<IBinder>(const String16&));
+ MOCK_CONST_METHOD1(checkService, sp<IBinder>(const String16&));
+ MOCK_METHOD3(addService, status_t(const String16&, const sp<IBinder>&, bool));
+ MOCK_METHOD0(listServices, Vector<String16>());
+
+ protected:
+ MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
+class BinderMock : public BBinder {
+ public:
+ BinderMock() {
+ }
+
+ MOCK_METHOD2(dump, status_t(int, const Vector<String16>&));
+};
+
+// gmock black magic to provide a WithArg<0>(WriteOnFd(output)) matcher
+typedef void WriteOnFdFunction(int);
+
+class WriteOnFdAction : public ActionInterface<WriteOnFdFunction> {
+ public:
+ explicit WriteOnFdAction(const std::string& output) : output_(output) {
+ }
+ virtual Result Perform(const ArgumentTuple& args) {
+ int fd = ::std::tr1::get<0>(args);
+ android::base::WriteStringToFd(output_, fd);
+ }
+
+ private:
+ std::string output_;
+};
+
+// Matcher used to emulate dump() by writing on its file descriptor.
+Action<WriteOnFdFunction> WriteOnFd(const std::string& output) {
+ return MakeAction(new WriteOnFdAction(output));
+}
+
+// Matcher for args using Android's Vector<String16> format
+// TODO: move it to some common testing library
+MATCHER_P(AndroidElementsAre, expected, "") {
+ std::ostringstream errors;
+ if (arg.size() != expected.size()) {
+ errors << " sizes do not match (expected " << expected.size() << ", got " << arg.size()
+ << ")\n";
+ }
+ int i = 0;
+ std::ostringstream actual_stream, expected_stream;
+ for (String16 actual : arg) {
+ std::string actual_str = String8(actual).c_str();
+ std::string expected_str = expected[i];
+ actual_stream << "'" << actual_str << "' ";
+ expected_stream << "'" << expected_str << "' ";
+ if (actual_str != expected_str) {
+ errors << " element mismatch at index " << i << "\n";
+ }
+ i++;
+ }
+
+ if (!errors.str().empty()) {
+ errors << "\nExpected args: " << expected_stream.str()
+ << "\nActual args: " << actual_stream.str();
+ *result_listener << errors.str();
+ return false;
+ }
+ return true;
+}
+
+// Custom action to sleep for timeout seconds
+ACTION_P(Sleep, timeout) {
+ sleep(timeout);
+}
+
+class DumpsysTest : public Test {
+ public:
+ DumpsysTest() : sm_(), dump_(&sm_), stdout_(), stderr_() {
+ }
+
+ void ExpectListServices(std::vector<std::string> services) {
+ Vector<String16> services16;
+ for (auto& service : services) {
+ services16.add(String16(service.c_str()));
+ }
+ EXPECT_CALL(sm_, listServices()).WillRepeatedly(Return(services16));
+ }
+
+ sp<BinderMock> ExpectCheckService(const char* name, bool running = true) {
+ sp<BinderMock> binder_mock;
+ if (running) {
+ binder_mock = new BinderMock;
+ }
+ EXPECT_CALL(sm_, checkService(String16(name))).WillRepeatedly(Return(binder_mock));
+ return binder_mock;
+ }
+
+ void ExpectDump(const char* name, const std::string& output) {
+ sp<BinderMock> binder_mock = ExpectCheckService(name);
+ EXPECT_CALL(*binder_mock, dump(_, _))
+ .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
+ }
+
+ void ExpectDumpWithArgs(const char* name, std::vector<std::string> args,
+ const std::string& output) {
+ sp<BinderMock> binder_mock = ExpectCheckService(name);
+ EXPECT_CALL(*binder_mock, dump(_, AndroidElementsAre(args)))
+ .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
+ }
+
+ void ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) {
+ sp<BinderMock> binder_mock = ExpectCheckService(name);
+ EXPECT_CALL(*binder_mock, dump(_, _))
+ .WillRepeatedly(DoAll(Sleep(timeout_s), WithArg<0>(WriteOnFd(output)), Return(0)));
+ }
+
+ void CallMain(const std::vector<std::string>& args) {
+ const char* argv[1024] = {"/some/virtual/dir/dumpsys"};
+ int argc = (int)args.size() + 1;
+ int i = 1;
+ for (const std::string& arg : args) {
+ argv[i++] = arg.c_str();
+ }
+ CaptureStdout();
+ CaptureStderr();
+ int status = dump_.main(argc, const_cast<char**>(argv));
+ stdout_ = GetCapturedStdout();
+ stderr_ = GetCapturedStderr();
+ EXPECT_THAT(status, Eq(0));
+ }
+
+ void AssertRunningServices(const std::vector<std::string>& services) {
+ std::string expected("Currently running services:\n");
+ for (const std::string& service : services) {
+ expected.append(" ").append(service).append("\n");
+ }
+ EXPECT_THAT(stdout_, HasSubstr(expected));
+ }
+
+ void AssertOutput(const std::string& expected) {
+ EXPECT_THAT(stdout_, StrEq(expected));
+ }
+
+ void AssertOutputContains(const std::string& expected) {
+ EXPECT_THAT(stdout_, HasSubstr(expected));
+ }
+
+ void AssertDumped(const std::string& service, const std::string& dump) {
+ EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
+ }
+
+ void AssertNotDumped(const std::string& dump) {
+ EXPECT_THAT(stdout_, Not(HasSubstr(dump)));
+ }
+
+ void AssertStopped(const std::string& service) {
+ EXPECT_THAT(stderr_, HasSubstr("Can't find service: " + service + "\n"));
+ }
+
+ ServiceManagerMock sm_;
+ Dumpsys dump_;
+
+ private:
+ std::string stdout_, stderr_;
+};
+
+// Tests 'dumpsys -l' when all services are running
+TEST_F(DumpsysTest, ListAllServices) {
+ ExpectListServices({"Locksmith", "Valet"});
+ ExpectCheckService("Locksmith");
+ ExpectCheckService("Valet");
+
+ CallMain({"-l"});
+
+ AssertRunningServices({"Locksmith", "Valet"});
+}
+
+// Tests 'dumpsys -l' when a service is not running
+TEST_F(DumpsysTest, ListRunningServices) {
+ ExpectListServices({"Locksmith", "Valet"});
+ ExpectCheckService("Locksmith");
+ ExpectCheckService("Valet", false);
+
+ CallMain({"-l"});
+
+ AssertRunningServices({"Locksmith"});
+ AssertNotDumped({"Valet"});
+}
+
+// Tests 'dumpsys service_name' on a service is running
+TEST_F(DumpsysTest, DumpRunningService) {
+ ExpectDump("Valet", "Here's your car");
+
+ CallMain({"Valet"});
+
+ AssertOutput("Here's your car");
+}
+
+// Tests 'dumpsys -t 1 service_name' on a service that times out after 2s
+TEST_F(DumpsysTest, DumpRunningServiceTimeout) {
+ ExpectDumpAndHang("Valet", 2, "Here's your car");
+
+ CallMain({"-t", "1", "Valet"});
+
+ AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1s) EXPIRED");
+ AssertNotDumped("Here's your car");
+
+ // Must wait so binder mock is deleted, otherwise test will fail with a leaked object
+ sleep(1);
+}
+
+// Tests 'dumpsys service_name Y U NO HAVE ARGS' on a service that is running
+TEST_F(DumpsysTest, DumpWithArgsRunningService) {
+ ExpectDumpWithArgs("SERVICE", {"Y", "U", "NO", "HANDLE", "ARGS"}, "I DO!");
+
+ CallMain({"SERVICE", "Y", "U", "NO", "HANDLE", "ARGS"});
+
+ AssertOutput("I DO!");
+}
+
+// Tests 'dumpsys' with no arguments
+TEST_F(DumpsysTest, DumpMultipleServices) {
+ ExpectListServices({"running1", "stopped2", "running3"});
+ ExpectDump("running1", "dump1");
+ ExpectCheckService("stopped2", false);
+ ExpectDump("running3", "dump3");
+
+ CallMain({});
+
+ AssertRunningServices({"running1", "running3"});
+ AssertDumped("running1", "dump1");
+ AssertStopped("stopped2");
+ AssertDumped("running3", "dump3");
+}
+
+// Tests 'dumpsys --skip skipped3 skipped5', which should skip these services
+TEST_F(DumpsysTest, DumpWithSkip) {
+ ExpectListServices({"running1", "stopped2", "skipped3", "running4", "skipped5"});
+ ExpectDump("running1", "dump1");
+ ExpectCheckService("stopped2", false);
+ ExpectDump("skipped3", "dump3");
+ ExpectDump("running4", "dump4");
+ ExpectDump("skipped5", "dump5");
+
+ CallMain({"--skip", "skipped3", "skipped5"});
+
+ AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"});
+ AssertDumped("running1", "dump1");
+ AssertDumped("running4", "dump4");
+ AssertStopped("stopped2");
+ AssertNotDumped("dump3");
+ AssertNotDumped("dump5");
+}
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index ddf3aa8..dfc3e58 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -25,7 +25,6 @@
namespace android {
GLHelper::GLHelper() :
- mGraphicBufferAlloc(new GraphicBufferAlloc()),
mDisplay(EGL_NO_DISPLAY),
mContext(EGL_NO_CONTEXT),
mDummySurface(EGL_NO_SURFACE),
@@ -203,7 +202,7 @@
sp<GLConsumer>* glConsumer, EGLSurface* surface) {
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer, mGraphicBufferAlloc);
+ BufferQueue::createBufferQueue(&producer, &consumer);
sp<GLConsumer> glc = new GLConsumer(consumer, name,
GL_TEXTURE_EXTERNAL_OES, false, true);
glc->setDefaultBufferSize(w, h);
@@ -365,6 +364,7 @@
if (!result) {
fprintf(stderr, "Shader source:\n");
printShaderSource(lines);
+ delete[] src;
return false;
}
delete[] src;
diff --git a/cmds/flatland/GLHelper.h b/cmds/flatland/GLHelper.h
index 7a9e9e3..d09463a 100644
--- a/cmds/flatland/GLHelper.h
+++ b/cmds/flatland/GLHelper.h
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <gui/GraphicBufferAlloc.h>
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <gui/SurfaceControl.h>
@@ -75,8 +74,6 @@
bool setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders);
- sp<GraphicBufferAlloc> mGraphicBufferAlloc;
-
EGLDisplay mDisplay;
EGLContext mContext;
EGLSurface mDummySurface;
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
index c47b0c8..ec1e543 100644
--- a/cmds/flatland/Main.cpp
+++ b/cmds/flatland/Main.cpp
@@ -16,7 +16,6 @@
#define ATRACE_TAG ATRACE_TAG_ALWAYS
-#include <gui/GraphicBufferAlloc.h>
#include <gui/Surface.h>
#include <gui/SurfaceControl.h>
#include <gui/GLConsumer.h>
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
new file mode 100644
index 0000000..33db6db
--- /dev/null
+++ b/cmds/installd/Android.bp
@@ -0,0 +1,75 @@
+cc_defaults {
+ name: "installd_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: [
+ "CacheItem.cpp",
+ "CacheTracker.cpp",
+ "InstalldNativeService.cpp",
+ "dexopt.cpp",
+ "globals.cpp",
+ "utils.cpp",
+ "binder/android/os/IInstalld.aidl",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "liblogwrap",
+ "libselinux",
+ "libutils",
+ ],
+
+ clang: true,
+}
+
+//
+// Static library used in testing and executable
+//
+
+cc_library_static {
+ name: "libinstalld",
+ defaults: ["installd_defaults"],
+
+ export_include_dirs: ["."],
+ aidl: {
+ export_aidl_headers: true,
+ },
+}
+
+//
+// Executable
+//
+
+cc_binary {
+ name: "installd",
+ defaults: ["installd_defaults"],
+ srcs: ["installd.cpp"],
+
+ static_libs: ["libdiskusage"],
+
+ init_rc: ["installd.rc"],
+}
+
+// OTA chroot tool
+
+cc_binary {
+ name: "otapreopt_chroot",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ clang: true,
+
+ srcs: ["otapreopt_chroot.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+}
+
+subdirs = ["tests"]
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 86df596..1d21b3c 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -1,56 +1,12 @@
LOCAL_PATH := $(call my-dir)
-common_src_files := commands.cpp globals.cpp utils.cpp
-common_cflags := -Wall -Werror
-
-#
-# Static library used in testing and executable
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libinstalld
-LOCAL_MODULE_TAGS := eng tests
-LOCAL_SRC_FILES := $(common_src_files)
-LOCAL_CFLAGS := $(common_cflags)
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- liblogwrap \
- libselinux \
-
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-LOCAL_CLANG := true
-include $(BUILD_STATIC_LIBRARY)
-
-#
-# Executable
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := installd
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(common_cflags)
-LOCAL_SRC_FILES := installd.cpp $(common_src_files)
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libcutils \
- liblog \
- liblogwrap \
- libselinux \
-
-LOCAL_STATIC_LIBRARIES := libdiskusage
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-LOCAL_INIT_RC := installd.rc
-LOCAL_CLANG := true
-include $(BUILD_EXECUTABLE)
-
#
# OTA Executable
#
include $(CLEAR_VARS)
LOCAL_MODULE := otapreopt
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(common_cflags)
+LOCAL_CFLAGS := -Wall -Werror
# Base & ASLR boundaries for boot image creation.
ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
@@ -67,32 +23,17 @@
LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
-LOCAL_SRC_FILES := otapreopt.cpp $(common_src_files)
+LOCAL_SRC_FILES := otapreopt.cpp globals.cpp utils.cpp dexopt.cpp
+LOCAL_HEADER_LIBRARIES := dex2oat_headers
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
liblog \
liblogwrap \
libselinux \
+ libutils \
LOCAL_STATIC_LIBRARIES := libdiskusage
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-LOCAL_CLANG := true
-include $(BUILD_EXECUTABLE)
-
-# OTA chroot tool
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := otapreopt_chroot
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(common_cflags)
-
-LOCAL_SRC_FILES := otapreopt_chroot.cpp
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- liblog \
-
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
@@ -120,7 +61,3 @@
LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot otapreopt_slot
include $(BUILD_PREBUILT)
-
-# Tests.
-
-include $(LOCAL_PATH)/tests/Android.mk
\ No newline at end of file
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp
new file mode 100644
index 0000000..515f915
--- /dev/null
+++ b/cmds/installd/CacheItem.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 <inttypes.h>
+#include <stdint.h>
+#include <sys/xattr.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CacheItem::CacheItem(FTSENT* p) {
+ 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;
+
+ mParent = static_cast<CacheItem*>(p->fts_parent->fts_pointer);
+ if (mParent) {
+ group = mParent->group;
+ tombstone = mParent->tombstone;
+ mName = p->fts_name;
+ mName.insert(0, "/");
+ } else {
+ group = false;
+ tombstone = false;
+ 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;
+ CacheItem* parent = mParent;
+ while (parent) {
+ res.insert(0, parent->mName);
+ parent = parent->mParent;
+ }
+ return res;
+}
+
+int CacheItem::purge() {
+ int res = 0;
+ auto path = buildPath();
+ if (directory) {
+ FTS *fts;
+ FTSENT *p;
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+ PLOG(WARNING) << "Failed to fts_open " << path;
+ return -1;
+ }
+ while ((p = fts_read(fts)) != nullptr) {
+ switch (p->fts_info) {
+ case FTS_D:
+ if (p->fts_level == 0) {
+ p->fts_number = tombstone;
+ } else {
+ p->fts_number = p->fts_parent->fts_number
+ | (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0);
+ }
+ break;
+ case FTS_F:
+ if (p->fts_parent->fts_number) {
+ if (truncate(p->fts_path, 0) != 0) {
+ PLOG(WARNING) << "Failed to truncate " << p->fts_path;
+ res = -1;
+ }
+ } else {
+ if (unlink(p->fts_path) != 0) {
+ PLOG(WARNING) << "Failed to unlink " << p->fts_path;
+ res = -1;
+ }
+ }
+ break;
+ case FTS_DEFAULT:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (unlink(p->fts_path) != 0) {
+ PLOG(WARNING) << "Failed to unlink " << p->fts_path;
+ res = -1;
+ }
+ break;
+ case FTS_DP:
+ if (rmdir(p->fts_path) != 0) {
+ PLOG(WARNING) << "Failed to rmdir " << p->fts_path;
+ res = -1;
+ }
+ break;
+ }
+ }
+ } else {
+ if (tombstone) {
+ if (truncate(path.c_str(), 0) != 0) {
+ PLOG(WARNING) << "Failed to truncate " << path;
+ res = -1;
+ }
+ } else {
+ if (unlink(path.c_str()) != 0) {
+ PLOG(WARNING) << "Failed to unlink " << path;
+ res = -1;
+ }
+ }
+ }
+ return res;
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/CacheItem.h b/cmds/installd/CacheItem.h
new file mode 100644
index 0000000..84b77aa
--- /dev/null
+++ b/cmds/installd/CacheItem.h
@@ -0,0 +1,64 @@
+/*
+ * 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 deleted as a
+ * group.
+ */
+class CacheItem {
+public:
+ CacheItem(FTSENT* p);
+ ~CacheItem();
+
+ std::string toString();
+ std::string buildPath();
+
+ int purge();
+
+ short level;
+ bool directory;
+ bool group;
+ bool tombstone;
+ int64_t size;
+ time_t modified;
+
+private:
+ 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..e293948
--- /dev/null
+++ b/cmds/installd/CacheTracker.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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 <sys/xattr.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() {
+ ATRACE_BEGIN("loadStats quota");
+ cacheUsed = 0;
+ if (loadQuotaStats()) {
+ return;
+ }
+ ATRACE_END();
+
+ 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();
+}
+
+bool CacheTracker::loadQuotaStats() {
+ int cacheGid = multiuser_get_cache_gid(mUserId, mAppId);
+ int extCacheGid = multiuser_get_ext_cache_gid(mUserId, mAppId);
+ if (!mQuotaDevice.empty() && cacheGid != -1 && extCacheGid != -1) {
+ struct dqblk dq;
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), cacheGid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid;
+ }
+ return false;
+ } else {
+ cacheUsed += dq.dqb_curspace;
+ }
+
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), extCacheGid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid;
+ }
+ return false;
+ } else {
+ cacheUsed += dq.dqb_curspace;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+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_NOCHDIR | FTS_XDEV, NULL))) {
+ PLOG(WARNING) << "Failed to fts_open " << path;
+ return;
+ }
+ while ((p = fts_read(fts)) != nullptr) {
+ if (p->fts_level == 0) continue;
+
+ // Create tracking nodes for everything we encounter
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_DEFAULT:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE: {
+ auto item = std::shared_ptr<CacheItem>(new CacheItem(p));
+ p->fts_pointer = static_cast<void*>(item.get());
+ items.push_back(item);
+ }
+ }
+
+ switch (p->fts_info) {
+ case FTS_D: {
+ auto item = static_cast<CacheItem*>(p->fts_pointer);
+ item->group |= (getxattr(p->fts_path, kXattrCacheGroup, nullptr, 0) >= 0);
+ item->tombstone |= (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0);
+
+ // When group, immediately collect all files under tree
+ if (item->group) {
+ while ((p = fts_read(fts)) != nullptr) {
+ if (p->fts_info == FTS_DP && p->fts_level == item->level) break;
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_DEFAULT:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ item->size += p->fts_statp->st_blocks * 512;
+ item->modified = std::max(item->modified, p->fts_statp->st_mtime);
+ }
+ }
+ }
+ }
+ }
+
+ // Bubble up modified time to parent
+ switch (p->fts_info) {
+ case FTS_DP:
+ case FTS_DEFAULT:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE: {
+ auto item = static_cast<CacheItem*>(p->fts_pointer);
+ auto parent = static_cast<CacheItem*>(p->fts_parent->fts_pointer);
+ if (parent) {
+ parent->modified = std::max(parent->modified, item->modified);
+ }
+ }
+ }
+ }
+ 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::stable_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..44359b4
--- /dev/null
+++ b/cmds/installd/CacheTracker.h
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+ bool loadQuotaStats();
+ 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
new file mode 100644
index 0000000..60c89a9
--- /dev/null
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -0,0 +1,2357 @@
+/*
+** Copyright 2008, 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 "InstalldNativeService.h"
+
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
+#include <errno.h>
+#include <inttypes.h>
+#include <fstream>
+#include <fts.h>
+#include <regex>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/file.h>
+#include <sys/resource.h>
+#include <sys/quota.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#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"
+#include "installd_deps.h"
+#include "otapreopt_utils.h"
+#include "utils.h"
+
+#include "CacheTracker.h"
+#include "MatchExtensionGen.h"
+
+#ifndef LOG_TAG
+#define LOG_TAG "installd"
+#endif
+
+using android::base::StringPrintf;
+using std::endl;
+
+namespace android {
+namespace installd {
+
+static constexpr const char* kCpPath = "/system/bin/cp";
+static constexpr const char* kXattrDefault = "user.default";
+
+static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
+
+static constexpr const char* PKG_LIB_POSTFIX = "/lib";
+static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
+static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
+
+static constexpr const char *kIdMapPath = "/system/bin/idmap";
+static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
+static constexpr const char* IDMAP_SUFFIX = "@idmap";
+
+// NOTE: keep in sync with Installer
+static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
+static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
+static constexpr int FLAG_USE_QUOTA = 1 << 12;
+static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
+static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
+static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15;
+static constexpr int FLAG_FORCE = 1 << 16;
+
+namespace {
+
+constexpr const char* kDump = "android.permission.DUMP";
+
+static binder::Status ok() {
+ return binder::Status::ok();
+}
+
+static binder::Status exception(uint32_t code, const std::string& msg) {
+ return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+}
+
+static binder::Status error() {
+ return binder::Status::fromServiceSpecificError(errno);
+}
+
+static binder::Status error(const std::string& msg) {
+ PLOG(ERROR) << msg;
+ return binder::Status::fromServiceSpecificError(errno, String8(msg.c_str()));
+}
+
+static binder::Status error(uint32_t code, const std::string& msg) {
+ LOG(ERROR) << msg << " (" << code << ")";
+ return binder::Status::fromServiceSpecificError(code, String8(msg.c_str()));
+}
+
+binder::Status checkPermission(const char* permission) {
+ pid_t pid;
+ uid_t uid;
+
+ if (checkCallingPermission(String16(permission), reinterpret_cast<int32_t*>(&pid),
+ reinterpret_cast<int32_t*>(&uid))) {
+ return ok();
+ } else {
+ return exception(binder::Status::EX_SECURITY,
+ StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission));
+ }
+}
+
+binder::Status checkUid(uid_t expectedUid) {
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (uid == expectedUid || uid == AID_ROOT) {
+ return ok();
+ } else {
+ return exception(binder::Status::EX_SECURITY,
+ StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
+ }
+}
+
+binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) {
+ if (!uuid || is_valid_filename(*uuid)) {
+ return ok();
+ } else {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ StringPrintf("UUID %s is malformed", uuid->c_str()));
+ }
+}
+
+binder::Status checkArgumentPackageName(const std::string& packageName) {
+ if (is_valid_package_name(packageName.c_str())) {
+ return ok();
+ } else {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ StringPrintf("Package name %s is malformed", packageName.c_str()));
+ }
+}
+
+#define ENFORCE_UID(uid) { \
+ binder::Status status = checkUid((uid)); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+}
+
+#define CHECK_ARGUMENT_UUID(uuid) { \
+ binder::Status status = checkArgumentUuid((uuid)); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+}
+
+#define CHECK_ARGUMENT_PACKAGE_NAME(packageName) { \
+ binder::Status status = \
+ checkArgumentPackageName((packageName)); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+}
+
+} // namespace
+
+status_t InstalldNativeService::start() {
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ status_t ret = BinderService<InstalldNativeService>::publish();
+ if (ret != android::OK) {
+ return ret;
+ }
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ return android::OK;
+}
+
+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()) {
+ out << dump_permission.toString8() << endl;
+ return PERMISSION_DENIED;
+ }
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ out << "installd is happy!" << endl;
+
+ {
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+ out << endl << "Storage mounts:" << endl;
+ for (const auto& n : mStorageMounts) {
+ out << " " << n.first << " = " << n.second << endl;
+ }
+
+ out << endl << "Quota reverse mounts:" << endl;
+ for (const auto& n : mQuotaReverseMounts) {
+ out << " " << n.first << " = " << n.second << endl;
+ }
+ }
+
+ {
+ std::lock_guard<std::recursive_mutex> lock(mQuotasLock);
+ 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;
+}
+
+/**
+ * Perform restorecon of the given path, but only perform recursive restorecon
+ * if the label of that top-level file actually changed. This can save us
+ * significant time by avoiding no-op traversals of large filesystem trees.
+ */
+static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid,
+ bool existing) {
+ int res = 0;
+ char* before = nullptr;
+ char* after = nullptr;
+
+ // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
+ // libselinux. Not needed here.
+
+ if (lgetfilecon(path.c_str(), &before) < 0) {
+ PLOG(ERROR) << "Failed before getfilecon for " << path;
+ goto fail;
+ }
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) {
+ PLOG(ERROR) << "Failed top-level restorecon for " << path;
+ goto fail;
+ }
+ if (lgetfilecon(path.c_str(), &after) < 0) {
+ PLOG(ERROR) << "Failed after getfilecon for " << path;
+ goto fail;
+ }
+
+ // If the initial top-level restorecon above changed the label, then go
+ // back and restorecon everything recursively
+ if (strcmp(before, after)) {
+ if (existing) {
+ LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
+ << path << "; running recursive restorecon";
+ }
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
+ SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
+ PLOG(ERROR) << "Failed recursive restorecon for " << path;
+ goto fail;
+ }
+ }
+
+ goto done;
+fail:
+ res = -1;
+done:
+ free(before);
+ free(after);
+ return res;
+}
+
+static int restorecon_app_data_lazy(const std::string& parent, const char* name,
+ const std::string& seInfo, uid_t uid, bool existing) {
+ return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid,
+ existing);
+}
+
+static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
+ if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << path;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Ensure that we have a hard-limit quota to protect against abusive apps;
+ * they should never use more than 90% of blocks or 50% of inodes.
+ */
+static int prepare_app_quota(const std::unique_ptr<std::string>& uuid, const std::string& device,
+ uid_t uid) {
+ if (device.empty()) return 0;
+
+ struct dqblk dq;
+ if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ PLOG(WARNING) << "Failed to find quota for " << uid;
+ return -1;
+ }
+
+ if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) {
+ auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
+ struct statvfs stat;
+ if (statvfs(path.c_str(), &stat) != 0) {
+ PLOG(WARNING) << "Failed to statvfs " << path;
+ return -1;
+ }
+
+ dq.dqb_valid = QIF_LIMITS;
+ dq.dqb_bhardlimit = (((stat.f_blocks * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE;
+ dq.dqb_ihardlimit = (stat.f_files / 2);
+ if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ PLOG(WARNING) << "Failed to set hard quota for " << uid;
+ return -1;
+ } else {
+ LOG(DEBUG) << "Applied hard quotas for " << uid;
+ return 0;
+ }
+ } else {
+ // Hard quota already set; assume it's reasonable
+ return 0;
+ }
+}
+
+binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+ const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
+ 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();
+
+ // Assume invalid inode unless filled in below
+ if (_aidl_return != nullptr) *_aidl_return = -1;
+
+ 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);
+
+ if (prepare_app_dir(path, targetMode, uid) ||
+ prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+ prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+ return error("Failed to prepare " + path);
+ }
+
+ // Consider restorecon over contents if label changed
+ if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
+ restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
+ restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+ return error("Failed to restorecon " + path);
+ }
+
+ // Remember inode numbers of cache directories so that we can clear
+ // contents while CE storage is locked
+ if (write_path_inode(path, "cache", kXattrInodeCache) ||
+ write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
+ return error("Failed to write_path_inode for " + path);
+ }
+
+ // And return the CE inode of the top-level data directory so we can
+ // clear contents while CE storage is locked
+ if ((_aidl_return != nullptr)
+ && get_path_inode(path, reinterpret_cast<ino_t*>(_aidl_return)) != 0) {
+ return error("Failed to get_path_inode for " + path);
+ }
+ }
+ if (flags & FLAG_STORAGE_DE) {
+ auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
+ bool existing = (access(path.c_str(), F_OK) == 0);
+
+ if (prepare_app_dir(path, targetMode, uid) ||
+ prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+ prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+ return error("Failed to prepare " + path);
+ }
+
+ // Consider restorecon over contents if label changed
+ if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
+ restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
+ restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+ return error("Failed to restorecon " + path);
+ }
+
+ if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) {
+ return error("Failed to set hard quota " + path);
+ }
+
+ if (property_get_bool("dalvik.vm.usejitprofiles", false)) {
+ const std::string profile_dir =
+ create_primary_current_profile_package_dir_path(userId, pkgname);
+ // read-write-execute only for the app user.
+ if (fs_prepare_dir_strict(profile_dir.c_str(), 0700, uid, uid) != 0) {
+ return error("Failed to prepare " + profile_dir);
+ }
+ const std::string profile_file = create_current_profile_path(userId, pkgname,
+ /*is_secondary_dex*/false);
+ // read-write only for the app user.
+ if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
+ return error("Failed to prepare " + profile_file);
+ }
+ const std::string ref_profile_path =
+ create_primary_reference_profile_package_dir_path(pkgname);
+ // dex2oat/profman runs under the shared app gid and it needs to read/write reference
+ // profiles.
+ int shared_app_gid = multiuser_get_shared_gid(0, appId);
+ if ((shared_app_gid != -1) && fs_prepare_dir_strict(
+ ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
+ return error("Failed to prepare " + ref_profile_path);
+ }
+ }
+ }
+ return ok();
+}
+
+binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags) {
+ 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();
+
+ // This method only exists to upgrade system apps that have requested
+ // forceDeviceEncrypted, so their default storage always lives in a
+ // consistent location. This only works on non-FBE devices, since we
+ // never want to risk exposing data on a device with real CE/DE storage.
+
+ auto ce_path = create_data_user_ce_package_path(uuid_, userId, pkgname);
+ auto de_path = create_data_user_de_package_path(uuid_, userId, pkgname);
+
+ // If neither directory is marked as default, assume CE is default
+ if (getxattr(ce_path.c_str(), kXattrDefault, nullptr, 0) == -1
+ && getxattr(de_path.c_str(), kXattrDefault, nullptr, 0) == -1) {
+ if (setxattr(ce_path.c_str(), kXattrDefault, nullptr, 0, 0) != 0) {
+ return error("Failed to mark default storage " + ce_path);
+ }
+ }
+
+ // Migrate default data location if needed
+ auto target = (flags & FLAG_STORAGE_DE) ? de_path : ce_path;
+ auto source = (flags & FLAG_STORAGE_DE) ? ce_path : de_path;
+
+ if (getxattr(target.c_str(), kXattrDefault, nullptr, 0) == -1) {
+ LOG(WARNING) << "Requested default storage " << target
+ << " is not active; migrating from " << source;
+ if (delete_dir_contents_and_dir(target) != 0) {
+ return error("Failed to delete " + target);
+ }
+ if (rename(source.c_str(), target.c_str()) != 0) {
+ return error("Failed to rename " + source + " to " + target);
+ }
+ }
+
+ return ok();
+}
+
+
+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);
+
+ binder::Status res = ok();
+ if (!clear_primary_reference_profile(packageName)) {
+ res = error("Failed to clear reference profile for " + packageName);
+ }
+ if (!clear_primary_current_profiles(packageName)) {
+ res = error("Failed to clear current profiles for " + packageName);
+ }
+ return res;
+}
+
+binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
+ 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();
+
+ binder::Status res = ok();
+ if (flags & FLAG_STORAGE_CE) {
+ auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
+ if (flags & FLAG_CLEAR_CACHE_ONLY) {
+ path = read_path_inode(path, "cache", kXattrInodeCache);
+ } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+ path = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
+ }
+ if (access(path.c_str(), F_OK) == 0) {
+ if (delete_dir_contents(path) != 0) {
+ res = error("Failed to delete contents of " + path);
+ }
+ }
+ }
+ if (flags & FLAG_STORAGE_DE) {
+ std::string suffix = "";
+ bool only_cache = false;
+ if (flags & FLAG_CLEAR_CACHE_ONLY) {
+ suffix = CACHE_DIR_POSTFIX;
+ only_cache = true;
+ } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+ suffix = CODE_CACHE_DIR_POSTFIX;
+ only_cache = true;
+ }
+
+ auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix;
+ if (access(path.c_str(), F_OK) == 0) {
+ if (delete_dir_contents(path) != 0) {
+ res = error("Failed to delete contents of " + path);
+ }
+ }
+ if (!only_cache) {
+ if (!clear_primary_current_profile(packageName, userId)) {
+ res = error("Failed to clear current profile for " + packageName);
+ }
+ }
+ }
+ return res;
+}
+
+static int destroy_app_reference_profile(const std::string& pkgname) {
+ return delete_dir_contents_and_dir(
+ create_primary_reference_profile_package_dir_path(pkgname),
+ /*ignore_if_missing*/ true);
+}
+
+static int destroy_app_current_profiles(const std::string& pkgname, userid_t userid) {
+ return delete_dir_contents_and_dir(
+ create_primary_current_profile_package_dir_path(userid, pkgname),
+ /*ignore_if_missing*/ true);
+}
+
+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);
+
+ binder::Status res = ok();
+ std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+ for (auto user : users) {
+ if (destroy_app_current_profiles(packageName, user) != 0) {
+ res = error("Failed to destroy current profiles for " + packageName);
+ }
+ }
+ if (destroy_app_reference_profile(packageName) != 0) {
+ res = error("Failed to destroy reference profile for " + packageName);
+ }
+ return res;
+}
+
+binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
+ 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();
+
+ binder::Status res = ok();
+ if (flags & FLAG_STORAGE_CE) {
+ auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
+ if (delete_dir_contents_and_dir(path) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ }
+ if (flags & FLAG_STORAGE_DE) {
+ auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
+ if (delete_dir_contents_and_dir(path) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ destroy_app_current_profiles(packageName, userId);
+ // TODO(calin): If the package is still installed by other users it's probably
+ // beneficial to keep the reference profile around.
+ // Verify if it's ok to do that.
+ destroy_app_reference_profile(packageName);
+ }
+ return res;
+}
+
+static gid_t get_cache_gid(uid_t uid) {
+ int32_t gid = multiuser_get_cache_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));
+ return (gid != -1) ? gid : uid;
+}
+
+binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::string>& uuid,
+ 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;
+ for (auto user : get_known_users(uuid_)) {
+ ATRACE_BEGIN("fixup user");
+ FTS* fts;
+ FTSENT* p;
+ auto ce_path = create_data_user_ce_path(uuid_, user);
+ auto de_path = create_data_user_de_path(uuid_, user);
+ char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+ return error("Failed to fts_open");
+ }
+ while ((p = fts_read(fts)) != nullptr) {
+ if (p->fts_info == FTS_D && p->fts_level == 1) {
+ // Track down inodes of cache directories
+ uint64_t raw = 0;
+ ino_t inode_cache = 0;
+ ino_t inode_code_cache = 0;
+ if (getxattr(p->fts_path, kXattrInodeCache, &raw, sizeof(raw)) == sizeof(raw)) {
+ inode_cache = raw;
+ }
+ if (getxattr(p->fts_path, kXattrInodeCodeCache, &raw, sizeof(raw)) == sizeof(raw)) {
+ inode_code_cache = raw;
+ }
+
+ // Figure out expected GID of each child
+ FTSENT* child = fts_children(fts, 0);
+ while (child != nullptr) {
+ if ((child->fts_statp->st_ino == inode_cache)
+ || (child->fts_statp->st_ino == inode_code_cache)
+ || !strcmp(child->fts_name, "cache")
+ || !strcmp(child->fts_name, "code_cache")) {
+ child->fts_number = get_cache_gid(p->fts_statp->st_uid);
+ } else {
+ child->fts_number = p->fts_statp->st_uid;
+ }
+ child = child->fts_link;
+ }
+ } else if (p->fts_level >= 2) {
+ if (p->fts_level > 2) {
+ // Inherit GID from parent once we're deeper into tree
+ p->fts_number = p->fts_parent->fts_number;
+ }
+
+ uid_t uid = p->fts_parent->fts_statp->st_uid;
+ gid_t cache_gid = get_cache_gid(uid);
+ gid_t expected = p->fts_number;
+ gid_t actual = p->fts_statp->st_gid;
+ if (actual == expected) {
+#if FIXUP_DEBUG
+ LOG(DEBUG) << "Ignoring " << p->fts_path << " with expected GID " << expected;
+#endif
+ if (!(flags & FLAG_FORCE)) {
+ fts_set(fts, p, FTS_SKIP);
+ }
+ } else if ((actual == uid) || (actual == cache_gid)) {
+ // Only consider fixing up when current GID belongs to app
+ if (p->fts_info != FTS_D) {
+ LOG(INFO) << "Fixing " << p->fts_path << " with unexpected GID " << actual
+ << " instead of " << expected;
+ }
+ switch (p->fts_info) {
+ case FTS_DP:
+ // If we're moving towards cache GID, we need to set S_ISGID
+ if (expected == cache_gid) {
+ if (chmod(p->fts_path, 02771) != 0) {
+ PLOG(WARNING) << "Failed to chmod " << p->fts_path;
+ }
+ }
+ // Intentional fall through to also set GID
+ case FTS_F:
+ if (chown(p->fts_path, -1, expected) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ break;
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_path, -1, expected) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ break;
+ }
+ } else {
+ // Ignore all other GID transitions, since they're kinda shady
+ LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected GID " << actual
+ << " instead of " << expected;
+ }
+ }
+ }
+ fts_close(fts);
+ ATRACE_END();
+ }
+ return ok();
+}
+
+binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
+ const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+ const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+ int32_t targetSdkVersion) {
+ ENFORCE_UID(AID_SYSTEM);
+ 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;
+ const char* package_name = packageName.c_str();
+ const char* data_app_name = dataAppName.c_str();
+
+ binder::Status res = ok();
+ std::vector<userid_t> users = get_known_users(from_uuid);
+
+ // Copy app
+ {
+ auto from = create_data_app_package_path(from_uuid, data_app_name);
+ auto to = create_data_app_package_path(to_uuid, data_app_name);
+ auto to_parent = create_data_app_path(to_uuid);
+
+ char *argv[] = {
+ (char*) kCpPath,
+ (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+ (char*) "-p", /* preserve timestamps, ownership, and permissions */
+ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+ (char*) "-P", /* Do not follow symlinks [default] */
+ (char*) "-d", /* don't dereference symlinks */
+ (char*) from.c_str(),
+ (char*) to_parent.c_str()
+ };
+
+ LOG(DEBUG) << "Copying " << from << " to " << to;
+ int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+ if (rc != 0) {
+ res = error(rc, "Failed copying " + from + " to " + to);
+ goto fail;
+ }
+
+ if (selinux_android_restorecon(to.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ res = error("Failed to restorecon " + to);
+ goto fail;
+ }
+ }
+
+ // Copy private data for all known users
+ for (auto user : users) {
+
+ // Data source may not exist for all users; that's okay
+ auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
+ if (access(from_ce.c_str(), F_OK) != 0) {
+ LOG(INFO) << "Missing source " << from_ce;
+ continue;
+ }
+
+ if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId,
+ seInfo, targetSdkVersion, nullptr).isOk()) {
+ res = error("Failed to create package target");
+ goto fail;
+ }
+
+ char *argv[] = {
+ (char*) kCpPath,
+ (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+ (char*) "-p", /* preserve timestamps, ownership, and permissions */
+ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+ (char*) "-P", /* Do not follow symlinks [default] */
+ (char*) "-d", /* don't dereference symlinks */
+ nullptr,
+ nullptr
+ };
+
+ {
+ auto from = create_data_user_de_package_path(from_uuid, user, package_name);
+ auto to = create_data_user_de_path(to_uuid, user);
+ argv[6] = (char*) from.c_str();
+ argv[7] = (char*) to.c_str();
+
+ LOG(DEBUG) << "Copying " << from << " to " << to;
+ int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+ if (rc != 0) {
+ res = error(rc, "Failed copying " + from + " to " + to);
+ goto fail;
+ }
+ }
+ {
+ auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
+ auto to = create_data_user_ce_path(to_uuid, user);
+ argv[6] = (char*) from.c_str();
+ argv[7] = (char*) to.c_str();
+
+ LOG(DEBUG) << "Copying " << from << " to " << to;
+ int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+ if (rc != 0) {
+ res = error(rc, "Failed copying " + from + " to " + to);
+ goto fail;
+ }
+ }
+
+ if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+ appId, seInfo).isOk()) {
+ res = error("Failed to restorecon");
+ goto fail;
+ }
+ }
+
+ // We let the framework scan the new location and persist that before
+ // deleting the data in the old location; this ordering ensures that
+ // we can recover from things like battery pulls.
+ return ok();
+
+fail:
+ // Nuke everything we might have already copied
+ {
+ auto to = create_data_app_package_path(to_uuid, data_app_name);
+ if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+ LOG(WARNING) << "Failed to rollback " << to;
+ }
+ }
+ for (auto user : users) {
+ {
+ auto to = create_data_user_de_package_path(to_uuid, user, package_name);
+ if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+ LOG(WARNING) << "Failed to rollback " << to;
+ }
+ }
+ {
+ auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
+ if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+ LOG(WARNING) << "Failed to rollback " << to;
+ }
+ }
+ }
+ return res;
+}
+
+binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std::string>& uuid,
+ 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) {
+ if (uuid_ == nullptr) {
+ if (ensure_config_user_dirs(userId) != 0) {
+ return error(StringPrintf("Failed to ensure dirs for %d", userId));
+ }
+ }
+ }
+
+ // Data under /data/media doesn't have an app, but we still want
+ // to limit it to prevent abuse.
+ if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid),
+ multiuser_get_uid(userId, AID_MEDIA_RW))) {
+ return error("Failed to set hard quota for media_rw");
+ }
+
+ return ok();
+}
+
+binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std::string>& uuid,
+ 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();
+ if (flags & FLAG_STORAGE_DE) {
+ auto path = create_data_user_de_path(uuid_, userId);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ if (uuid_ == nullptr) {
+ path = create_data_misc_legacy_path(userId);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ path = create_primary_cur_profile_dir_path(userId);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ }
+ }
+ if (flags & FLAG_STORAGE_CE) {
+ auto path = create_data_user_ce_path(uuid_, userId);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ path = findDataMediaPath(uuid, userId);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ }
+ return res;
+}
+
+binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
+ int64_t targetFreeBytes, int64_t cacheReservedBytes, 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;
+ auto data_path = create_data_path(uuid_);
+ auto device = findQuotaDeviceForUuid(uuid);
+ auto noop = (flags & FLAG_FREE_CACHE_NOOP);
+
+ int64_t free = data_disk_free(data_path);
+ if (free < 0) {
+ return error("Failed to determine free space for " + data_path);
+ }
+
+ int64_t cleared = 0;
+ int64_t needed = targetFreeBytes - free;
+ LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested "
+ << targetFreeBytes << "; needed " << needed;
+
+ if (free >= targetFreeBytes) {
+ return ok();
+ }
+
+ 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;
+ auto ce_path = create_data_user_ce_path(uuid_, user);
+ auto de_path = create_data_user_de_path(uuid_, user);
+ auto media_path = findDataMediaPath(uuid, user) + "/Android/data/";
+ char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(),
+ (char*) media_path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | 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;
+ if (multiuser_get_app_id(uid) == AID_MEDIA_RW) {
+ uid = (multiuser_get_app_id(p->fts_statp->st_gid) - AID_EXT_GID_START)
+ + AID_APP_START;
+ }
+ 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);
+ {
+ std::lock_guard<std::recursive_mutex> lock(mQuotasLock);
+ tracker->cacheQuota = mCacheQuotas[uid];
+ }
+ if (tracker->cacheQuota == 0) {
+#if MEASURE_DEBUG
+ LOG(WARNING) << "UID " << uid << " has no cache quota; assuming 64MB";
+#endif
+ 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");
+ int64_t cacheTotal = 0;
+ 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);
+ cacheTotal += it.second->cacheUsed;
+ }
+ 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()) {
+ // Only look at apps under quota when explicitly requested
+ if (active && (active->getCacheRatio() < 10000)
+ && !(flags & FLAG_FREE_CACHE_V2_DEFY_QUOTA)) {
+ LOG(DEBUG) << "Active ratio " << active->getCacheRatio()
+ << " isn't over quota, and defy not requested";
+ break;
+ }
+
+ // Only keep clearing when we haven't pushed into reserved area
+ if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) {
+ LOG(DEBUG) << "Refusing to clear cached data in reserved space";
+ break;
+ }
+
+ // 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;
+ cleared += 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 = targetFreeBytes - free;
+ if (needed <= 0) {
+ break;
+ } else {
+ LOG(WARNING) << "Expected to be done but still need " << needed;
+ }
+ }
+ }
+ ATRACE_END();
+
+ } else {
+ return error("Legacy cache logic no longer supported");
+ }
+
+ free = data_disk_free(data_path);
+ if (free >= targetFreeBytes) {
+ return ok();
+ } else {
+ return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64,
+ targetFreeBytes, 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();
+ const char* instruction_set = instructionSet.c_str();
+
+ if (validate_apk_path(path) && validate_system_app_path(path)) {
+ return error("Invalid path " + codePath);
+ }
+
+ if (!create_cache_path(dex_path, path, instruction_set)) {
+ return error("Failed to create cache path for " + codePath);
+ }
+
+ ALOGV("unlink %s\n", dex_path);
+ if (unlink(dex_path) < 0) {
+ // It's ok if we don't have a dalvik cache path. Report error only when the path exists
+ // but could not be unlinked.
+ if (errno != ENOENT) {
+ return error(StringPrintf("Failed to unlink %s", dex_path));
+ }
+ }
+ return ok();
+}
+
+struct stats {
+ int64_t codeSize;
+ int64_t dataSize;
+ int64_t cacheSize;
+};
+
+#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
+
+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;
+
+ if (stats != nullptr) {
+ uid_t uid = multiuser_get_uid(userId, appId);
+ 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
+ stats->dataSize += dq.dqb_curspace;
+ }
+
+ int cacheGid = multiuser_get_cache_gid(userId, appId);
+ if (cacheGid != -1) {
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), cacheGid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ 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_gid(0, appId);
+ if (sharedGid != -1) {
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), sharedGid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ 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;
+ }
+ }
+ }
+
+ if (extStats != nullptr) {
+ 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 extCacheGid = multiuser_get_ext_cache_gid(userId, appId);
+ if (extCacheGid != -1) {
+ if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), extCacheGid,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << extCacheGid;
+ }
+ } else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for GID " << extCacheGid << " " << dq.dqb_curspace;
+#endif
+ extStats->dataSize += dq.dqb_curspace;
+ extStats->cacheSize += dq.dqb_curspace;
+ }
+ }
+ }
+}
+
+static void collectManualStats(const std::string& path, struct stats* stats) {
+ 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))) {
+ const char *name = de->d_name;
+
+ int64_t size = 0;
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ size = s.st_blocks * 512;
+ }
+
+ if (de->d_type == DT_DIR) {
+ if (!strcmp(name, ".")) {
+ // Don't recurse, but still count node size
+ } else if (!strcmp(name, "..")) {
+ // Don't recurse or count node size
+ continue;
+ } else {
+ // Measure all children nodes
+ size = 0;
+ calculate_tree_size(StringPrintf("%s/%s", path.c_str(), name), &size);
+ }
+
+ if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
+ stats->cacheSize += size;
+ }
+ }
+
+ // Legacy symlink isn't owned by app
+ if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
+ continue;
+ }
+
+ // Everything found inside is considered data
+ stats->dataSize += size;
+ }
+ 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;
+ }
+ int32_t user_uid = multiuser_get_app_id(s.st_uid);
+ if (!strcmp(name, ".") || !strcmp(name, "..")) {
+ continue;
+ } else if (exclude_apps && (user_uid >= AID_APP_START && user_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_NOCHDIR | FTS_XDEV, NULL))) {
+ PLOG(ERROR) << "Failed to fts_open " << path;
+ return;
+ }
+ while ((p = fts_read(fts)) != NULL) {
+ p->fts_number = p->fts_parent->fts_number;
+ 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;
+ }
+ // 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::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);
+ for (auto packageName : packageNames) {
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ }
+ // NOTE: Locking is relaxed on this method, since it's limited to
+ // read-only measurements without mutation.
+
+ // 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/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));
+
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ auto device = findQuotaDeviceForUuid(uuid);
+ if (device.empty()) {
+ flags &= ~FLAG_USE_QUOTA;
+ }
+
+ 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 (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(0, appId));
+ }
+ ATRACE_END();
+
+ ATRACE_BEGIN("quota");
+ collectQuotaStats(device, userId, appId, &stats, &extStats);
+ ATRACE_END();
+ } else {
+ ATRACE_BEGIN("code");
+ for (auto codePath : codePaths) {
+ calculate_tree_size(codePath, &stats.codeSize);
+ }
+ ATRACE_END();
+
+ for (size_t i = 0; i < packageNames.size(); i++) {
+ const char* pkgname = packageNames[i].c_str();
+
+ 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();
+
+ if (!uuid) {
+ ATRACE_BEGIN("profiles");
+ calculate_tree_size(
+ create_primary_current_profile_package_dir_path(userId, pkgname),
+ &stats.dataSize);
+ calculate_tree_size(
+ create_primary_reference_profile_package_dir_path(pkgname),
+ &stats.codeSize);
+ ATRACE_END();
+ }
+
+ ATRACE_BEGIN("external");
+ auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname);
+ collectManualStats(extPath, &extStats);
+ auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname);
+ calculate_tree_size(mediaPath, &extStats.dataSize);
+ ATRACE_END();
+ }
+
+ if (!uuid) {
+ ATRACE_BEGIN("dalvik");
+ int32_t sharedGid = multiuser_get_shared_gid(0, appId);
+ if (sharedGid != -1) {
+ calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+ sharedGid, -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);
+ // NOTE: Locking is relaxed on this method, since it's limited to
+ // read-only measurements without mutation.
+
+ // 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();
+
+ if (!uuid) {
+ ATRACE_BEGIN("profile");
+ auto userProfilePath = create_primary_cur_profile_dir_path(userId);
+ calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true);
+ auto refProfilePath = create_primary_ref_profile_dir_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();
+
+ if (!uuid) {
+ ATRACE_BEGIN("dalvik");
+ calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+ -1, -1, true);
+ calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize,
+ -1, -1, true);
+ ATRACE_END();
+ }
+
+ ATRACE_BEGIN("quota");
+ int64_t dataSize = extStats.dataSize;
+ 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
+ }
+ }
+ extStats.dataSize = dataSize;
+ 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();
+
+ if (!uuid) {
+ ATRACE_BEGIN("profile");
+ auto userProfilePath = create_primary_cur_profile_dir_path(userId);
+ calculate_tree_size(userProfilePath, &stats.dataSize);
+ auto refProfilePath = create_primary_ref_profile_dir_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();
+
+ if (!uuid) {
+ ATRACE_BEGIN("dalvik");
+ calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize);
+ calculate_tree_size(create_primary_cur_profile_dir_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, const std::vector<int32_t>& appIds,
+ std::vector<int64_t>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ // NOTE: Locking is relaxed on this method, since it's limited to
+ // read-only measurements without mutation.
+
+ // 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;
+ int64_t appSize = 0;
+
+ auto device = findQuotaDeviceForUuid(uuid);
+ if (device.empty()) {
+ flags &= ~FLAG_USE_QUOTA;
+ }
+
+ if (flags & FLAG_USE_QUOTA) {
+ struct dqblk dq;
+
+ ATRACE_BEGIN("quota");
+ 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;
+ }
+ ATRACE_END();
+
+ ATRACE_BEGIN("apps");
+ struct stats extStats;
+ memset(&extStats, 0, sizeof(extStats));
+ for (auto appId : appIds) {
+ if (appId >= AID_APP_START) {
+ collectQuotaStats(device, userId, appId, nullptr, &extStats);
+ }
+ }
+ appSize = extStats.dataSize;
+ ATRACE_END();
+ } else {
+ ATRACE_BEGIN("manual");
+ 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_NOCHDIR | 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_parent->fts_number == 0) {
+ 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:
+ // Ignore data belonging to specific apps
+ p->fts_number = p->fts_parent->fts_number;
+ if (p->fts_level == 1 && !strcmp(p->fts_name, "Android")) {
+ p->fts_number = 1;
+ }
+ case FTS_DEFAULT:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (p->fts_parent->fts_number == 1) {
+ appSize += size;
+ }
+ totalSize += size;
+ break;
+ }
+ }
+ fts_close(fts);
+ ATRACE_END();
+ }
+
+ std::vector<int64_t> ret;
+ ret.push_back(totalSize);
+ ret.push_back(audioSize);
+ ret.push_back(videoSize);
+ ret.push_back(imageSize);
+ ret.push_back(appSize);
+#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(mQuotasLock);
+
+ 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();
+
+ *_aidl_return = dump_profiles(uid, pkgname, code_paths);
+ return ok();
+}
+
+// TODO: Consider returning error codes.
+binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName,
+ bool* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ *_aidl_return = analyze_primary_profiles(uid, packageName);
+ return ok();
+}
+
+binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
+ const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
+ int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
+ const std::unique_ptr<std::string>& sharedLibraries,
+ const std::unique_ptr<std::string>& seInfo) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ 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() : "*";
+ const char* instruction_set = instructionSet.c_str();
+ const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
+ const char* compiler_filter = compilerFilter.c_str();
+ const char* volume_uuid = uuid ? uuid->c_str() : nullptr;
+ const char* shared_libraries = sharedLibraries ? sharedLibraries->c_str() : nullptr;
+ const char* se_info = seInfo ? seInfo->c_str() : nullptr;
+ int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,
+ oat_dir, dexFlags, compiler_filter, volume_uuid, shared_libraries, se_info);
+ return res ? error(res, "Failed to dexopt") : ok();
+}
+
+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];
+ sprintf(boot_marker_path,
+ "%s/%s/%s/.booting",
+ android_data_dir.path,
+ DALVIK_CACHE,
+ instruction_set);
+
+ ALOGV("mark_boot_complete : %s", boot_marker_path);
+ if (unlink(boot_marker_path) != 0) {
+ return error(StringPrintf("Failed to unlink %s", boot_marker_path));
+ }
+ return ok();
+}
+
+void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
+ struct stat* statbuf)
+{
+ while (path[basepos] != 0) {
+ if (path[basepos] == '/') {
+ path[basepos] = 0;
+ if (lstat(path, statbuf) < 0) {
+ ALOGV("Making directory: %s\n", path);
+ if (mkdir(path, mode) == 0) {
+ chown(path, uid, gid);
+ } else {
+ ALOGW("Unable to make directory %s: %s\n", path, strerror(errno));
+ }
+ }
+ path[basepos] = '/';
+ basepos++;
+ }
+ basepos++;
+ }
+}
+
+binder::Status InstalldNativeService::linkNativeLibraryDirectory(
+ const std::unique_ptr<std::string>& uuid, const std::string& packageName,
+ const std::string& nativeLibPath32, int32_t userId) {
+ 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();
+ const char* asecLibDir = nativeLibPath32.c_str();
+ struct stat s, libStat;
+ binder::Status res = ok();
+
+ auto _pkgdir = create_data_user_ce_package_path(uuid_, userId, pkgname);
+ auto _libsymlink = _pkgdir + PKG_LIB_POSTFIX;
+
+ const char* pkgdir = _pkgdir.c_str();
+ const char* libsymlink = _libsymlink.c_str();
+
+ if (stat(pkgdir, &s) < 0) {
+ return error("Failed to stat " + _pkgdir);
+ }
+
+ if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
+ return error("Failed to chown " + _pkgdir);
+ }
+
+ if (chmod(pkgdir, 0700) < 0) {
+ res = error("Failed to chmod " + _pkgdir);
+ goto out;
+ }
+
+ if (lstat(libsymlink, &libStat) < 0) {
+ if (errno != ENOENT) {
+ res = error("Failed to stat " + _libsymlink);
+ goto out;
+ }
+ } else {
+ if (S_ISDIR(libStat.st_mode)) {
+ if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
+ res = error("Failed to delete " + _libsymlink);
+ goto out;
+ }
+ } else if (S_ISLNK(libStat.st_mode)) {
+ if (unlink(libsymlink) < 0) {
+ res = error("Failed to unlink " + _libsymlink);
+ goto out;
+ }
+ }
+ }
+
+ if (symlink(asecLibDir, libsymlink) < 0) {
+ res = error("Failed to symlink " + _libsymlink + " to " + nativeLibPath32);
+ goto out;
+ }
+
+out:
+ if (chmod(pkgdir, s.st_mode) < 0) {
+ auto msg = "Failed to cleanup chmod " + _pkgdir;
+ if (res.isOk()) {
+ res = error(msg);
+ } else {
+ PLOG(ERROR) << msg;
+ }
+ }
+
+ if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
+ auto msg = "Failed to cleanup chown " + _pkgdir;
+ if (res.isOk()) {
+ res = error(msg);
+ } else {
+ PLOG(ERROR) << msg;
+ }
+ }
+
+ return res;
+}
+
+static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
+{
+ execl(kIdMapPath, kIdMapPath, "--fd", target_apk, overlay_apk,
+ StringPrintf("%d", idmap_fd).c_str(), (char*)NULL);
+ PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
+}
+
+static void run_verify_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
+{
+ execl(kIdMapPath, kIdMapPath, "--verify", target_apk, overlay_apk,
+ StringPrintf("%d", idmap_fd).c_str(), (char*)NULL);
+ PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
+}
+
+static bool delete_stale_idmap(const char* target_apk, const char* overlay_apk,
+ const char* idmap_path, int32_t uid) {
+ int idmap_fd = open(idmap_path, O_RDWR);
+ if (idmap_fd < 0) {
+ PLOG(ERROR) << "idmap open failed: " << idmap_path;
+ unlink(idmap_path);
+ return true;
+ }
+
+ pid_t pid;
+ pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ if (setgid(uid) != 0) {
+ LOG(ERROR) << "setgid(" << uid << ") failed during idmap";
+ exit(1);
+ }
+ if (setuid(uid) != 0) {
+ LOG(ERROR) << "setuid(" << uid << ") failed during idmap";
+ exit(1);
+ }
+ if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
+ PLOG(ERROR) << "flock(" << idmap_path << ") failed during idmap";
+ exit(1);
+ }
+
+ run_verify_idmap(target_apk, overlay_apk, idmap_fd);
+ exit(1); /* only if exec call to deleting stale idmap failed */
+ } else {
+ int status = wait_child(pid);
+ close(idmap_fd);
+
+ if (status != 0) {
+ // Failed on verifying if idmap is made from target_apk and overlay_apk.
+ LOG(DEBUG) << "delete stale idmap: " << idmap_path;
+ unlink(idmap_path);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
+// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
+static int flatten_path(const char *prefix, const char *suffix,
+ const char *overlay_path, char *idmap_path, size_t N)
+{
+ if (overlay_path == NULL || idmap_path == NULL) {
+ return -1;
+ }
+ const size_t len_overlay_path = strlen(overlay_path);
+ // will access overlay_path + 1 further below; requires absolute path
+ if (len_overlay_path < 2 || *overlay_path != '/') {
+ return -1;
+ }
+ const size_t len_idmap_root = strlen(prefix);
+ const size_t len_suffix = strlen(suffix);
+ if (SIZE_MAX - len_idmap_root < len_overlay_path ||
+ SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) {
+ // additions below would cause overflow
+ return -1;
+ }
+ if (N < len_idmap_root + len_overlay_path + len_suffix) {
+ return -1;
+ }
+ memset(idmap_path, 0, N);
+ snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix);
+ char *ch = idmap_path + len_idmap_root;
+ while (*ch != '\0') {
+ if (*ch == '/') {
+ *ch = '@';
+ }
+ ++ch;
+ }
+ return 0;
+}
+
+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);
+
+ int idmap_fd = -1;
+ char idmap_path[PATH_MAX];
+ struct stat idmap_stat;
+ bool outdated = false;
+
+ if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
+ idmap_path, sizeof(idmap_path)) == -1) {
+ ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
+ goto fail;
+ }
+
+ if (stat(idmap_path, &idmap_stat) < 0) {
+ outdated = true;
+ } else {
+ outdated = delete_stale_idmap(target_apk, overlay_apk, idmap_path, uid);
+ }
+
+ if (outdated) {
+ idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
+ } else {
+ idmap_fd = open(idmap_path, O_RDWR);
+ }
+
+ if (idmap_fd < 0) {
+ ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
+ goto fail;
+ }
+ if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
+ ALOGE("idmap cannot chown '%s'\n", idmap_path);
+ goto fail;
+ }
+ if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
+ ALOGE("idmap cannot chmod '%s'\n", idmap_path);
+ goto fail;
+ }
+
+ if (!outdated) {
+ close(idmap_fd);
+ return ok();
+ }
+
+ pid_t pid;
+ pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ if (setgid(uid) != 0) {
+ ALOGE("setgid(%d) failed during idmap\n", uid);
+ exit(1);
+ }
+ if (setuid(uid) != 0) {
+ ALOGE("setuid(%d) failed during idmap\n", uid);
+ exit(1);
+ }
+ if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
+ ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
+ exit(1);
+ }
+
+ run_idmap(target_apk, overlay_apk, idmap_fd);
+ exit(1); /* only if exec call to idmap failed */
+ } else {
+ int status = wait_child(pid);
+ if (status != 0) {
+ ALOGE("idmap failed, status=0x%04x\n", status);
+ goto fail;
+ }
+ }
+
+ close(idmap_fd);
+ return ok();
+fail:
+ if (idmap_fd >= 0) {
+ close(idmap_fd);
+ unlink(idmap_path);
+ }
+ return error();
+}
+
+binder::Status InstalldNativeService::removeIdmap(const std::string& overlayApkPath) {
+ const char* overlay_apk = overlayApkPath.c_str();
+ char idmap_path[PATH_MAX];
+
+ if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
+ idmap_path, sizeof(idmap_path)) == -1) {
+ ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
+ return error();
+ }
+ if (unlink(idmap_path) < 0) {
+ ALOGE("couldn't unlink idmap file %s\n", idmap_path);
+ return error();
+ }
+ return ok();
+}
+
+binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+ const std::string& seInfo) {
+ 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();
+
+ // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
+ unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgName = packageName.c_str();
+ const char* seinfo = seInfo.c_str();
+
+ uid_t uid = multiuser_get_uid(userId, appId);
+ if (flags & FLAG_STORAGE_CE) {
+ auto path = create_data_user_ce_package_path(uuid_, userId, pkgName);
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
+ res = error("restorecon failed for " + path);
+ }
+ }
+ if (flags & FLAG_STORAGE_DE) {
+ auto path = create_data_user_de_package_path(uuid_, userId, pkgName);
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
+ res = error("restorecon failed for " + path);
+ }
+ }
+ return res;
+}
+
+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];
+
+ if (validate_apk_path(oat_dir)) {
+ return error("Invalid path " + oatDir);
+ }
+ if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
+ return error("Failed to prepare " + oatDir);
+ }
+ if (selinux_android_restorecon(oat_dir, 0)) {
+ return error("Failed to restorecon " + oatDir);
+ }
+ snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set);
+ if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
+ return error(StringPrintf("Failed to prepare %s", oat_instr_dir));
+ }
+ return ok();
+}
+
+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);
+ }
+ if (delete_dir_contents_and_dir(packageDir) != 0) {
+ return error("Failed to delete " + packageDir);
+ }
+ return ok();
+}
+
+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();
+ char from_path[PKG_PATH_MAX];
+ char to_path[PKG_PATH_MAX];
+ snprintf(from_path, PKG_PATH_MAX, "%s/%s", from_base, relative_path);
+ snprintf(to_path, PKG_PATH_MAX, "%s/%s", to_base, relative_path);
+
+ if (validate_apk_path_subdirs(from_path)) {
+ return error(StringPrintf("Invalid from path %s", from_path));
+ }
+
+ if (validate_apk_path_subdirs(to_path)) {
+ return error(StringPrintf("Invalid to path %s", to_path));
+ }
+
+ if (link(from_path, to_path) < 0) {
+ return error(StringPrintf("Failed to link from %s to %s", from_path, to_path));
+ }
+
+ return ok();
+}
+
+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();
+ const char* oat_dir = outputPath.c_str();
+
+ bool success = move_ab(apk_path, instruction_set, oat_dir);
+ return success ? ok() : error();
+}
+
+binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
+ const std::string& instructionSet, const std::unique_ptr<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();
+ const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
+
+ bool res = delete_odex(apk_path, instruction_set, oat_dir);
+ return res ? ok() : error();
+}
+
+binder::Status InstalldNativeService::reconcileSecondaryDexFile(
+ const std::string& dexPath, const std::string& packageName, int32_t uid,
+ const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
+ int32_t storage_flag, bool* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(volumeUuid);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+ bool result = android::installd::reconcile_secondary_dex_file(
+ dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return);
+ return result ? ok() : error();
+}
+
+binder::Status InstalldNativeService::invalidateMounts() {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+
+ mStorageMounts.clear();
+ mQuotaReverseMounts.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;
+ while (!in.eof()) {
+ std::getline(in, source, ' ');
+ std::getline(in, target, ' ');
+ std::getline(in, ignored);
+
+#if !BYPASS_SDCARDFS
+ if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
+#endif
+
+#if !BYPASS_QUOTA
+ if (source.compare(0, 11, "/dev/block/") == 0) {
+ struct dqblk dq;
+ if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
+ reinterpret_cast<char*>(&dq)) == 0) {
+ LOG(DEBUG) << "Found quota mount " << source << " at " << target;
+ mQuotaReverseMounts[target] = source;
+
+ // ext4 only enables DQUOT_USAGE_ENABLED by default, so we
+ // need to kick it again to enable DQUOT_LIMITS_ENABLED.
+ if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0
+ && errno != EBUSY) {
+ PLOG(ERROR) << "Failed to enable USRQUOTA on " << source;
+ }
+ if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0
+ && errno != EBUSY) {
+ PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source;
+ }
+ }
+ }
+#endif
+ }
+ return ok();
+}
+
+std::string InstalldNativeService::findDataMediaPath(
+ const std::unique_ptr<std::string>& uuid, userid_t userid) {
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
+ auto resolved = mStorageMounts[path];
+ if (resolved.empty()) {
+ LOG(WARNING) << "Failed to find storage mount for " << path;
+ resolved = path;
+ }
+ return StringPrintf("%s/%u", resolved.c_str(), userid);
+}
+
+std::string InstalldNativeService::findQuotaDeviceForUuid(
+ const std::unique_ptr<std::string>& uuid) {
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+ auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
+ return mQuotaReverseMounts[path];
+}
+
+binder::Status InstalldNativeService::isQuotaSupported(
+ const std::unique_ptr<std::string>& volumeUuid, bool* _aidl_return) {
+ *_aidl_return = !findQuotaDeviceForUuid(volumeUuid).empty();
+ return ok();
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
new file mode 100644
index 0000000..4011315
--- /dev/null
+++ b/cmds/installd/InstalldNativeService.h
@@ -0,0 +1,142 @@
+/*
+**
+** Copyright 2008, 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 COMMANDS_H_
+#define COMMANDS_H_
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <vector>
+#include <unordered_map>
+
+#include <android-base/macros.h>
+#include <binder/BinderService.h>
+#include <cutils/multiuser.h>
+
+#include "android/os/BnInstalld.h"
+#include "installd_constants.h"
+
+namespace android {
+namespace installd {
+
+class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {
+public:
+ static status_t start();
+ static char const* getServiceName() { return "installd"; }
+ virtual status_t dump(int fd, const Vector<String16> &args) override;
+
+ binder::Status createUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ int32_t userSerial, int32_t flags);
+ binder::Status destroyUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ int32_t flags);
+
+ binder::Status createAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+ const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+ binder::Status restoreconAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+ const std::string& seInfo);
+ binder::Status migrateAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags);
+ binder::Status clearAppData(const std::unique_ptr<std::string>& uuid,
+ 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 fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
+
+ binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
+ 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, const std::vector<int32_t>& appIds,
+ 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,
+ const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+ int32_t targetSdkVersion);
+
+ binder::Status dexopt(const std::string& apkPath, int32_t uid,
+ const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
+ int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
+ const std::unique_ptr<std::string>& sharedLibraries,
+ const std::unique_ptr<std::string>& seInfo);
+
+ binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
+
+ binder::Status mergeProfiles(int32_t uid, const std::string& packageName, bool* _aidl_return);
+ binder::Status dumpProfiles(int32_t uid, const std::string& packageName,
+ const std::string& codePaths, bool* _aidl_return);
+ binder::Status clearAppProfiles(const std::string& packageName);
+ binder::Status destroyAppProfiles(const std::string& packageName);
+
+ binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,
+ int32_t uid);
+ binder::Status removeIdmap(const std::string& overlayApkPath);
+ 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 targetFreeBytes,
+ int64_t cacheReservedBytes, 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);
+ binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
+ const std::string& toBase);
+ binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
+ const std::string& outputPath);
+ binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
+ const std::unique_ptr<std::string>& outputPath);
+ binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
+ const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
+ const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
+
+ binder::Status invalidateMounts();
+ binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
+ bool* _aidl_return);
+
+private:
+ std::recursive_mutex mLock;
+
+ std::recursive_mutex mMountsLock;
+ std::recursive_mutex mQuotasLock;
+
+ /* Map of all storage mounts from source to target */
+ std::unordered_map<std::string, std::string> mStorageMounts;
+ /* Map of all quota mounts from target to source */
+ std::unordered_map<std::string, std::string> mQuotaReverseMounts;
+
+ /* Map from UID to cache quota size */
+ std::unordered_map<uid_t, int64_t> mCacheQuotas;
+
+ std::string findDataMediaPath(const std::unique_ptr<std::string>& uuid, userid_t userid);
+ std::string findQuotaDeviceForUuid(const std::unique_ptr<std::string>& uuid);
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // COMMANDS_H_
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
new file mode 100644
index 0000000..f09a397
--- /dev/null
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IInstalld {
+ void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
+ void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
+
+ long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
+ int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
+ void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags, int appId, @utf8InCpp String seInfo);
+ void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags);
+ void clearAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags, long ceDataInode);
+ void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags, long ceDataInode);
+
+ void fixupAppData(@nullable @utf8InCpp String uuid, int flags);
+
+ 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, in int[] appIds);
+
+ 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,
+ @utf8InCpp String seInfo, int targetSdkVersion);
+
+ void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+ @utf8InCpp String instructionSet, int dexoptNeeded,
+ @nullable @utf8InCpp String outputPath, int dexFlags,
+ @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
+ @nullable @utf8InCpp String sharedLibraries,
+ @nullable @utf8InCpp String seInfo);
+
+ void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
+
+ boolean mergeProfiles(int uid, @utf8InCpp String packageName);
+ boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String codePaths);
+ void clearAppProfiles(@utf8InCpp String packageName);
+ void destroyAppProfiles(@utf8InCpp String packageName);
+
+ void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
+ void removeIdmap(@utf8InCpp String overlayApkPath);
+ void rmPackageDir(@utf8InCpp String packageDir);
+ void markBootComplete(@utf8InCpp String instructionSet);
+ void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
+ long cacheReservedBytes, int flags);
+ void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
+ @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
+ void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
+ void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase,
+ @utf8InCpp String toBase);
+ void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+ @utf8InCpp String outputPath);
+ void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+ @nullable @utf8InCpp String outputPath);
+
+ boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
+ int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
+ int storage_flag);
+
+ void invalidateMounts();
+ boolean isQuotaSupported(@nullable @utf8InCpp String uuid);
+}
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
deleted file mode 100644
index 271c75b..0000000
--- a/cmds/installd/commands.cpp
+++ /dev/null
@@ -1,2294 +0,0 @@
-/*
-** Copyright 2008, 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 "commands.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <regex>
-#include <stdlib.h>
-#include <sys/capability.h>
-#include <sys/file.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/xattr.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#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/sched_policy.h>
-#include <diskusage/dirsize.h>
-#include <logwrap/logwrap.h>
-#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
-#include <system/thread_defs.h>
-
-#include <globals.h>
-#include <installd_deps.h>
-#include <otapreopt_utils.h>
-#include <utils.h>
-
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-
-using android::base::EndsWith;
-using android::base::StringPrintf;
-
-namespace android {
-namespace installd {
-
-static constexpr const char* kCpPath = "/system/bin/cp";
-static constexpr const char* kXattrDefault = "user.default";
-
-static constexpr const char* PKG_LIB_POSTFIX = "/lib";
-static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
-static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
-
-static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
-static constexpr const char* IDMAP_SUFFIX = "@idmap";
-
-// NOTE: keep in sync with StorageManager
-static constexpr int FLAG_STORAGE_DE = 1 << 0;
-static constexpr int FLAG_STORAGE_CE = 1 << 1;
-
-// NOTE: keep in sync with Installer
-static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
-static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
-
-/* dexopt needed flags matching those in dalvik.system.DexFile */
-static constexpr int DEXOPT_DEX2OAT_NEEDED = 1;
-static constexpr int DEXOPT_PATCHOAT_NEEDED = 2;
-static constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3;
-
-#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
-
-typedef int fd_t;
-
-static bool property_get_bool(const char* property_name, bool default_value = false) {
- char tmp_property_value[kPropertyValueMax];
- bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0;
- if (!have_property) {
- return default_value;
- }
- return strcmp(tmp_property_value, "true") == 0;
-}
-
-// Keep profile paths in sync with ActivityThread.
-constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof";
-static std::string create_primary_profile(const std::string& profile_dir) {
- return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
-}
-
-/**
- * Perform restorecon of the given path, but only perform recursive restorecon
- * if the label of that top-level file actually changed. This can save us
- * significant time by avoiding no-op traversals of large filesystem trees.
- */
-static int restorecon_app_data_lazy(const std::string& path, const char* seinfo, uid_t uid) {
- int res = 0;
- char* before = nullptr;
- char* after = nullptr;
-
- // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
- // libselinux. Not needed here.
-
- if (lgetfilecon(path.c_str(), &before) < 0) {
- PLOG(ERROR) << "Failed before getfilecon for " << path;
- goto fail;
- }
- if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, 0) < 0) {
- PLOG(ERROR) << "Failed top-level restorecon for " << path;
- goto fail;
- }
- if (lgetfilecon(path.c_str(), &after) < 0) {
- PLOG(ERROR) << "Failed after getfilecon for " << path;
- goto fail;
- }
-
- // If the initial top-level restorecon above changed the label, then go
- // back and restorecon everything recursively
- if (strcmp(before, after)) {
- LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " << path
- << "; running recursive restorecon";
- if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid,
- SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
- PLOG(ERROR) << "Failed recursive restorecon for " << path;
- goto fail;
- }
- }
-
- goto done;
-fail:
- res = -1;
-done:
- free(before);
- free(after);
- return res;
-}
-
-static int restorecon_app_data_lazy(const std::string& parent, const char* name, const char* seinfo,
- uid_t uid) {
- return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seinfo, uid);
-}
-
-static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
- if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
- PLOG(ERROR) << "Failed to prepare " << path;
- return -1;
- }
- return 0;
-}
-
-static int prepare_app_dir(const std::string& parent, const char* name, mode_t target_mode,
- uid_t uid) {
- return prepare_app_dir(StringPrintf("%s/%s", parent.c_str(), name), target_mode, uid);
-}
-
-int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- appid_t appid, const char* seinfo, int target_sdk_version) {
- uid_t uid = multiuser_get_uid(userid, appid);
- mode_t target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
- if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
- if (prepare_app_dir(path, target_mode, uid) ||
- prepare_app_dir(path, "cache", 0771, uid) ||
- prepare_app_dir(path, "code_cache", 0771, uid)) {
- return -1;
- }
-
- // Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seinfo, uid) ||
- restorecon_app_data_lazy(path, "cache", seinfo, uid) ||
- restorecon_app_data_lazy(path, "code_cache", seinfo, uid)) {
- return -1;
- }
-
- // Remember inode numbers of cache directories so that we can clear
- // contents while CE storage is locked
- if (write_path_inode(path, "cache", kXattrInodeCache) ||
- write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
- return -1;
- }
- }
- if (flags & FLAG_STORAGE_DE) {
- auto path = create_data_user_de_package_path(uuid, userid, pkgname);
- if (prepare_app_dir(path, target_mode, uid)) {
- // TODO: include result once 25796509 is fixed
- return 0;
- }
-
- // Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seinfo, uid)) {
- return -1;
- }
-
- if (property_get_bool("dalvik.vm.usejitprofiles")) {
- const std::string profile_path = create_data_user_profile_package_path(userid, pkgname);
- // read-write-execute only for the app user.
- if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
- PLOG(ERROR) << "Failed to prepare " << profile_path;
- return -1;
- }
- std::string profile_file = create_primary_profile(profile_path);
- // read-write only for the app user.
- if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
- PLOG(ERROR) << "Failed to prepare " << profile_path;
- return -1;
- }
- const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
- // dex2oat/profman runs under the shared app gid and it needs to read/write reference
- // profiles.
- appid_t shared_app_gid = multiuser_get_shared_app_gid(uid);
- if (fs_prepare_dir_strict(
- ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
- PLOG(ERROR) << "Failed to prepare " << ref_profile_path;
- return -1;
- }
- }
- }
- return 0;
-}
-
-int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
- // This method only exists to upgrade system apps that have requested
- // forceDeviceEncrypted, so their default storage always lives in a
- // consistent location. This only works on non-FBE devices, since we
- // never want to risk exposing data on a device with real CE/DE storage.
-
- auto ce_path = create_data_user_ce_package_path(uuid, userid, pkgname);
- auto de_path = create_data_user_de_package_path(uuid, userid, pkgname);
-
- // If neither directory is marked as default, assume CE is default
- if (getxattr(ce_path.c_str(), kXattrDefault, nullptr, 0) == -1
- && getxattr(de_path.c_str(), kXattrDefault, nullptr, 0) == -1) {
- if (setxattr(ce_path.c_str(), kXattrDefault, nullptr, 0, 0) != 0) {
- PLOG(ERROR) << "Failed to mark default storage " << ce_path;
- return -1;
- }
- }
-
- // Migrate default data location if needed
- auto target = (flags & FLAG_STORAGE_DE) ? de_path : ce_path;
- auto source = (flags & FLAG_STORAGE_DE) ? ce_path : de_path;
-
- if (getxattr(target.c_str(), kXattrDefault, nullptr, 0) == -1) {
- LOG(WARNING) << "Requested default storage " << target
- << " is not active; migrating from " << source;
- if (delete_dir_contents_and_dir(target) != 0) {
- PLOG(ERROR) << "Failed to delete";
- return -1;
- }
- if (rename(source.c_str(), target.c_str()) != 0) {
- PLOG(ERROR) << "Failed to rename";
- return -1;
- }
- }
-
- return 0;
-}
-
-static bool clear_profile(const std::string& profile) {
- base::unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
- if (ufd.get() < 0) {
- if (errno != ENOENT) {
- PLOG(WARNING) << "Could not open profile " << profile;
- return false;
- } else {
- // Nothing to clear. That's ok.
- return true;
- }
- }
-
- if (flock(ufd.get(), LOCK_EX | LOCK_NB) != 0) {
- if (errno != EWOULDBLOCK) {
- PLOG(WARNING) << "Error locking profile " << profile;
- }
- // This implies that the app owning this profile is running
- // (and has acquired the lock).
- //
- // If we can't acquire the lock bail out since clearing is useless anyway
- // (the app will write again to the profile).
- //
- // Note:
- // This does not impact the this is not an issue for the profiling correctness.
- // In case this is needed because of an app upgrade, profiles will still be
- // eventually cleared by the app itself due to checksum mismatch.
- // If this is needed because profman advised, then keeping the data around
- // until the next run is again not an issue.
- //
- // If the app attempts to acquire a lock while we've held one here,
- // it will simply skip the current write cycle.
- return false;
- }
-
- bool truncated = ftruncate(ufd.get(), 0) == 0;
- if (!truncated) {
- PLOG(WARNING) << "Could not truncate " << profile;
- }
- if (flock(ufd.get(), LOCK_UN) != 0) {
- PLOG(WARNING) << "Error unlocking profile " << profile;
- }
- return truncated;
-}
-
-static bool clear_reference_profile(const char* pkgname) {
- std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
- std::string reference_profile = create_primary_profile(reference_profile_dir);
- return clear_profile(reference_profile);
-}
-
-static bool clear_current_profile(const char* pkgname, userid_t user) {
- std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
- std::string profile = create_primary_profile(profile_dir);
- return clear_profile(profile);
-}
-
-static bool clear_current_profiles(const char* pkgname) {
- bool success = true;
- std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
- for (auto user : users) {
- success &= clear_current_profile(pkgname, user);
- }
- return success;
-}
-
-int clear_app_profiles(const char* pkgname) {
- bool success = true;
- success &= clear_reference_profile(pkgname);
- success &= clear_current_profiles(pkgname);
- return success ? 0 : -1;
-}
-
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- ino_t ce_data_inode) {
- int res = 0;
- if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
- if (flags & FLAG_CLEAR_CACHE_ONLY) {
- path = read_path_inode(path, "cache", kXattrInodeCache);
- } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
- path = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
- }
- if (access(path.c_str(), F_OK) == 0) {
- res |= delete_dir_contents(path);
- }
- }
- if (flags & FLAG_STORAGE_DE) {
- std::string suffix = "";
- bool only_cache = false;
- if (flags & FLAG_CLEAR_CACHE_ONLY) {
- suffix = CACHE_DIR_POSTFIX;
- only_cache = true;
- } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
- suffix = CODE_CACHE_DIR_POSTFIX;
- only_cache = true;
- }
-
- auto path = create_data_user_de_package_path(uuid, userid, pkgname) + suffix;
- if (access(path.c_str(), F_OK) == 0) {
- // TODO: include result once 25796509 is fixed
- delete_dir_contents(path);
- }
- if (!only_cache) {
- if (!clear_current_profile(pkgname, userid)) {
- res |= -1;
- }
- }
- }
- return res;
-}
-
-static int destroy_app_reference_profile(const char *pkgname) {
- return delete_dir_contents_and_dir(
- create_data_ref_profile_package_path(pkgname),
- /*ignore_if_missing*/ true);
-}
-
-static int destroy_app_current_profiles(const char *pkgname, userid_t userid) {
- return delete_dir_contents_and_dir(
- create_data_user_profile_package_path(userid, pkgname),
- /*ignore_if_missing*/ true);
-}
-
-int destroy_app_profiles(const char *pkgname) {
- int result = 0;
- std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
- for (auto user : users) {
- result |= destroy_app_current_profiles(pkgname, user);
- }
- result |= destroy_app_reference_profile(pkgname);
- return result;
-}
-
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- ino_t ce_data_inode) {
- int res = 0;
- if (flags & FLAG_STORAGE_CE) {
- res |= delete_dir_contents_and_dir(
- create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode));
- }
- if (flags & FLAG_STORAGE_DE) {
- res |= delete_dir_contents_and_dir(
- create_data_user_de_package_path(uuid, userid, pkgname));
- destroy_app_current_profiles(pkgname, userid);
- // TODO(calin): If the package is still installed by other users it's probably
- // beneficial to keep the reference profile around.
- // Verify if it's ok to do that.
- destroy_app_reference_profile(pkgname);
- }
- return res;
-}
-
-int move_complete_app(const char *from_uuid, const char *to_uuid, const char *package_name,
- const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version) {
- std::vector<userid_t> users = get_known_users(from_uuid);
-
- // Copy app
- {
- auto from = create_data_app_package_path(from_uuid, data_app_name);
- auto to = create_data_app_package_path(to_uuid, data_app_name);
- auto to_parent = create_data_app_path(to_uuid);
-
- char *argv[] = {
- (char*) kCpPath,
- (char*) "-F", /* delete any existing destination file first (--remove-destination) */
- (char*) "-p", /* preserve timestamps, ownership, and permissions */
- (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
- (char*) "-P", /* Do not follow symlinks [default] */
- (char*) "-d", /* don't dereference symlinks */
- (char*) from.c_str(),
- (char*) to_parent.c_str()
- };
-
- LOG(DEBUG) << "Copying " << from << " to " << to;
- int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
-
- if (rc != 0) {
- LOG(ERROR) << "Failed copying " << from << " to " << to
- << ": status " << rc;
- goto fail;
- }
-
- if (selinux_android_restorecon(to.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
- LOG(ERROR) << "Failed to restorecon " << to;
- goto fail;
- }
- }
-
- // Copy private data for all known users
- for (auto user : users) {
-
- // Data source may not exist for all users; that's okay
- auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
- if (access(from_ce.c_str(), F_OK) != 0) {
- LOG(INFO) << "Missing source " << from_ce;
- continue;
- }
-
- if (create_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
- appid, seinfo, target_sdk_version) != 0) {
- LOG(ERROR) << "Failed to create package target on " << to_uuid;
- goto fail;
- }
-
- char *argv[] = {
- (char*) kCpPath,
- (char*) "-F", /* delete any existing destination file first (--remove-destination) */
- (char*) "-p", /* preserve timestamps, ownership, and permissions */
- (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
- (char*) "-P", /* Do not follow symlinks [default] */
- (char*) "-d", /* don't dereference symlinks */
- nullptr,
- nullptr
- };
-
- {
- auto from = create_data_user_de_package_path(from_uuid, user, package_name);
- auto to = create_data_user_de_path(to_uuid, user);
- argv[6] = (char*) from.c_str();
- argv[7] = (char*) to.c_str();
-
- LOG(DEBUG) << "Copying " << from << " to " << to;
- int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
- if (rc != 0) {
- LOG(ERROR) << "Failed copying " << from << " to " << to << " with status " << rc;
- goto fail;
- }
- }
- {
- auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
- auto to = create_data_user_ce_path(to_uuid, user);
- argv[6] = (char*) from.c_str();
- argv[7] = (char*) to.c_str();
-
- LOG(DEBUG) << "Copying " << from << " to " << to;
- int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
- if (rc != 0) {
- LOG(ERROR) << "Failed copying " << from << " to " << to << " with status " << rc;
- goto fail;
- }
- }
-
- if (restorecon_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
- appid, seinfo) != 0) {
- LOG(ERROR) << "Failed to restorecon";
- goto fail;
- }
- }
-
- // We let the framework scan the new location and persist that before
- // deleting the data in the old location; this ordering ensures that
- // we can recover from things like battery pulls.
- return 0;
-
-fail:
- // Nuke everything we might have already copied
- {
- auto to = create_data_app_package_path(to_uuid, data_app_name);
- if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
- LOG(WARNING) << "Failed to rollback " << to;
- }
- }
- for (auto user : users) {
- {
- auto to = create_data_user_de_package_path(to_uuid, user, package_name);
- if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
- LOG(WARNING) << "Failed to rollback " << to;
- }
- }
- {
- auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
- if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
- LOG(WARNING) << "Failed to rollback " << to;
- }
- }
- }
- return -1;
-}
-
-int create_user_data(const char *uuid, userid_t userid, int user_serial ATTRIBUTE_UNUSED,
- int flags) {
- if (flags & FLAG_STORAGE_DE) {
- if (uuid == nullptr) {
- return ensure_config_user_dirs(userid);
- }
- }
- return 0;
-}
-
-int destroy_user_data(const char *uuid, userid_t userid, int flags) {
- int res = 0;
- if (flags & FLAG_STORAGE_DE) {
- res |= delete_dir_contents_and_dir(create_data_user_de_path(uuid, userid), true);
- if (uuid == nullptr) {
- res |= delete_dir_contents_and_dir(create_data_misc_legacy_path(userid), true);
- res |= delete_dir_contents_and_dir(create_data_user_profiles_path(userid), true);
- }
- }
- if (flags & FLAG_STORAGE_CE) {
- res |= delete_dir_contents_and_dir(create_data_user_ce_path(uuid, userid), true);
- res |= delete_dir_contents_and_dir(create_data_media_path(uuid, userid), true);
- }
- return res;
-}
-
-/* Try to ensure free_size bytes of storage are available.
- * Returns 0 on success.
- * This is rather simple-minded because doing a full LRU would
- * be potentially memory-intensive, and without atime it would
- * also require that apps constantly modify file metadata even
- * when just reading from the cache, which is pretty awful.
- */
-int free_cache(const char *uuid, int64_t free_size) {
- cache_t* cache;
- int64_t avail;
-
- auto data_path = create_data_path(uuid);
-
- avail = data_disk_free(data_path);
- if (avail < 0) return -1;
-
- ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
- if (avail >= free_size) return 0;
-
- cache = start_cache_collection();
-
- 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()));
- }
-
- clear_cache_files(data_path, cache, free_size);
- finish_cache_collection(cache);
-
- return data_disk_free(data_path) >= free_size ? 0 : -1;
-}
-
-int rm_dex(const char *path, const char *instruction_set)
-{
- char dex_path[PKG_PATH_MAX];
-
- if (validate_apk_path(path) && validate_system_app_path(path)) {
- ALOGE("invalid apk path '%s' (bad prefix)\n", path);
- return -1;
- }
-
- if (!create_cache_path(dex_path, path, instruction_set)) return -1;
-
- ALOGV("unlink %s\n", dex_path);
- if (unlink(dex_path) < 0) {
- if (errno != ENOENT) {
- ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno));
- }
- return -1;
- } else {
- return 0;
- }
-}
-
-static void add_app_data_size(std::string& path, int64_t *codesize, int64_t *datasize,
- int64_t *cachesize) {
- DIR *d;
- int dfd;
- struct dirent *de;
- struct stat s;
-
- d = opendir(path.c_str());
- if (d == nullptr) {
- PLOG(WARNING) << "Failed to open " << path;
- return;
- }
- dfd = dirfd(d);
- while ((de = readdir(d))) {
- const char *name = de->d_name;
-
- int64_t statsize = 0;
- if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
- statsize = stat_size(&s);
- }
-
- if (de->d_type == DT_DIR) {
- int subfd;
- int64_t dirsize = 0;
- /* always skip "." and ".." */
- if (name[0] == '.') {
- if (name[1] == 0) continue;
- if ((name[1] == '.') && (name[2] == 0)) continue;
- }
- subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
- if (subfd >= 0) {
- dirsize = calculate_dir_size(subfd);
- close(subfd);
- }
- // TODO: check xattrs!
- if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
- *datasize += statsize;
- *cachesize += dirsize;
- } else {
- *datasize += dirsize + statsize;
- }
- } else if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
- *codesize += statsize;
- } else {
- *datasize += statsize;
- }
- }
- closedir(d);
-}
-
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
- const char *code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
- int64_t* asecsize) {
- DIR *d;
- int dfd;
-
- d = opendir(code_path);
- if (d != nullptr) {
- dfd = dirfd(d);
- *codesize += calculate_dir_size(dfd);
- closedir(d);
- }
-
- if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
- add_app_data_size(path, codesize, datasize, cachesize);
- }
- if (flags & FLAG_STORAGE_DE) {
- auto path = create_data_user_de_package_path(uuid, userid, pkgname);
- add_app_data_size(path, codesize, datasize, cachesize);
- }
-
- *asecsize = 0;
-
- return 0;
-}
-
-int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode) {
- if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
- return get_path_inode(path, inode);
- }
- return -1;
-}
-
-static int split_count(const char *str)
-{
- char *ctx;
- int count = 0;
- char buf[kPropertyValueMax];
-
- strncpy(buf, str, sizeof(buf));
- char *pBuf = buf;
-
- while(strtok_r(pBuf, " ", &ctx) != NULL) {
- count++;
- pBuf = NULL;
- }
-
- return count;
-}
-
-static int split(char *buf, const char **argv)
-{
- char *ctx;
- int count = 0;
- char *tok;
- char *pBuf = buf;
-
- while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) {
- argv[count++] = tok;
- pBuf = NULL;
- }
-
- return count;
-}
-
-static void run_patchoat(int input_fd, int oat_fd, const char* input_file_name,
- const char* output_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 output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN];
- char input_oat_fd_arg[strlen("--input-oat-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", oat_fd);
- sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_fd);
- ALOGV("Running %s isa=%s in-fd=%d (%s) out-fd=%d (%s)\n",
- PATCHOAT_BIN, instruction_set, input_fd, input_file_name, oat_fd, output_file_name);
-
- /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */
- char* argv[7];
- 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] = output_oat_fd_arg;
- argv[5] = input_oat_fd_arg;
- argv[6] = 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 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, bool debuggable, bool post_bootcomplete,
- int profile_fd, const char* shared_libraries) {
- static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
-
- 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;
- }
-
- char dex2oat_Xms_flag[kPropertyValueMax];
- bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
-
- char dex2oat_Xmx_flag[kPropertyValueMax];
- bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
-
- char dex2oat_threads_buf[kPropertyValueMax];
- bool have_dex2oat_threads_flag = get_property(post_bootcomplete
- ? "dalvik.vm.dex2oat-threads"
- : "dalvik.vm.boot-dex2oat-threads",
- dex2oat_threads_buf,
- NULL) > 0;
- char dex2oat_threads_arg[kPropertyValueMax + 2];
- if (have_dex2oat_threads_flag) {
- sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf);
- }
-
- char dex2oat_isa_features_key[kPropertyKeyMax];
- sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
- char dex2oat_isa_features[kPropertyValueMax];
- bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key,
- dex2oat_isa_features, NULL) > 0;
-
- char dex2oat_isa_variant_key[kPropertyKeyMax];
- sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
- char dex2oat_isa_variant[kPropertyValueMax];
- bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key,
- dex2oat_isa_variant, NULL) > 0;
-
- const char *dex2oat_norelocation = "-Xnorelocate";
- bool have_dex2oat_relocation_skip_flag = false;
-
- char dex2oat_flags[kPropertyValueMax];
- int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags",
- dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags);
- ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);
-
- // If we booting without the real /data, don't spend time compiling.
- char vold_decrypt[kPropertyValueMax];
- bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0;
- bool skip_compilation = (have_vold_decrypt &&
- (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
- (strcmp(vold_decrypt, "1") == 0)));
-
- bool generate_debug_info = property_get_bool("debug.generate-debug-info");
-
- char app_image_format[kPropertyValueMax];
- char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
- bool have_app_image_format =
- image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
- if (have_app_image_format) {
- sprintf(image_format_arg, "--image-format=%s", app_image_format);
- }
-
- char dex2oat_large_app_threshold[kPropertyValueMax];
- bool have_dex2oat_large_app_threshold =
- get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0;
- char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax];
- if (have_dex2oat_large_app_threshold) {
- sprintf(dex2oat_large_app_threshold_arg,
- "--very-large-app-threshold=%s",
- dex2oat_large_app_threshold);
- }
-
- static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
-
- static const char* RUNTIME_ARG = "--runtime-arg";
-
- static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig
-
- char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];
- char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
- char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];
- char oat_location_arg[strlen("--oat-location=") + PKG_PATH_MAX];
- char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
- char instruction_set_variant_arg[strlen("--instruction-set-variant=") + kPropertyValueMax];
- char instruction_set_features_arg[strlen("--instruction-set-features=") + kPropertyValueMax];
- char dex2oat_Xms_arg[strlen("-Xms") + kPropertyValueMax];
- char dex2oat_Xmx_arg[strlen("-Xmx") + kPropertyValueMax];
- char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
- bool have_dex2oat_swap_fd = false;
- char dex2oat_swap_fd[strlen("--swap-fd=") + MAX_INT_LEN];
- bool have_dex2oat_image_fd = false;
- char dex2oat_image_fd[strlen("--app-image-fd=") + MAX_INT_LEN];
-
- sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
- sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
- sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
- sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
- sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
- sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant);
- sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features);
- if (swap_fd >= 0) {
- have_dex2oat_swap_fd = true;
- sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd);
- }
- if (image_fd >= 0) {
- have_dex2oat_image_fd = true;
- sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd);
- }
-
- if (have_dex2oat_Xms_flag) {
- sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag);
- }
- if (have_dex2oat_Xmx_flag) {
- sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
- }
-
- // Compute compiler filter.
-
- bool have_dex2oat_compiler_filter_flag;
- if (skip_compilation) {
- strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none");
- have_dex2oat_compiler_filter_flag = true;
- have_dex2oat_relocation_skip_flag = true;
- } else if (vm_safe_mode) {
- strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
- have_dex2oat_compiler_filter_flag = true;
- } else if (compiler_filter != nullptr &&
- strlen(compiler_filter) + strlen("--compiler-filter=") <
- arraysize(dex2oat_compiler_filter_arg)) {
- sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
- have_dex2oat_compiler_filter_flag = true;
- } else {
- char dex2oat_compiler_filter_flag[kPropertyValueMax];
- have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
- dex2oat_compiler_filter_flag, NULL) > 0;
- if (have_dex2oat_compiler_filter_flag) {
- sprintf(dex2oat_compiler_filter_arg,
- "--compiler-filter=%s",
- dex2oat_compiler_filter_flag);
- }
- }
-
- // Check whether all apps should be compiled debuggable.
- if (!debuggable) {
- char prop_buf[kPropertyValueMax];
- debuggable =
- (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
- (prop_buf[0] == '1');
- }
- char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
- if (profile_fd != -1) {
- sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
- }
-
-
- ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
-
- const char* argv[7 // program name, mandatory arguments and the final NULL
- + (have_dex2oat_isa_variant ? 1 : 0)
- + (have_dex2oat_isa_features ? 1 : 0)
- + (have_dex2oat_Xms_flag ? 2 : 0)
- + (have_dex2oat_Xmx_flag ? 2 : 0)
- + (have_dex2oat_compiler_filter_flag ? 1 : 0)
- + (have_dex2oat_threads_flag ? 1 : 0)
- + (have_dex2oat_swap_fd ? 1 : 0)
- + (have_dex2oat_image_fd ? 1 : 0)
- + (have_dex2oat_relocation_skip_flag ? 2 : 0)
- + (generate_debug_info ? 1 : 0)
- + (debuggable ? 1 : 0)
- + (have_app_image_format ? 1 : 0)
- + dex2oat_flags_count
- + (profile_fd == -1 ? 0 : 1)
- + (shared_libraries != nullptr ? 4 : 0)
- + (have_dex2oat_large_app_threshold ? 1 : 0)];
- int i = 0;
- argv[i++] = DEX2OAT_BIN;
- argv[i++] = zip_fd_arg;
- argv[i++] = zip_location_arg;
- argv[i++] = oat_fd_arg;
- argv[i++] = oat_location_arg;
- argv[i++] = instruction_set_arg;
- if (have_dex2oat_isa_variant) {
- argv[i++] = instruction_set_variant_arg;
- }
- if (have_dex2oat_isa_features) {
- argv[i++] = instruction_set_features_arg;
- }
- if (have_dex2oat_Xms_flag) {
- argv[i++] = RUNTIME_ARG;
- argv[i++] = dex2oat_Xms_arg;
- }
- if (have_dex2oat_Xmx_flag) {
- argv[i++] = RUNTIME_ARG;
- argv[i++] = dex2oat_Xmx_arg;
- }
- if (have_dex2oat_compiler_filter_flag) {
- argv[i++] = dex2oat_compiler_filter_arg;
- }
- if (have_dex2oat_threads_flag) {
- argv[i++] = dex2oat_threads_arg;
- }
- if (have_dex2oat_swap_fd) {
- argv[i++] = dex2oat_swap_fd;
- }
- if (have_dex2oat_image_fd) {
- argv[i++] = dex2oat_image_fd;
- }
- if (generate_debug_info) {
- argv[i++] = "--generate-debug-info";
- }
- if (debuggable) {
- argv[i++] = "--debuggable";
- }
- if (have_app_image_format) {
- argv[i++] = image_format_arg;
- }
- if (have_dex2oat_large_app_threshold) {
- argv[i++] = dex2oat_large_app_threshold_arg;
- }
- if (dex2oat_flags_count) {
- i += split(dex2oat_flags, argv + i);
- }
- if (have_dex2oat_relocation_skip_flag) {
- argv[i++] = RUNTIME_ARG;
- argv[i++] = dex2oat_norelocation;
- }
- if (profile_fd != -1) {
- argv[i++] = profile_arg;
- }
- if (shared_libraries != nullptr) {
- argv[i++] = RUNTIME_ARG;
- argv[i++] = "-classpath";
- argv[i++] = RUNTIME_ARG;
- argv[i++] = shared_libraries;
- }
- // Do not add after dex2oat_flags, they should override others for debugging.
- argv[i] = NULL;
-
- execv(DEX2OAT_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
-}
-
-/*
- * Whether dexopt should use a swap file when compiling an APK.
- *
- * If kAlwaysProvideSwapFile, do this on all devices (dex2oat will make a more informed decision
- * itself, anyways).
- *
- * Otherwise, read "dalvik.vm.dex2oat-swap". If the property exists, return whether it is "true".
- *
- * Otherwise, return true if this is a low-mem device.
- *
- * Otherwise, return default value.
- */
-static bool kAlwaysProvideSwapFile = false;
-static bool kDefaultProvideSwapFile = true;
-
-static bool ShouldUseSwapFileForDexopt() {
- if (kAlwaysProvideSwapFile) {
- return true;
- }
-
- // Check the "override" property. If it exists, return value == "true".
- char dex2oat_prop_buf[kPropertyValueMax];
- if (get_property("dalvik.vm.dex2oat-swap", dex2oat_prop_buf, "") > 0) {
- if (strcmp(dex2oat_prop_buf, "true") == 0) {
- return true;
- } else {
- return false;
- }
- }
-
- // Shortcut for default value. This is an implementation optimization for the process sketched
- // above. If the default value is true, we can avoid to check whether this is a low-mem device,
- // as low-mem is never returning false. The compiler will optimize this away if it can.
- if (kDefaultProvideSwapFile) {
- return true;
- }
-
- bool is_low_mem = property_get_bool("ro.config.low_ram");
- if (is_low_mem) {
- return true;
- }
-
- // Default value must be false here.
- return kDefaultProvideSwapFile;
-}
-
-static void SetDex2OatAndPatchOatScheduling(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));
- exit(70);
- }
- if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
- ALOGE("setpriority failed: %s\n", strerror(errno));
- exit(71);
- }
- }
-}
-
-static void close_all_fds(const std::vector<fd_t>& fds, const char* description) {
- for (size_t i = 0; i < fds.size(); i++) {
- if (close(fds[i]) != 0) {
- PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i;
- }
- }
-}
-
-static fd_t open_profile_dir(const std::string& profile_dir) {
- fd_t profile_dir_fd = TEMP_FAILURE_RETRY(open(profile_dir.c_str(),
- O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
- if (profile_dir_fd < 0) {
- // In a multi-user environment, these directories can be created at
- // different points and it's possible we'll attempt to open a profile
- // dir before it exists.
- if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to open profile_dir: " << profile_dir;
- }
- }
- return profile_dir_fd;
-}
-
-static fd_t open_primary_profile_file_from_dir(const std::string& profile_dir, mode_t open_mode) {
- fd_t profile_dir_fd = open_profile_dir(profile_dir);
- if (profile_dir_fd < 0) {
- return -1;
- }
-
- fd_t profile_fd = -1;
- std::string profile_file = create_primary_profile(profile_dir);
-
- profile_fd = TEMP_FAILURE_RETRY(open(profile_file.c_str(), open_mode | O_NOFOLLOW));
- if (profile_fd == -1) {
- // It's not an error if the profile file does not exist.
- if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
- }
- }
- // TODO(calin): use AutoCloseFD instead of closing the fd manually.
- if (close(profile_dir_fd) != 0) {
- PLOG(WARNING) << "Could not close profile dir " << profile_dir;
- }
- return profile_fd;
-}
-
-static fd_t open_primary_profile_file(userid_t user, const char* pkgname) {
- std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
- return open_primary_profile_file_from_dir(profile_dir, O_RDONLY);
-}
-
-static fd_t open_reference_profile(uid_t uid, const char* pkgname, bool read_write) {
- std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
- int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY;
- fd_t fd = open_primary_profile_file_from_dir(reference_profile_dir, flags);
- if (fd < 0) {
- return -1;
- }
- if (read_write) {
- // Fix the owner.
- if (fchown(fd, uid, uid) < 0) {
- close(fd);
- return -1;
- }
- }
- return fd;
-}
-
-static void open_profile_files(uid_t uid, const char* pkgname,
- /*out*/ std::vector<fd_t>* profiles_fd, /*out*/ fd_t* reference_profile_fd) {
- // Open the reference profile in read-write mode as profman might need to save the merge.
- *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true);
- if (*reference_profile_fd < 0) {
- // We can't access the reference profile file.
- return;
- }
-
- std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
- for (auto user : users) {
- fd_t profile_fd = open_primary_profile_file(user, pkgname);
- // Add to the lists only if both fds are valid.
- if (profile_fd >= 0) {
- profiles_fd->push_back(profile_fd);
- }
- }
-}
-
-static void drop_capabilities(uid_t uid) {
- if (setgid(uid) != 0) {
- ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
- exit(64);
- }
- if (setuid(uid) != 0) {
- ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
- exit(65);
- }
- // drop capabilities
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- if (capset(&capheader, &capdata[0]) < 0) {
- ALOGE("capset failed: %s\n", strerror(errno));
- exit(66);
- }
-}
-
-static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
-static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
-static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
-
-static void run_profman_merge(const std::vector<fd_t>& profiles_fd, fd_t reference_profile_fd) {
- static const size_t MAX_INT_LEN = 32;
- static const char* PROFMAN_BIN = "/system/bin/profman";
-
- std::vector<std::string> profile_args(profiles_fd.size());
- char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
- for (size_t k = 0; k < profiles_fd.size(); k++) {
- sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k]);
- profile_args[k].assign(profile_buf);
- }
- char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
- sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd);
-
- // program name, reference profile fd, the final NULL and the profile fds
- const char* argv[3 + profiles_fd.size()];
- int i = 0;
- argv[i++] = PROFMAN_BIN;
- argv[i++] = reference_profile_arg;
- for (size_t k = 0; k < profile_args.size(); k++) {
- argv[i++] = profile_args[k].c_str();
- }
- // Do not add after dex2oat_flags, they should override others for debugging.
- argv[i] = NULL;
-
- execv(PROFMAN_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
- exit(68); /* only get here on exec failure */
-}
-
-// Decides if profile guided compilation is needed or not based on existing profiles.
-// Returns true if there is enough information in the current profiles that worth
-// a re-compilation of the package.
-// If the return value is true all the current profiles would have been merged into
-// the reference profiles accessible with open_reference_profile().
-static bool analyse_profiles(uid_t uid, const char* pkgname) {
- std::vector<fd_t> profiles_fd;
- fd_t reference_profile_fd = -1;
- open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd);
- if (profiles_fd.empty() || (reference_profile_fd == -1)) {
- // Skip profile guided compilation because no profiles were found.
- // Or if the reference profile info couldn't be opened.
- close_all_fds(profiles_fd, "profiles_fd");
- if ((reference_profile_fd != - 1) && (close(reference_profile_fd) != 0)) {
- PLOG(WARNING) << "Failed to close fd for reference profile";
- }
- return false;
- }
-
- ALOGV("PROFMAN (MERGE): --- BEGIN '%s' ---\n", pkgname);
-
- pid_t pid = fork();
- if (pid == 0) {
- /* child -- drop privileges before continuing */
- drop_capabilities(uid);
- run_profman_merge(profiles_fd, reference_profile_fd);
- exit(68); /* only get here on exec failure */
- }
- /* parent */
- int return_code = wait_child(pid);
- bool need_to_compile = false;
- bool should_clear_current_profiles = false;
- bool should_clear_reference_profile = false;
- if (!WIFEXITED(return_code)) {
- LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code;
- } else {
- return_code = WEXITSTATUS(return_code);
- switch (return_code) {
- case PROFMAN_BIN_RETURN_CODE_COMPILE:
- need_to_compile = true;
- should_clear_current_profiles = true;
- should_clear_reference_profile = false;
- break;
- case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
- need_to_compile = false;
- should_clear_current_profiles = false;
- should_clear_reference_profile = false;
- break;
- case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
- LOG(WARNING) << "Bad profiles for package " << pkgname;
- need_to_compile = false;
- should_clear_current_profiles = true;
- should_clear_reference_profile = true;
- break;
- case PROFMAN_BIN_RETURN_CODE_ERROR_IO: // fall-through
- case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING:
- // Temporary IO problem (e.g. locking). Ignore but log a warning.
- LOG(WARNING) << "IO error while reading profiles for package " << pkgname;
- need_to_compile = false;
- should_clear_current_profiles = false;
- should_clear_reference_profile = false;
- break;
- default:
- // Unknown return code or error. Unlink profiles.
- LOG(WARNING) << "Unknown error code while processing profiles for package " << pkgname
- << ": " << return_code;
- need_to_compile = false;
- should_clear_current_profiles = true;
- should_clear_reference_profile = true;
- break;
- }
- }
- close_all_fds(profiles_fd, "profiles_fd");
- if (close(reference_profile_fd) != 0) {
- PLOG(WARNING) << "Failed to close fd for reference profile";
- }
- if (should_clear_current_profiles) {
- clear_current_profiles(pkgname);
- }
- if (should_clear_reference_profile) {
- clear_reference_profile(pkgname);
- }
- return need_to_compile;
-}
-
-static void run_profman_dump(const std::vector<fd_t>& profile_fds,
- fd_t reference_profile_fd,
- const std::vector<std::string>& dex_locations,
- const std::vector<fd_t>& apk_fds,
- fd_t output_fd) {
- std::vector<std::string> profman_args;
- static const char* PROFMAN_BIN = "/system/bin/profman";
- profman_args.push_back(PROFMAN_BIN);
- profman_args.push_back("--dump-only");
- profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd));
- if (reference_profile_fd != -1) {
- profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
- reference_profile_fd));
- }
- for (fd_t profile_fd : profile_fds) {
- profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fd));
- }
- for (const std::string& dex_location : dex_locations) {
- profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
- }
- for (fd_t apk_fd : apk_fds) {
- profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fd));
- }
- const char **argv = new const char*[profman_args.size() + 1];
- size_t i = 0;
- for (const std::string& profman_arg : profman_args) {
- argv[i++] = profman_arg.c_str();
- }
- argv[i] = NULL;
-
- execv(PROFMAN_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
- exit(68); /* only get here on exec failure */
-}
-
-static const char* get_location_from_path(const char* path) {
- static constexpr char kLocationSeparator = '/';
- const char *location = strrchr(path, kLocationSeparator);
- if (location == NULL) {
- return path;
- } else {
- // Skip the separator character.
- return location + 1;
- }
-}
-
-// Dumps the contents of a profile file, using pkgname's dex files for pretty
-// printing the result.
-bool dump_profile(uid_t uid, const char* pkgname, const char* code_path_string) {
- std::vector<fd_t> profile_fds;
- fd_t reference_profile_fd = -1;
- std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname);
-
- ALOGV("PROFMAN (DUMP): --- BEGIN '%s' ---\n", pkgname);
-
- open_profile_files(uid, pkgname, &profile_fds, &reference_profile_fd);
-
- const bool has_reference_profile = (reference_profile_fd != -1);
- const bool has_profiles = !profile_fds.empty();
-
- if (!has_reference_profile && !has_profiles) {
- ALOGE("profman dump: no profiles to dump for '%s'", pkgname);
- return false;
- }
-
- fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
- if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
- ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
- return false;
- }
- std::vector<std::string> code_full_paths = base::Split(code_path_string, ";");
- std::vector<std::string> dex_locations;
- std::vector<fd_t> apk_fds;
- for (const std::string& code_full_path : code_full_paths) {
- const char* full_path = code_full_path.c_str();
- fd_t apk_fd = open(full_path, O_RDONLY | O_NOFOLLOW);
- if (apk_fd == -1) {
- ALOGE("installd cannot open '%s'\n", full_path);
- return false;
- }
- dex_locations.push_back(get_location_from_path(full_path));
- apk_fds.push_back(apk_fd);
- }
-
- pid_t pid = fork();
- if (pid == 0) {
- /* child -- drop privileges before continuing */
- drop_capabilities(uid);
- run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
- apk_fds, output_fd);
- exit(68); /* only get here on exec failure */
- }
- /* parent */
- close_all_fds(apk_fds, "apk_fds");
- close_all_fds(profile_fds, "profile_fds");
- if (close(reference_profile_fd) != 0) {
- PLOG(WARNING) << "Failed to close fd for reference profile";
- }
- int return_code = wait_child(pid);
- if (!WIFEXITED(return_code)) {
- LOG(WARNING) << "profman failed for package " << pkgname << ": "
- << return_code;
- return false;
- }
- return true;
-}
-
-// Translate the given oat path to an art (app image) path. An empty string
-// denotes an error.
-static std::string create_image_filename(const std::string& oat_path) {
- // A standard dalvik-cache entry. Replace ".dex" with ".art."
- if (EndsWith(oat_path, ".dex")) {
- std::string art_path = oat_path;
- art_path.replace(art_path.length() - strlen("dex"), strlen("dex"), "art");
- CHECK(EndsWith(art_path, ".art"));
- return art_path;
- }
-
- // An odex entry. Not that this may not be an extension, e.g., in the OTA
- // case (where the base name will have an extension for the B artifact).
- size_t odex_pos = oat_path.rfind(".odex");
- if (odex_pos != std::string::npos) {
- std::string art_path = oat_path;
- art_path.replace(odex_pos, strlen(".odex"), ".art");
- CHECK_NE(art_path.find(".art"), std::string::npos);
- return art_path;
- }
-
- // Don't know how to handle this.
- return "";
-}
-
-static bool add_extension_to_file_name(char* file_name, const char* extension) {
- if (strlen(file_name) + strlen(extension) + 1 > PKG_PATH_MAX) {
- return false;
- }
- strcat(file_name, extension);
- return true;
-}
-
-static int open_output_file(const char* file_name, bool recreate, int permissions) {
- int flags = O_RDWR | O_CREAT;
- if (recreate) {
- if (unlink(file_name) < 0) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "open_output_file: Couldn't unlink " << file_name;
- }
- }
- flags |= O_EXCL;
- }
- return open(file_name, flags, permissions);
-}
-
-static bool set_permissions_and_ownership(int fd, bool is_public, int uid, const char* path) {
- if (fchmod(fd,
- S_IRUSR|S_IWUSR|S_IRGRP |
- (is_public ? S_IROTH : 0)) < 0) {
- ALOGE("installd cannot chmod '%s' during dexopt\n", path);
- return false;
- } else if (fchown(fd, AID_SYSTEM, uid) < 0) {
- ALOGE("installd cannot chown '%s' during dexopt\n", path);
- return false;
- }
- return true;
-}
-
-static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
- const char* oat_dir, /*out*/ char* out_path) {
- // Early best-effort check whether we can fit the the path into our buffers.
- // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
- // without a swap file, if necessary. Reference profiles file also add an extra ".prof"
- // extension to the cache path (5 bytes).
- if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
- ALOGE("apk_path too long '%s'\n", apk_path);
- return false;
- }
-
- if (oat_dir != NULL && oat_dir[0] != '!') {
- if (validate_apk_path(oat_dir)) {
- ALOGE("invalid oat_dir '%s'\n", oat_dir);
- return false;
- }
- if (!calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) {
- return false;
- }
- } else {
- if (!create_cache_path(out_path, apk_path, instruction_set)) {
- return false;
- }
- }
- return true;
-}
-
-// TODO: Consider returning error codes.
-bool merge_profiles(uid_t uid, const char *pkgname) {
- return analyse_profiles(uid, pkgname);
-}
-
-static const char* parse_null(const char* arg) {
- if (strcmp(arg, "!") == 0) {
- return nullptr;
- } else {
- return arg;
- }
-}
-
-int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) {
- return dexopt(params[0], // apk_path
- atoi(params[1]), // uid
- params[2], // pkgname
- params[3], // instruction_set
- atoi(params[4]), // dexopt_needed
- params[5], // oat_dir
- atoi(params[6]), // dexopt_flags
- params[7], // compiler_filter
- parse_null(params[8]), // volume_uuid
- parse_null(params[9])); // shared_libraries
- static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count");
-}
-
-// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
-// on destruction. It will also run the given cleanup (unless told not to) after closing.
-//
-// Usage example:
-//
-// Dex2oatFileWrapper<std::function<void ()>> file(open(...),
-// [name]() {
-// unlink(name.c_str());
-// });
-// // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
-// wrapper if captured as a reference.
-//
-// if (file.get() == -1) {
-// // Error opening...
-// }
-//
-// ...
-// if (error) {
-// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
-// // and delete the file (after the fd is closed).
-// return -1;
-// }
-//
-// (Success case)
-// file.SetCleanup(false);
-// // 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(int value, Cleanup cleanup)
- : value_(value), cleanup_(cleanup), do_cleanup_(true) {}
-
- ~Dex2oatFileWrapper() {
- reset(-1);
- }
-
- int get() {
- return value_;
- }
-
- void SetCleanup(bool cleanup) {
- do_cleanup_ = cleanup;
- }
-
- void reset(int new_value) {
- if (value_ >= 0) {
- close(value_);
- }
- if (do_cleanup_ && cleanup_ != nullptr) {
- cleanup_();
- }
-
- value_ = new_value;
- }
-
- void reset(int new_value, Cleanup new_cleanup) {
- if (value_ >= 0) {
- close(value_);
- }
- if (do_cleanup_ && cleanup_ != nullptr) {
- cleanup_();
- }
-
- value_ = new_value;
- cleanup_ = new_cleanup;
- }
-
- private:
- int value_;
- Cleanup cleanup_;
- bool do_cleanup_;
-};
-
-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;
-
- // Don't use profile for vm_safe_mode. b/30688277
- profile_guided = profile_guided && !vm_safe_mode;
-
- CHECK(pkgname != nullptr);
- CHECK(pkgname[0] != 0);
-
- // 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] != '*') {
- // 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.
- }
-
- if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
- LOG_FATAL("dexopt flags contains unknown fields\n");
- }
-
- char out_path[PKG_PATH_MAX];
- if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
- return false;
- }
-
- const char *input_file;
- char in_odex_path[PKG_PATH_MAX];
- switch (dexopt_needed) {
- case DEXOPT_DEX2OAT_NEEDED:
- input_file = apk_path;
- break;
-
- case DEXOPT_PATCHOAT_NEEDED:
- if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
- return -1;
- }
- input_file = in_odex_path;
- break;
-
- case DEXOPT_SELF_PATCHOAT_NEEDED:
- input_file = out_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);
-
- 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;
- }
-
- const std::string out_path_str(out_path);
- Dex2oatFileWrapper<std::function<void ()>> out_fd(
- open_output_file(out_path, /*recreate*/true, /*permissions*/0644),
- [out_path_str]() { unlink(out_path_str.c_str()); });
- if (out_fd.get() < 0) {
- ALOGE("installd cannot open '%s' for output during dexopt\n", out_path);
- return -1;
- }
- if (!set_permissions_and_ownership(out_fd.get(), is_public, uid, out_path)) {
- 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_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;
- }
- }
- }
-
- // 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_path);
- if (!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;
- }
- }
- }
- }
-
- ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
-
- pid_t pid = fork();
- if (pid == 0) {
- /* child -- drop privileges before continuing */
- drop_capabilities(uid);
-
- SetDex2OatAndPatchOatScheduling(boot_complete);
- if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
- ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));
- _exit(67);
- }
-
- if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
- || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
- run_patchoat(input_fd.get(),
- out_fd.get(),
- input_file,
- out_path,
- pkgname,
- instruction_set);
- } else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
- // 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_fd.get(),
- image_fd.get(),
- input_file_name,
- out_path,
- swap_fd.get(),
- instruction_set,
- compiler_filter,
- vm_safe_mode,
- debuggable,
- boot_complete,
- reference_profile_fd.get(),
- shared_libraries);
- } else {
- ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
- _exit(73);
- }
- _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);
- } else {
- ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
- return -1;
- }
- }
-
- struct utimbuf ut;
- ut.actime = input_stat.st_atime;
- ut.modtime = input_stat.st_mtime;
- utime(out_path, &ut);
-
- // We've been successful, don't delete output.
- out_fd.SetCleanup(false);
- image_fd.SetCleanup(false);
- reference_profile_fd.SetCleanup(false);
-
- return 0;
-}
-
-int mark_boot_complete(const char* instruction_set)
-{
- char boot_marker_path[PKG_PATH_MAX];
- sprintf(boot_marker_path,
- "%s/%s/%s/.booting",
- android_data_dir.path,
- DALVIK_CACHE,
- instruction_set);
-
- ALOGV("mark_boot_complete : %s", boot_marker_path);
- if (unlink(boot_marker_path) != 0) {
- ALOGE("Unable to unlink boot marker at %s, error=%s", boot_marker_path,
- strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
- struct stat* statbuf)
-{
- while (path[basepos] != 0) {
- if (path[basepos] == '/') {
- path[basepos] = 0;
- if (lstat(path, statbuf) < 0) {
- ALOGV("Making directory: %s\n", path);
- if (mkdir(path, mode) == 0) {
- chown(path, uid, gid);
- } else {
- ALOGW("Unable to make directory %s: %s\n", path, strerror(errno));
- }
- }
- path[basepos] = '/';
- basepos++;
- }
- basepos++;
- }
-}
-
-int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId)
-{
- struct stat s, libStat;
- int rc = 0;
-
- std::string _pkgdir(create_data_user_ce_package_path(uuid, userId, pkgname));
- std::string _libsymlink(_pkgdir + PKG_LIB_POSTFIX);
-
- const char* pkgdir = _pkgdir.c_str();
- const char* libsymlink = _libsymlink.c_str();
-
- if (stat(pkgdir, &s) < 0) return -1;
-
- if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
- ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno));
- return -1;
- }
-
- if (chmod(pkgdir, 0700) < 0) {
- ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
- rc = -1;
- goto out;
- }
-
- if (lstat(libsymlink, &libStat) < 0) {
- if (errno != ENOENT) {
- ALOGE("couldn't stat lib dir: %s\n", strerror(errno));
- rc = -1;
- goto out;
- }
- } else {
- if (S_ISDIR(libStat.st_mode)) {
- if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
- rc = -1;
- goto out;
- }
- } else if (S_ISLNK(libStat.st_mode)) {
- if (unlink(libsymlink) < 0) {
- ALOGE("couldn't unlink lib dir: %s\n", strerror(errno));
- rc = -1;
- goto out;
- }
- }
- }
-
- if (symlink(asecLibDir, libsymlink) < 0) {
- ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir,
- strerror(errno));
- rc = -errno;
- goto out;
- }
-
-out:
- if (chmod(pkgdir, s.st_mode) < 0) {
- ALOGE("linklib() 2: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
- rc = -errno;
- }
-
- if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
- ALOGE("failed to chown '%s' : %s\n", pkgdir, strerror(errno));
- return -errno;
- }
-
- return rc;
-}
-
-static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
-{
- static const char *IDMAP_BIN = "/system/bin/idmap";
- static const size_t MAX_INT_LEN = 32;
- char idmap_str[MAX_INT_LEN];
-
- snprintf(idmap_str, sizeof(idmap_str), "%d", idmap_fd);
-
- execl(IDMAP_BIN, IDMAP_BIN, "--fd", target_apk, overlay_apk, idmap_str, (char*)NULL);
- ALOGE("execl(%s) failed: %s\n", IDMAP_BIN, strerror(errno));
-}
-
-// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
-// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
-static int flatten_path(const char *prefix, const char *suffix,
- const char *overlay_path, char *idmap_path, size_t N)
-{
- if (overlay_path == NULL || idmap_path == NULL) {
- return -1;
- }
- const size_t len_overlay_path = strlen(overlay_path);
- // will access overlay_path + 1 further below; requires absolute path
- if (len_overlay_path < 2 || *overlay_path != '/') {
- return -1;
- }
- const size_t len_idmap_root = strlen(prefix);
- const size_t len_suffix = strlen(suffix);
- if (SIZE_MAX - len_idmap_root < len_overlay_path ||
- SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) {
- // additions below would cause overflow
- return -1;
- }
- if (N < len_idmap_root + len_overlay_path + len_suffix) {
- return -1;
- }
- memset(idmap_path, 0, N);
- snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix);
- char *ch = idmap_path + len_idmap_root;
- while (*ch != '\0') {
- if (*ch == '/') {
- *ch = '@';
- }
- ++ch;
- }
- return 0;
-}
-
-int idmap(const char *target_apk, const char *overlay_apk, uid_t uid)
-{
- ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
-
- int idmap_fd = -1;
- char idmap_path[PATH_MAX];
-
- if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
- idmap_path, sizeof(idmap_path)) == -1) {
- ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
- goto fail;
- }
-
- unlink(idmap_path);
- idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
- if (idmap_fd < 0) {
- ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
- goto fail;
- }
- if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
- ALOGE("idmap cannot chown '%s'\n", idmap_path);
- goto fail;
- }
- if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
- ALOGE("idmap cannot chmod '%s'\n", idmap_path);
- goto fail;
- }
-
- pid_t pid;
- pid = fork();
- if (pid == 0) {
- /* child -- drop privileges before continuing */
- if (setgid(uid) != 0) {
- ALOGE("setgid(%d) failed during idmap\n", uid);
- exit(1);
- }
- if (setuid(uid) != 0) {
- ALOGE("setuid(%d) failed during idmap\n", uid);
- exit(1);
- }
- if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
- ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
- exit(1);
- }
-
- run_idmap(target_apk, overlay_apk, idmap_fd);
- exit(1); /* only if exec call to idmap failed */
- } else {
- int status = wait_child(pid);
- if (status != 0) {
- ALOGE("idmap failed, status=0x%04x\n", status);
- goto fail;
- }
- }
-
- close(idmap_fd);
- return 0;
-fail:
- if (idmap_fd >= 0) {
- close(idmap_fd);
- unlink(idmap_path);
- }
- return -1;
-}
-
-int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
- appid_t appid, const char* seinfo) {
- int res = 0;
-
- // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
- unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
-
- if (!pkgName || !seinfo) {
- ALOGE("Package name or seinfo tag is null when trying to restorecon.");
- return -1;
- }
-
- uid_t uid = multiuser_get_uid(userid, appid);
- if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgName);
- if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
- PLOG(ERROR) << "restorecon failed for " << path;
- res = -1;
- }
- }
- if (flags & FLAG_STORAGE_DE) {
- auto path = create_data_user_de_package_path(uuid, userid, pkgName);
- if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
- PLOG(ERROR) << "restorecon failed for " << path;
- // TODO: include result once 25796509 is fixed
- }
- }
-
- return res;
-}
-
-int create_oat_dir(const char* oat_dir, const char* instruction_set)
-{
- char oat_instr_dir[PKG_PATH_MAX];
-
- if (validate_apk_path(oat_dir)) {
- ALOGE("invalid apk path '%s' (bad prefix)\n", oat_dir);
- return -1;
- }
- if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
- return -1;
- }
- if (selinux_android_restorecon(oat_dir, 0)) {
- ALOGE("cannot restorecon dir '%s': %s\n", oat_dir, strerror(errno));
- return -1;
- }
- snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set);
- if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
- return -1;
- }
- return 0;
-}
-
-int rm_package_dir(const char* apk_path)
-{
- if (validate_apk_path(apk_path)) {
- ALOGE("invalid apk path '%s' (bad prefix)\n", apk_path);
- return -1;
- }
- return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */);
-}
-
-int link_file(const char* relative_path, const char* from_base, const char* to_base) {
- char from_path[PKG_PATH_MAX];
- char to_path[PKG_PATH_MAX];
- snprintf(from_path, PKG_PATH_MAX, "%s/%s", from_base, relative_path);
- snprintf(to_path, PKG_PATH_MAX, "%s/%s", to_base, relative_path);
-
- if (validate_apk_path_subdirs(from_path)) {
- ALOGE("invalid app data sub-path '%s' (bad prefix)\n", from_path);
- return -1;
- }
-
- if (validate_apk_path_subdirs(to_path)) {
- ALOGE("invalid app data sub-path '%s' (bad prefix)\n", to_path);
- return -1;
- }
-
- const int ret = link(from_path, to_path);
- if (ret < 0) {
- ALOGE("link(%s, %s) failed : %s", from_path, to_path, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-// Helper for move_ab, so that we can have common failure-case cleanup.
-static bool unlink_and_rename(const char* from, const char* to) {
- // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
- // return a failure.
- struct stat s;
- if (stat(to, &s) == 0) {
- if (!S_ISREG(s.st_mode)) {
- LOG(ERROR) << from << " is not a regular file to replace for A/B.";
- return false;
- }
- if (unlink(to) != 0) {
- LOG(ERROR) << "Could not unlink " << to << " to move A/B.";
- return false;
- }
- } else {
- // This may be a permission problem. We could investigate the error code, but we'll just
- // let the rename failure do the work for us.
- }
-
- // Try to rename "to" to "from."
- if (rename(from, to) != 0) {
- PLOG(ERROR) << "Could not rename " << from << " to " << to;
- return false;
- }
-
- return true;
-}
-
-// Move/rename a B artifact (from) to an A artifact (to).
-static bool move_ab_path(const std::string& b_path, const std::string& a_path) {
- // Check whether B exists.
- {
- struct stat s;
- if (stat(b_path.c_str(), &s) != 0) {
- // Silently ignore for now. The service calling this isn't smart enough to understand
- // lack of artifacts at the moment.
- return false;
- }
- if (!S_ISREG(s.st_mode)) {
- LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
- // Try to unlink, but swallow errors.
- unlink(b_path.c_str());
- return false;
- }
- }
-
- // Rename B to A.
- if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) {
- // Delete the b_path so we don't try again (or fail earlier).
- if (unlink(b_path.c_str()) != 0) {
- PLOG(ERROR) << "Could not unlink " << b_path;
- }
-
- return false;
- }
-
- return true;
-}
-
-int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
- if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) {
- LOG(ERROR) << "Cannot move_ab with null input";
- return -1;
- }
-
- // Get the current slot suffix. No suffix, no A/B.
- std::string slot_suffix;
- {
- char buf[kPropertyValueMax];
- if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) {
- return -1;
- }
- slot_suffix = buf;
-
- if (!ValidateTargetSlotSuffix(slot_suffix)) {
- LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
- return -1;
- }
- }
-
- // Validate other inputs.
- if (validate_apk_path(apk_path) != 0) {
- LOG(ERROR) << "invalid apk_path " << apk_path;
- return -1;
- }
- if (validate_apk_path(oat_dir) != 0) {
- LOG(ERROR) << "invalid oat_dir " << oat_dir;
- return -1;
- }
-
- char a_path[PKG_PATH_MAX];
- if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
- return -1;
- }
- const std::string a_image_path = create_image_filename(a_path);
-
- // B path = A path + slot suffix.
- const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str());
- const std::string b_image_path = StringPrintf("%s.%s",
- a_image_path.c_str(),
- slot_suffix.c_str());
-
- bool oat_success = move_ab_path(b_path, a_path);
- bool success;
-
- if (oat_success) {
- // Note: we can live without an app image. As such, ignore failure to move the image file.
- // If we decide to require the app image, or the app image being moved correctly,
- // then change accordingly.
- constexpr bool kIgnoreAppImageFailure = true;
-
- bool art_success = true;
- if (!a_image_path.empty()) {
- art_success = move_ab_path(b_image_path, a_image_path);
- if (!art_success) {
- unlink(a_image_path.c_str());
- }
- }
-
- success = art_success || kIgnoreAppImageFailure;
- } else {
- // Cleanup: delete B image, ignore errors.
- unlink(b_image_path.c_str());
-
- success = false;
- }
-
- return success ? 0 : -1;
-}
-
-bool delete_odex(const char *apk_path, const char *instruction_set, const char *oat_dir) {
- // Delete the oat/odex file.
- char out_path[PKG_PATH_MAX];
- if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
- return false;
- }
-
- // In case of a permission failure report the issue. Otherwise just print a warning.
- auto unlink_and_check = [](const char* path) -> bool {
- int result = unlink(path);
- if (result != 0) {
- if (errno == EACCES || errno == EPERM) {
- PLOG(ERROR) << "Could not unlink " << path;
- return false;
- }
- PLOG(WARNING) << "Could not unlink " << path;
- }
- return true;
- };
-
- // Delete the oat/odex file.
- bool return_value_oat = unlink_and_check(out_path);
-
- // Derive and delete the app image.
- bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
-
- // Report success.
- return return_value_oat && return_value_art;
-}
-
-} // namespace installd
-} // namespace android
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
deleted file mode 100644
index ba27517..0000000
--- a/cmds/installd/commands.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-**
-** Copyright 2008, 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 COMMANDS_H_
-#define COMMANDS_H_
-
-#include <inttypes.h>
-#include <unistd.h>
-
-#include <cutils/multiuser.h>
-
-#include <installd_constants.h>
-
-namespace android {
-namespace installd {
-
-static constexpr size_t DEXOPT_PARAM_COUNT = 10U;
-
-int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- appid_t appid, const char* seinfo, int target_sdk_version);
-int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
- appid_t appid, const char* seinfo);
-int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags);
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- ino_t ce_data_inode);
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- ino_t ce_data_inode);
-
-int move_complete_app(const char* from_uuid, const char *to_uuid, const char *package_name,
- const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version);
-
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
- const char* code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
- int64_t *asecsize);
-int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode);
-
-int create_user_data(const char *uuid, userid_t userid, int user_serial, int flags);
-int destroy_user_data(const char *uuid, userid_t userid, int flags);
-
-int rm_dex(const char *path, const char *instruction_set);
-int free_cache(const char *uuid, int64_t free_size);
-
-bool merge_profiles(uid_t uid, const char *pkgname);
-
-bool dump_profile(uid_t uid, const char *pkgname, const char *dex_files);
-
-int dexopt(const char *apk_path,
- uid_t uid,
- const char *pkgName,
- const char *instruction_set,
- int dexopt_needed,
- const char* oat_dir,
- int dexopt_flags,
- const char* compiler_filter,
- const char* volume_uuid,
- const char* shared_libraries);
-static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
-
-// Helper for the above, converting arguments.
-int dexopt(const char* const params[DEXOPT_PARAM_COUNT]);
-
-int mark_boot_complete(const char *instruction_set);
-int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
-int idmap(const char *target_path, const char *overlay_path, uid_t uid);
-int create_oat_dir(const char* oat_dir, const char *instruction_set);
-int rm_package_dir(const char* apk_path);
-int clear_app_profiles(const char* pkgname);
-int destroy_app_profiles(const char* pkgname);
-int link_file(const char *relative_path, const char *from_base, const char *to_base);
-
-// Move a B version over to the A location. Only works for oat_dir != nullptr.
-int move_ab(const char *apk_path, const char *instruction_set, const char* oat_dir);
-
-// Delete odex files generated by dexopt.
-bool delete_odex(const char *apk_path, const char *instruction_set, const char *oat_dir);
-
-} // namespace installd
-} // namespace android
-
-#endif // COMMANDS_H_
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
new file mode 100644
index 0000000..70cf35c
--- /dev/null
+++ b/cmds/installd/dexopt.cpp
@@ -0,0 +1,1905 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "installed"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.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 <selinux/android.h>
+#include <system/thread_defs.h>
+
+#include "dexopt.h"
+#include "installd_deps.h"
+#include "otapreopt_utils.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+using android::base::EndsWith;
+using android::base::unique_fd;
+
+namespace android {
+namespace installd {
+
+// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
+struct FreeDelete {
+ // NOTE: Deleting a const object is valid but free() takes a non-const pointer.
+ void operator()(const void* ptr) const {
+ free(const_cast<void*>(ptr));
+ }
+};
+
+// Alias for std::unique_ptr<> that uses the C function free() to delete objects.
+template <typename T>
+using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
+
+static unique_fd invalid_unique_fd() {
+ return unique_fd(-1);
+}
+
+static bool clear_profile(const std::string& profile) {
+ unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
+ if (ufd.get() < 0) {
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Could not open profile " << profile;
+ return false;
+ } else {
+ // Nothing to clear. That's ok.
+ return true;
+ }
+ }
+
+ if (flock(ufd.get(), LOCK_EX | LOCK_NB) != 0) {
+ if (errno != EWOULDBLOCK) {
+ PLOG(WARNING) << "Error locking profile " << profile;
+ }
+ // This implies that the app owning this profile is running
+ // (and has acquired the lock).
+ //
+ // If we can't acquire the lock bail out since clearing is useless anyway
+ // (the app will write again to the profile).
+ //
+ // Note:
+ // This does not impact the this is not an issue for the profiling correctness.
+ // In case this is needed because of an app upgrade, profiles will still be
+ // eventually cleared by the app itself due to checksum mismatch.
+ // If this is needed because profman advised, then keeping the data around
+ // until the next run is again not an issue.
+ //
+ // If the app attempts to acquire a lock while we've held one here,
+ // it will simply skip the current write cycle.
+ return false;
+ }
+
+ bool truncated = ftruncate(ufd.get(), 0) == 0;
+ if (!truncated) {
+ PLOG(WARNING) << "Could not truncate " << profile;
+ }
+ if (flock(ufd.get(), LOCK_UN) != 0) {
+ PLOG(WARNING) << "Error unlocking profile " << profile;
+ }
+ return truncated;
+}
+
+// Clear the reference profile for the given location.
+// The location is the package name for primary apks or the dex path for secondary dex files.
+static bool clear_reference_profile(const std::string& location, bool is_secondary_dex) {
+ return clear_profile(create_reference_profile_path(location, is_secondary_dex));
+}
+
+// Clear the reference profile for the given location.
+// The location is the package name for primary apks or the dex path for secondary dex files.
+static bool clear_current_profile(const std::string& pkgname, userid_t user,
+ bool is_secondary_dex) {
+ return clear_profile(create_current_profile_path(user, pkgname, is_secondary_dex));
+}
+
+// Clear the reference profile for the primary apk of the given package.
+bool clear_primary_reference_profile(const std::string& pkgname) {
+ return clear_reference_profile(pkgname, /*is_secondary_dex*/false);
+}
+
+// Clear all current profile for the primary apk of the given package.
+bool clear_primary_current_profiles(const std::string& pkgname) {
+ bool success = true;
+ // For secondary dex files, we don't really need the user but we use it for sanity checks.
+ std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+ for (auto user : users) {
+ success &= clear_current_profile(pkgname, user, /*is_secondary_dex*/false);
+ }
+ return success;
+}
+
+// Clear the current profile for the primary apk of the given package and user.
+bool clear_primary_current_profile(const std::string& pkgname, userid_t user) {
+ return clear_current_profile(pkgname, user, /*is_secondary_dex*/false);
+}
+
+static int split_count(const char *str)
+{
+ char *ctx;
+ int count = 0;
+ char buf[kPropertyValueMax];
+
+ strncpy(buf, str, sizeof(buf));
+ char *pBuf = buf;
+
+ while(strtok_r(pBuf, " ", &ctx) != NULL) {
+ count++;
+ pBuf = NULL;
+ }
+
+ return count;
+}
+
+static int split(char *buf, const char **argv)
+{
+ char *ctx;
+ int count = 0;
+ char *tok;
+ char *pBuf = buf;
+
+ while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) {
+ argv[count++] = tok;
+ pBuf = NULL;
+ }
+
+ return count;
+}
+
+static const char* get_location_from_path(const char* path) {
+ static constexpr char kLocationSeparator = '/';
+ const char *location = strrchr(path, kLocationSeparator);
+ if (location == NULL) {
+ return path;
+ } else {
+ // Skip the separator character.
+ return location + 1;
+ }
+}
+
+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 debuggable, bool post_bootcomplete, int profile_fd, const char* shared_libraries) {
+ static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
+
+ 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;
+ }
+
+ // Get the relative path to the input file.
+ const char* relative_input_file_name = get_location_from_path(input_file_name);
+
+ char dex2oat_Xms_flag[kPropertyValueMax];
+ bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
+
+ char dex2oat_Xmx_flag[kPropertyValueMax];
+ bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
+
+ char dex2oat_threads_buf[kPropertyValueMax];
+ bool have_dex2oat_threads_flag = get_property(post_bootcomplete
+ ? "dalvik.vm.dex2oat-threads"
+ : "dalvik.vm.boot-dex2oat-threads",
+ dex2oat_threads_buf,
+ NULL) > 0;
+ char dex2oat_threads_arg[kPropertyValueMax + 2];
+ if (have_dex2oat_threads_flag) {
+ sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf);
+ }
+
+ char dex2oat_isa_features_key[kPropertyKeyMax];
+ sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
+ char dex2oat_isa_features[kPropertyValueMax];
+ bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key,
+ dex2oat_isa_features, NULL) > 0;
+
+ char dex2oat_isa_variant_key[kPropertyKeyMax];
+ sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
+ char dex2oat_isa_variant[kPropertyValueMax];
+ bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key,
+ dex2oat_isa_variant, NULL) > 0;
+
+ const char *dex2oat_norelocation = "-Xnorelocate";
+ bool have_dex2oat_relocation_skip_flag = false;
+
+ char dex2oat_flags[kPropertyValueMax];
+ int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags",
+ dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags);
+ ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);
+
+ // If we are booting without the real /data, don't spend time compiling.
+ char vold_decrypt[kPropertyValueMax];
+ bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0;
+ bool skip_compilation = (have_vold_decrypt &&
+ (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
+ (strcmp(vold_decrypt, "1") == 0)));
+
+ bool generate_debug_info = property_get_bool("debug.generate-debug-info", false);
+
+ char app_image_format[kPropertyValueMax];
+ char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
+ bool have_app_image_format =
+ image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+ if (have_app_image_format) {
+ sprintf(image_format_arg, "--image-format=%s", app_image_format);
+ }
+
+ char dex2oat_large_app_threshold[kPropertyValueMax];
+ bool have_dex2oat_large_app_threshold =
+ get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0;
+ char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax];
+ if (have_dex2oat_large_app_threshold) {
+ sprintf(dex2oat_large_app_threshold_arg,
+ "--very-large-app-threshold=%s",
+ dex2oat_large_app_threshold);
+ }
+
+ static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
+
+ static const char* RUNTIME_ARG = "--runtime-arg";
+
+ static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig
+
+ // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
+ // use arraysize instead.
+ char zip_fd_arg[arraysize("--zip-fd=") + MAX_INT_LEN];
+ char zip_location_arg[arraysize("--zip-location=") + PKG_PATH_MAX];
+ char input_vdex_fd_arg[arraysize("--input-vdex-fd=") + MAX_INT_LEN];
+ char output_vdex_fd_arg[arraysize("--output-vdex-fd=") + MAX_INT_LEN];
+ char oat_fd_arg[arraysize("--oat-fd=") + MAX_INT_LEN];
+ char oat_location_arg[arraysize("--oat-location=") + PKG_PATH_MAX];
+ char instruction_set_arg[arraysize("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
+ char instruction_set_variant_arg[arraysize("--instruction-set-variant=") + kPropertyValueMax];
+ char instruction_set_features_arg[arraysize("--instruction-set-features=") + kPropertyValueMax];
+ char dex2oat_Xms_arg[arraysize("-Xms") + kPropertyValueMax];
+ char dex2oat_Xmx_arg[arraysize("-Xmx") + kPropertyValueMax];
+ char dex2oat_compiler_filter_arg[arraysize("--compiler-filter=") + kPropertyValueMax];
+ bool have_dex2oat_swap_fd = false;
+ char dex2oat_swap_fd[arraysize("--swap-fd=") + MAX_INT_LEN];
+ bool have_dex2oat_image_fd = false;
+ char dex2oat_image_fd[arraysize("--app-image-fd=") + MAX_INT_LEN];
+
+ sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
+ sprintf(zip_location_arg, "--zip-location=%s", relative_input_file_name);
+ sprintf(input_vdex_fd_arg, "--input-vdex-fd=%d", input_vdex_fd);
+ sprintf(output_vdex_fd_arg, "--output-vdex-fd=%d", output_vdex_fd);
+ sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
+ sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
+ sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
+ sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant);
+ sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features);
+ if (swap_fd >= 0) {
+ have_dex2oat_swap_fd = true;
+ sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd);
+ }
+ if (image_fd >= 0) {
+ have_dex2oat_image_fd = true;
+ sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd);
+ }
+
+ if (have_dex2oat_Xms_flag) {
+ sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag);
+ }
+ if (have_dex2oat_Xmx_flag) {
+ sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
+ }
+
+ // Compute compiler filter.
+
+ bool have_dex2oat_compiler_filter_flag = false;
+ if (skip_compilation) {
+ strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=extract");
+ have_dex2oat_compiler_filter_flag = true;
+ have_dex2oat_relocation_skip_flag = true;
+ } else if (compiler_filter != nullptr) {
+ if (strlen(compiler_filter) + strlen("--compiler-filter=") <
+ arraysize(dex2oat_compiler_filter_arg)) {
+ sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
+ have_dex2oat_compiler_filter_flag = true;
+ } else {
+ ALOGW("Compiler filter name '%s' is too large (max characters is %zu)",
+ compiler_filter,
+ kPropertyValueMax);
+ }
+ }
+
+ if (!have_dex2oat_compiler_filter_flag) {
+ char dex2oat_compiler_filter_flag[kPropertyValueMax];
+ have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
+ dex2oat_compiler_filter_flag, NULL) > 0;
+ if (have_dex2oat_compiler_filter_flag) {
+ sprintf(dex2oat_compiler_filter_arg,
+ "--compiler-filter=%s",
+ dex2oat_compiler_filter_flag);
+ }
+ }
+
+ // Check whether all apps should be compiled debuggable.
+ if (!debuggable) {
+ char prop_buf[kPropertyValueMax];
+ debuggable =
+ (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
+ (prop_buf[0] == '1');
+ }
+ char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
+ if (profile_fd != -1) {
+ sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
+ }
+
+ // Get the directory of the apk to pass as a base classpath directory.
+ char base_dir[arraysize("--classpath-dir=") + PKG_PATH_MAX];
+ std::string apk_dir(input_file_name);
+ unsigned long dir_index = apk_dir.rfind('/');
+ bool has_base_dir = dir_index != std::string::npos;
+ if (has_base_dir) {
+ apk_dir = apk_dir.substr(0, dir_index);
+ sprintf(base_dir, "--classpath-dir=%s", apk_dir.c_str());
+ }
+
+
+ ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, relative_input_file_name, output_file_name);
+
+ const char* argv[9 // program name, mandatory arguments and the final NULL
+ + (have_dex2oat_isa_variant ? 1 : 0)
+ + (have_dex2oat_isa_features ? 1 : 0)
+ + (have_dex2oat_Xms_flag ? 2 : 0)
+ + (have_dex2oat_Xmx_flag ? 2 : 0)
+ + (have_dex2oat_compiler_filter_flag ? 1 : 0)
+ + (have_dex2oat_threads_flag ? 1 : 0)
+ + (have_dex2oat_swap_fd ? 1 : 0)
+ + (have_dex2oat_image_fd ? 1 : 0)
+ + (have_dex2oat_relocation_skip_flag ? 2 : 0)
+ + (generate_debug_info ? 1 : 0)
+ + (debuggable ? 1 : 0)
+ + (have_app_image_format ? 1 : 0)
+ + dex2oat_flags_count
+ + (profile_fd == -1 ? 0 : 1)
+ + (shared_libraries != nullptr ? 4 : 0)
+ + (has_base_dir ? 1 : 0)
+ + (have_dex2oat_large_app_threshold ? 1 : 0)];
+ int i = 0;
+ argv[i++] = DEX2OAT_BIN;
+ argv[i++] = zip_fd_arg;
+ argv[i++] = zip_location_arg;
+ argv[i++] = input_vdex_fd_arg;
+ argv[i++] = output_vdex_fd_arg;
+ argv[i++] = oat_fd_arg;
+ argv[i++] = oat_location_arg;
+ argv[i++] = instruction_set_arg;
+ if (have_dex2oat_isa_variant) {
+ argv[i++] = instruction_set_variant_arg;
+ }
+ if (have_dex2oat_isa_features) {
+ argv[i++] = instruction_set_features_arg;
+ }
+ if (have_dex2oat_Xms_flag) {
+ argv[i++] = RUNTIME_ARG;
+ argv[i++] = dex2oat_Xms_arg;
+ }
+ if (have_dex2oat_Xmx_flag) {
+ argv[i++] = RUNTIME_ARG;
+ argv[i++] = dex2oat_Xmx_arg;
+ }
+ if (have_dex2oat_compiler_filter_flag) {
+ argv[i++] = dex2oat_compiler_filter_arg;
+ }
+ if (have_dex2oat_threads_flag) {
+ argv[i++] = dex2oat_threads_arg;
+ }
+ if (have_dex2oat_swap_fd) {
+ argv[i++] = dex2oat_swap_fd;
+ }
+ if (have_dex2oat_image_fd) {
+ argv[i++] = dex2oat_image_fd;
+ }
+ if (generate_debug_info) {
+ argv[i++] = "--generate-debug-info";
+ }
+ if (debuggable) {
+ argv[i++] = "--debuggable";
+ }
+ if (have_app_image_format) {
+ argv[i++] = image_format_arg;
+ }
+ if (have_dex2oat_large_app_threshold) {
+ argv[i++] = dex2oat_large_app_threshold_arg;
+ }
+ if (dex2oat_flags_count) {
+ i += split(dex2oat_flags, argv + i);
+ }
+ if (have_dex2oat_relocation_skip_flag) {
+ argv[i++] = RUNTIME_ARG;
+ argv[i++] = dex2oat_norelocation;
+ }
+ if (profile_fd != -1) {
+ argv[i++] = profile_arg;
+ }
+ if (shared_libraries != nullptr) {
+ argv[i++] = RUNTIME_ARG;
+ argv[i++] = "-classpath";
+ argv[i++] = RUNTIME_ARG;
+ argv[i++] = shared_libraries;
+ }
+ if (has_base_dir) {
+ argv[i++] = base_dir;
+ }
+ // Do not add after dex2oat_flags, they should override others for debugging.
+ argv[i] = NULL;
+
+ execv(DEX2OAT_BIN, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
+}
+
+/*
+ * Whether dexopt should use a swap file when compiling an APK.
+ *
+ * If kAlwaysProvideSwapFile, do this on all devices (dex2oat will make a more informed decision
+ * itself, anyways).
+ *
+ * Otherwise, read "dalvik.vm.dex2oat-swap". If the property exists, return whether it is "true".
+ *
+ * Otherwise, return true if this is a low-mem device.
+ *
+ * Otherwise, return default value.
+ */
+static bool kAlwaysProvideSwapFile = false;
+static bool kDefaultProvideSwapFile = true;
+
+static bool ShouldUseSwapFileForDexopt() {
+ if (kAlwaysProvideSwapFile) {
+ return true;
+ }
+
+ // Check the "override" property. If it exists, return value == "true".
+ char dex2oat_prop_buf[kPropertyValueMax];
+ if (get_property("dalvik.vm.dex2oat-swap", dex2oat_prop_buf, "") > 0) {
+ if (strcmp(dex2oat_prop_buf, "true") == 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // Shortcut for default value. This is an implementation optimization for the process sketched
+ // above. If the default value is true, we can avoid to check whether this is a low-mem device,
+ // as low-mem is never returning false. The compiler will optimize this away if it can.
+ if (kDefaultProvideSwapFile) {
+ return true;
+ }
+
+ bool is_low_mem = property_get_bool("ro.config.low_ram", false);
+ if (is_low_mem) {
+ return true;
+ }
+
+ // Default value must be false here.
+ return kDefaultProvideSwapFile;
+}
+
+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));
+ exit(70);
+ }
+ if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+ ALOGE("setpriority failed: %s\n", strerror(errno));
+ exit(71);
+ }
+ }
+}
+
+static bool create_profile(int uid, const std::string& profile) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), O_CREAT | O_NOFOLLOW, 0600)));
+ if (fd.get() < 0) {
+ if (errno == EEXIST) {
+ return true;
+ } else {
+ PLOG(ERROR) << "Failed to create profile " << profile;
+ return false;
+ }
+ }
+ // Profiles should belong to the app; make sure of that by giving ownership to
+ // the app uid. If we cannot do that, there's no point in returning the fd
+ // since dex2oat/profman will fail with SElinux denials.
+ if (fchown(fd.get(), uid, uid) < 0) {
+ PLOG(ERROR) << "Could not chwon profile " << profile;
+ return false;
+ }
+ return true;
+}
+
+static unique_fd open_profile(int uid, const std::string& profile, bool read_write) {
+ // Check if we need to open the profile for a read-write operation. If so, we
+ // might need to create the profile since the file might not be there. Reference
+ // profiles are created on the fly so they might not exist beforehand.
+ if (read_write) {
+ if (!create_profile(uid, profile)) {
+ return invalid_unique_fd();
+ }
+ }
+ int flags = read_write ? O_RDWR : O_RDONLY;
+ // Do not follow symlinks when opening a profile:
+ // - primary profiles should not contain symlinks in their paths
+ // - secondary dex paths should have been already resolved and validated
+ flags |= O_NOFOLLOW;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
+ if (fd.get() < 0) {
+ if (errno != ENOENT) {
+ // Profiles might be missing for various reasons. For example, in a
+ // multi-user environment, the profile directory for one user can be created
+ // after we start a merge. In this case the current profile for that user
+ // will not be found.
+ // Also, the secondary dex profiles might be deleted by the app at any time,
+ // so we can't we need to prepare if they are missing.
+ PLOG(ERROR) << "Failed to open profile " << profile;
+ }
+ return invalid_unique_fd();
+ }
+
+ return fd;
+}
+
+static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& location,
+ bool is_secondary_dex) {
+ std::string profile = create_current_profile_path(user, location, is_secondary_dex);
+ return open_profile(uid, profile, /*read_write*/false);
+}
+
+static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write,
+ bool is_secondary_dex) {
+ std::string profile = create_reference_profile_path(location, is_secondary_dex);
+ return open_profile(uid, profile, read_write);
+}
+
+static void open_profile_files(uid_t uid, const std::string& location, bool is_secondary_dex,
+ /*out*/ std::vector<unique_fd>* profiles_fd, /*out*/ unique_fd* reference_profile_fd) {
+ // Open the reference profile in read-write mode as profman might need to save the merge.
+ *reference_profile_fd = open_reference_profile(uid, location, /*read_write*/ true,
+ is_secondary_dex);
+
+ // For secondary dex files, we don't really need the user but we use it for sanity checks.
+ // Note: the user owning the dex file should be the current user.
+ std::vector<userid_t> users;
+ if (is_secondary_dex){
+ users.push_back(multiuser_get_user_id(uid));
+ } else {
+ users = get_known_users(/*volume_uuid*/ nullptr);
+ }
+ for (auto user : users) {
+ unique_fd profile_fd = open_current_profile(uid, user, location, is_secondary_dex);
+ // Add to the lists only if both fds are valid.
+ if (profile_fd.get() >= 0) {
+ profiles_fd->push_back(std::move(profile_fd));
+ }
+ }
+}
+
+static void drop_capabilities(uid_t uid) {
+ if (setgid(uid) != 0) {
+ ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
+ exit(64);
+ }
+ if (setuid(uid) != 0) {
+ ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
+ exit(65);
+ }
+ // drop capabilities
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(66);
+ }
+}
+
+static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
+static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
+
+static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
+ const unique_fd& reference_profile_fd) {
+ static const size_t MAX_INT_LEN = 32;
+ static const char* PROFMAN_BIN = "/system/bin/profman";
+
+ std::vector<std::string> profile_args(profiles_fd.size());
+ char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
+ for (size_t k = 0; k < profiles_fd.size(); k++) {
+ sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k].get());
+ profile_args[k].assign(profile_buf);
+ }
+ char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
+ sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd.get());
+
+ // program name, reference profile fd, the final NULL and the profile fds
+ const char* argv[3 + profiles_fd.size()];
+ int i = 0;
+ argv[i++] = PROFMAN_BIN;
+ argv[i++] = reference_profile_arg;
+ for (size_t k = 0; k < profile_args.size(); k++) {
+ argv[i++] = profile_args[k].c_str();
+ }
+ // Do not add after dex2oat_flags, they should override others for debugging.
+ argv[i] = NULL;
+
+ execv(PROFMAN_BIN, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+ exit(68); /* only get here on exec failure */
+}
+
+// Decides if profile guided compilation is needed or not based on existing profiles.
+// The location is the package name for primary apks or the dex path for secondary dex files.
+// Returns true if there is enough information in the current profiles that makes it
+// worth to recompile the given location.
+// If the return value is true all the current profiles would have been merged into
+// the reference profiles accessible with open_reference_profile().
+static bool analyze_profiles(uid_t uid, const std::string& location, bool is_secondary_dex) {
+ std::vector<unique_fd> profiles_fd;
+ unique_fd reference_profile_fd;
+ open_profile_files(uid, location, is_secondary_dex, &profiles_fd, &reference_profile_fd);
+ if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) {
+ // Skip profile guided compilation because no profiles were found.
+ // Or if the reference profile info couldn't be opened.
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ drop_capabilities(uid);
+ run_profman_merge(profiles_fd, reference_profile_fd);
+ exit(68); /* only get here on exec failure */
+ }
+ /* parent */
+ int return_code = wait_child(pid);
+ bool need_to_compile = false;
+ bool should_clear_current_profiles = false;
+ bool should_clear_reference_profile = false;
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "profman failed for location " << location << ": " << return_code;
+ } else {
+ return_code = WEXITSTATUS(return_code);
+ switch (return_code) {
+ case PROFMAN_BIN_RETURN_CODE_COMPILE:
+ need_to_compile = true;
+ should_clear_current_profiles = true;
+ should_clear_reference_profile = false;
+ break;
+ case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
+ need_to_compile = false;
+ should_clear_current_profiles = false;
+ should_clear_reference_profile = false;
+ break;
+ case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
+ LOG(WARNING) << "Bad profiles for location " << location;
+ need_to_compile = false;
+ should_clear_current_profiles = true;
+ should_clear_reference_profile = true;
+ break;
+ case PROFMAN_BIN_RETURN_CODE_ERROR_IO: // fall-through
+ case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING:
+ // Temporary IO problem (e.g. locking). Ignore but log a warning.
+ LOG(WARNING) << "IO error while reading profiles for location " << location;
+ need_to_compile = false;
+ should_clear_current_profiles = false;
+ should_clear_reference_profile = false;
+ break;
+ default:
+ // Unknown return code or error. Unlink profiles.
+ LOG(WARNING) << "Unknown error code while processing profiles for location "
+ << location << ": " << return_code;
+ need_to_compile = false;
+ should_clear_current_profiles = true;
+ should_clear_reference_profile = true;
+ break;
+ }
+ }
+
+ if (should_clear_current_profiles) {
+ if (is_secondary_dex) {
+ // For secondary dex files, the owning user is the current user.
+ clear_current_profile(location, multiuser_get_user_id(uid), is_secondary_dex);
+ } else {
+ clear_primary_current_profiles(location);
+ }
+ }
+ if (should_clear_reference_profile) {
+ clear_reference_profile(location, is_secondary_dex);
+ }
+ return need_to_compile;
+}
+
+// Decides if profile guided compilation is needed or not based on existing profiles.
+// The analysis is done for the primary apks of the given package.
+// Returns true if there is enough information in the current profiles that makes it
+// worth to recompile the package.
+// If the return value is true all the current profiles would have been merged into
+// the reference profiles accessible with open_reference_profile().
+bool analyze_primary_profiles(uid_t uid, const std::string& pkgname) {
+ return analyze_profiles(uid, pkgname, /*is_secondary_dex*/false);
+}
+
+static void run_profman_dump(const std::vector<unique_fd>& profile_fds,
+ const unique_fd& reference_profile_fd,
+ const std::vector<std::string>& dex_locations,
+ const std::vector<unique_fd>& apk_fds,
+ const unique_fd& output_fd) {
+ std::vector<std::string> profman_args;
+ static const char* PROFMAN_BIN = "/system/bin/profman";
+ profman_args.push_back(PROFMAN_BIN);
+ profman_args.push_back("--dump-only");
+ profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd.get()));
+ if (reference_profile_fd != -1) {
+ profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
+ reference_profile_fd.get()));
+ }
+ for (size_t i = 0; i < profile_fds.size(); i++) {
+ profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fds[i].get()));
+ }
+ for (const std::string& dex_location : dex_locations) {
+ profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
+ }
+ for (size_t i = 0; i < apk_fds.size(); i++) {
+ profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fds[i].get()));
+ }
+ const char **argv = new const char*[profman_args.size() + 1];
+ size_t i = 0;
+ for (const std::string& profman_arg : profman_args) {
+ argv[i++] = profman_arg.c_str();
+ }
+ argv[i] = NULL;
+
+ execv(PROFMAN_BIN, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+ exit(68); /* only get here on exec failure */
+}
+
+bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths) {
+ std::vector<unique_fd> profile_fds;
+ unique_fd reference_profile_fd;
+ std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname.c_str());
+
+ open_profile_files(uid, pkgname, /*is_secondary_dex*/false,
+ &profile_fds, &reference_profile_fd);
+
+ const bool has_reference_profile = (reference_profile_fd.get() != -1);
+ const bool has_profiles = !profile_fds.empty();
+
+ if (!has_reference_profile && !has_profiles) {
+ LOG(ERROR) << "profman dump: no profiles to dump for " << pkgname;
+ return false;
+ }
+
+ unique_fd output_fd(open(out_file_name.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644));
+ if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
+ return false;
+ }
+ std::vector<std::string> code_full_paths = base::Split(code_paths, ";");
+ std::vector<std::string> dex_locations;
+ std::vector<unique_fd> apk_fds;
+ for (const std::string& code_full_path : code_full_paths) {
+ const char* full_path = code_full_path.c_str();
+ unique_fd apk_fd(open(full_path, O_RDONLY | O_NOFOLLOW));
+ if (apk_fd == -1) {
+ ALOGE("installd cannot open '%s'\n", full_path);
+ return false;
+ }
+ dex_locations.push_back(get_location_from_path(full_path));
+ apk_fds.push_back(std::move(apk_fd));
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ drop_capabilities(uid);
+ run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
+ apk_fds, output_fd);
+ exit(68); /* only get here on exec failure */
+ }
+ /* parent */
+ int return_code = wait_child(pid);
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "profman failed for package " << pkgname << ": "
+ << return_code;
+ return false;
+ }
+ return true;
+}
+
+static std::string replace_file_extension(const std::string& oat_path, const std::string& new_ext) {
+ // A standard dalvik-cache entry. Replace ".dex" with `new_ext`.
+ if (EndsWith(oat_path, ".dex")) {
+ std::string new_path = oat_path;
+ new_path.replace(new_path.length() - strlen(".dex"), strlen(".dex"), new_ext);
+ CHECK(EndsWith(new_path, new_ext.c_str()));
+ return new_path;
+ }
+
+ // An odex entry. Not that this may not be an extension, e.g., in the OTA
+ // case (where the base name will have an extension for the B artifact).
+ size_t odex_pos = oat_path.rfind(".odex");
+ if (odex_pos != std::string::npos) {
+ std::string new_path = oat_path;
+ new_path.replace(odex_pos, strlen(".odex"), new_ext);
+ CHECK_NE(new_path.find(new_ext), std::string::npos);
+ return new_path;
+ }
+
+ // Don't know how to handle this.
+ return "";
+}
+
+// Translate the given oat path to an art (app image) path. An empty string
+// denotes an error.
+static std::string create_image_filename(const std::string& oat_path) {
+ return replace_file_extension(oat_path, ".art");
+}
+
+// Translate the given oat path to a vdex path. An empty string denotes an error.
+static std::string create_vdex_filename(const std::string& oat_path) {
+ return replace_file_extension(oat_path, ".vdex");
+}
+
+static bool add_extension_to_file_name(char* file_name, const char* extension) {
+ if (strlen(file_name) + strlen(extension) + 1 > PKG_PATH_MAX) {
+ return false;
+ }
+ strcat(file_name, extension);
+ return true;
+}
+
+static int open_output_file(const char* file_name, bool recreate, int permissions) {
+ int flags = O_RDWR | O_CREAT;
+ if (recreate) {
+ if (unlink(file_name) < 0) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "open_output_file: Couldn't unlink " << file_name;
+ }
+ }
+ flags |= O_EXCL;
+ }
+ return open(file_name, flags, permissions);
+}
+
+static bool set_permissions_and_ownership(
+ int fd, bool is_public, int uid, const char* path, bool is_secondary_dex) {
+ // Primary apks are owned by the system. Secondary dex files are owned by the app.
+ int owning_uid = is_secondary_dex ? uid : AID_SYSTEM;
+ if (fchmod(fd,
+ S_IRUSR|S_IWUSR|S_IRGRP |
+ (is_public ? S_IROTH : 0)) < 0) {
+ ALOGE("installd cannot chmod '%s' during dexopt\n", path);
+ return false;
+ } else if (fchown(fd, owning_uid, uid) < 0) {
+ ALOGE("installd cannot chown '%s' during dexopt\n", path);
+ return false;
+ }
+ return true;
+}
+
+static bool IsOutputDalvikCache(const char* oat_dir) {
+ // InstallerConnection.java (which invokes installd) transforms Java null arguments
+ // into '!'. Play it safe by handling it both.
+ // TODO: ensure we never get null.
+ // TODO: pass a flag instead of inferring if the output is dalvik cache.
+ return oat_dir == nullptr || oat_dir[0] == '!';
+}
+
+static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
+ const char* oat_dir, bool is_secondary_dex, /*out*/ char* out_oat_path) {
+ // Early best-effort check whether we can fit the the path into our buffers.
+ // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
+ // without a swap file, if necessary. Reference profiles file also add an extra ".prof"
+ // extension to the cache path (5 bytes).
+ if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
+ ALOGE("apk_path too long '%s'\n", apk_path);
+ return false;
+ }
+
+ if (!IsOutputDalvikCache(oat_dir)) {
+ // Oat dirs for secondary dex files are already validated.
+ if (!is_secondary_dex && validate_apk_path(oat_dir)) {
+ ALOGE("cannot validate apk path with oat_dir '%s'\n", oat_dir);
+ return false;
+ }
+ if (!calculate_oat_file_path(out_oat_path, oat_dir, apk_path, instruction_set)) {
+ return false;
+ }
+ } else {
+ if (!create_cache_path(out_oat_path, apk_path, instruction_set)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
+// on destruction. It will also run the given cleanup (unless told not to) after closing.
+//
+// Usage example:
+//
+// Dex2oatFileWrapper file(open(...),
+// [name]() {
+// unlink(name.c_str());
+// });
+// // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
+// wrapper if captured as a reference.
+//
+// if (file.get() == -1) {
+// // Error opening...
+// }
+//
+// ...
+// if (error) {
+// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
+// // and delete the file (after the fd is closed).
+// return -1;
+// }
+//
+// (Success case)
+// file.SetCleanup(false);
+// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
+// // (leaving the file around; after the fd is closed).
+//
+class Dex2oatFileWrapper {
+ public:
+ Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(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);
+ }
+
+ int get() {
+ return value_;
+ }
+
+ void SetCleanup(bool cleanup) {
+ do_cleanup_ = cleanup;
+ }
+
+ void reset(int new_value) {
+ if (auto_close_ && value_ >= 0) {
+ close(value_);
+ }
+ if (do_cleanup_ && cleanup_ != nullptr) {
+ cleanup_();
+ }
+
+ value_ = new_value;
+ }
+
+ void reset(int new_value, std::function<void ()> new_cleanup) {
+ if (auto_close_ && value_ >= 0) {
+ close(value_);
+ }
+ if (do_cleanup_ && cleanup_ != nullptr) {
+ cleanup_();
+ }
+
+ value_ = new_value;
+ cleanup_ = new_cleanup;
+ }
+
+ void DisableAutoClose() {
+ auto_close_ = false;
+ }
+
+ private:
+ void release() {
+ value_ = -1;
+ do_cleanup_ = false;
+ cleanup_ = nullptr;
+ }
+ int value_;
+ std::function<void ()> cleanup_;
+ bool do_cleanup_;
+ bool auto_close_;
+};
+
+// (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, bool is_secondary_dex) {
+ // 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).
+ // Note that we don't create an image for secondary dex files.
+ if (is_secondary_dex || !profile_guided) {
+ return Dex2oatFileWrapper();
+ }
+
+ 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(), is_secondary_dex)) {
+ 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.
+unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
+ if (!ShouldUseSwapFileForDexopt()) {
+ return invalid_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 invalid_unique_fd();
+ }
+ 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 std::string& pkgname,
+ const std::string& dex_path, bool profile_guided, bool is_public, int uid,
+ bool is_secondary_dex) {
+ // Public apps should not be compiled with profile information ever. Same goes for the special
+ // package '*' used for the system server.
+ if (!profile_guided || is_public || (pkgname[0] == '*')) {
+ return Dex2oatFileWrapper();
+ }
+
+ // Open reference profile in read only mode as dex2oat does not get write permissions.
+ const std::string location = is_secondary_dex ? dex_path : pkgname;
+ unique_fd ufd = open_reference_profile(uid, location, /*read_write*/false, is_secondary_dex);
+ const auto& cleanup = [location, is_secondary_dex]() {
+ clear_reference_profile(location.c_str(), is_secondary_dex);
+ };
+ return Dex2oatFileWrapper(ufd.release(), cleanup);
+}
+
+// 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, bool is_secondary_dex,
+ bool profile_guided, 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;
+ std::string in_vdex_path_str;
+
+ // Infer the name of the output VDEX.
+ const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
+ if (out_vdex_path_str.empty()) {
+ return false;
+ }
+
+ bool update_vdex_in_place = false;
+ if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
+ // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
+ const char* path = nullptr;
+ if (is_odex_location) {
+ if (calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
+ path = in_odex_path;
+ } else {
+ ALOGE("installd cannot compute input vdex location for '%s'\n", apk_path);
+ return false;
+ }
+ } else {
+ path = out_oat_path;
+ }
+ 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 false;
+ }
+ // We can update in place when all these conditions are met:
+ // 1) The vdex location to write to is the same as the vdex location to read (vdex files
+ // on /system typically cannot be updated in place).
+ // 2) We dex2oat due to boot image change, because we then know the existing vdex file
+ // cannot be currently used by a running process.
+ // 3) We are not doing a profile guided compilation, because dexlayout requires two
+ // different vdex files to operate.
+ update_vdex_in_place =
+ (in_vdex_path_str == out_vdex_path_str) &&
+ (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) &&
+ !profile_guided;
+ if (update_vdex_in_place) {
+ // Open the file read-write to be able to update it.
+ in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
+ if (in_vdex_wrapper_fd->get() == -1) {
+ // If we failed to open the file, we cannot update it in place.
+ update_vdex_in_place = false;
+ }
+ } else {
+ in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+ }
+ }
+
+ // If we are updating the vdex in place, we do not need to recreate a vdex,
+ // and can use the same existing one.
+ if (update_vdex_in_place) {
+ // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
+ // have bogus stale vdex files.
+ out_vdex_wrapper_fd->reset(
+ in_vdex_wrapper_fd->get(),
+ [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+ // 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(
+ open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
+ [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+ 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_wrapper_fd->get(), is_public, uid,
+ out_vdex_path_str.c_str(), is_secondary_dex)) {
+ ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
+ 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, bool is_secondary_dex,
+ char* out_oat_path) {
+ if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, 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) {
+ PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path;
+ } else if (!set_permissions_and_ownership(
+ wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) {
+ 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";
+ }
+}
+
+// Runs (execv) dexoptanalyzer on the given arguments.
+// The analyzer will check if the dex_file needs to be (re)compiled to match the compiler_filter.
+// If this is for a profile guided compilation, profile_was_updated will tell whether or not
+// the profile has changed.
+static void exec_dexoptanalyzer(const std::string& dex_file, const char* instruction_set,
+ const char* compiler_filter, bool profile_was_updated) {
+ static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer";
+ static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
+
+ 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;
+ }
+
+ char dex_file_arg[strlen("--dex-file=") + PKG_PATH_MAX];
+ char isa_arg[strlen("--isa=") + MAX_INSTRUCTION_SET_LEN];
+ char compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
+ const char* assume_profile_changed = "--assume-profile-changed";
+
+ sprintf(dex_file_arg, "--dex-file=%s", dex_file.c_str());
+ sprintf(isa_arg, "--isa=%s", instruction_set);
+ sprintf(compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
+
+ // program name, dex file, isa, filter, the final NULL
+ const char* argv[5 + (profile_was_updated ? 1 : 0)];
+ int i = 0;
+ argv[i++] = DEXOPTANALYZER_BIN;
+ argv[i++] = dex_file_arg;
+ argv[i++] = isa_arg;
+ argv[i++] = compiler_filter_arg;
+ if (profile_was_updated) {
+ argv[i++] = assume_profile_changed;
+ }
+ argv[i] = NULL;
+
+ execv(DEXOPTANALYZER_BIN, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno));
+}
+
+// Prepares the oat dir for the secondary dex files.
+static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid,
+ const char* instruction_set, std::string* oat_dir_out) {
+ unsigned long dirIndex = dex_path.rfind('/');
+ if (dirIndex == std::string::npos) {
+ LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path;
+ return false;
+ }
+ std::string dex_dir = dex_path.substr(0, dirIndex);
+
+ // Create oat file output directory.
+ mode_t oat_dir_mode = S_IRWXU | S_IRWXG | S_IXOTH;
+ if (prepare_app_cache_dir(dex_dir, "oat", oat_dir_mode, uid, uid) != 0) {
+ LOG(ERROR) << "Could not prepare oat dir for secondary dex: " << dex_path;
+ return false;
+ }
+
+ char oat_dir[PKG_PATH_MAX];
+ snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", dex_dir.c_str());
+ oat_dir_out->assign(oat_dir);
+
+ // Create oat/isa output directory.
+ if (prepare_app_cache_dir(*oat_dir_out, instruction_set, oat_dir_mode, uid, uid) != 0) {
+ LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << dex_path;
+ return false;
+ }
+
+ return true;
+}
+
+static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200;
+
+// Verifies the result of dexoptanalyzer executed for the apk_path.
+// If the result is valid returns true and sets dexopt_needed_out to a valid value.
+// Returns false for errors or unexpected result values.
+static bool process_dexoptanalyzer_result(const std::string& dex_path, int result,
+ int* dexopt_needed_out) {
+ // The result values are defined in dexoptanalyzer.
+ switch (result) {
+ case 0: // no_dexopt_needed
+ *dexopt_needed_out = NO_DEXOPT_NEEDED; return true;
+ case 1: // dex2oat_from_scratch
+ *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true;
+ case 5: // dex2oat_for_bootimage_odex
+ *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true;
+ case 6: // dex2oat_for_filter_odex
+ *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true;
+ case 7: // dex2oat_for_relocation_odex
+ *dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true;
+ case 2: // dex2oat_for_bootimage_oat
+ case 3: // dex2oat_for_filter_oat
+ case 4: // dex2oat_for_relocation_oat
+ LOG(ERROR) << "Dexoptnalyzer return the status of an oat file."
+ << " Expected odex file status for secondary dex " << dex_path
+ << " : dexoptanalyzer result=" << result;
+ return false;
+ default:
+ LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path
+ << " exec_dexoptanalyzer result=" << result;
+ return false;
+ }
+}
+
+// Processes the dex_path as a secondary dex files and return true if the path dex file should
+// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
+// successfully.
+// When returning true, the output parameters will be:
+// - is_public_out: whether or not the oat file should not be made public
+// - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded
+// - oat_dir_out: the oat dir path where the oat file should be stored
+// - dex_path_out: the real path of the dex file
+static bool process_secondary_dex_dexopt(const char* original_dex_path, const char* pkgname,
+ int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
+ const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
+ std::string* oat_dir_out, std::string* dex_path_out) {
+ int storage_flag;
+
+ if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
+ storage_flag = FLAG_STORAGE_CE;
+ if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+ LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
+ return false;
+ }
+ } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+ storage_flag = FLAG_STORAGE_DE;
+ } else {
+ LOG(ERROR) << "Secondary dex storage flag must be set";
+ return false;
+ }
+
+ {
+ // As opposed to the primary apk, secondary dex files might contain symlinks.
+ // Resolve the path before passing it to the validate method to
+ // make sure the verification is done on the real location.
+ UniqueCPtr<char> dex_real_path_cstr(realpath(original_dex_path, nullptr));
+ if (dex_real_path_cstr == nullptr) {
+ PLOG(ERROR) << "Could not get the real path of the secondary dex file "
+ << original_dex_path;
+ return false;
+ } else {
+ dex_path_out->assign(dex_real_path_cstr.get());
+ }
+ }
+ const std::string& dex_path = *dex_path_out;
+ if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ return false;
+ }
+
+ // Check if the path exist. If not, there's nothing to do.
+ struct stat dex_path_stat;
+ if (stat(dex_path.c_str(), &dex_path_stat) != 0) {
+ if (errno == ENOENT) {
+ // Secondary dex files might be deleted any time by the app.
+ // Nothing to do if that's the case
+ ALOGV("Secondary dex does not exist %s", dex_path.c_str());
+ return NO_DEXOPT_NEEDED;
+ } else {
+ PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+ }
+ }
+
+ // Check if we should make the oat file public.
+ // Note that if the dex file is not public the compiled code cannot be made public.
+ *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) &&
+ ((dex_path_stat.st_mode & S_IROTH) != 0);
+
+ // Prepare the oat directories.
+ if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, oat_dir_out)) {
+ return false;
+ }
+
+ // Analyze profiles.
+ bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child -- drop privileges before continuing.
+ drop_capabilities(uid);
+ // Run dexoptanalyzer to get dexopt_needed code.
+ exec_dexoptanalyzer(dex_path, instruction_set, compiler_filter, profile_was_updated);
+ exit(DEXOPTANALYZER_BIN_EXEC_ERROR);
+ }
+
+ /* parent */
+
+ int result = wait_child(pid);
+ if (!WIFEXITED(result)) {
+ LOG(ERROR) << "dexoptanalyzer failed for path " << dex_path << ": " << result;
+ return false;
+ }
+ result = WEXITSTATUS(result);
+ bool success = process_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+ // Run dexopt only if needed or forced.
+ // Note that dexoptanalyzer is executed even if force compilation is enabled.
+ // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result)
+ // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not
+ // for oat files from dalvik-cache.
+ if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) {
+ *dexopt_needed_out = DEX2OAT_FROM_SCRATCH;
+ }
+
+ return success;
+}
+
+int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set,
+ int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
+ const char* volume_uuid, const char* shared_libraries, const char* se_info) {
+ 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 debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
+ bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
+ bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+ bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0;
+
+ // Check if we're dealing with a secondary dex file and if we need to compile it.
+ std::string oat_dir_str;
+ std::string dex_real_path;
+ if (is_secondary_dex) {
+ if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid,
+ instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str,
+ &dex_real_path)) {
+ oat_dir = oat_dir_str.c_str();
+ dex_path = dex_real_path.c_str();
+ if (dexopt_needed == NO_DEXOPT_NEEDED) {
+ return 0; // Nothing to do, report success.
+ }
+ } else {
+ return -1; // We had an error, logged in the process method.
+ }
+ } else {
+ // Currently these flags are only use for secondary dex files.
+ // Verify that they are not set for primary apks.
+ CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0);
+ CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0);
+ }
+
+ // Open the input file.
+ unique_fd input_fd(open(dex_path, O_RDONLY, 0));
+ if (input_fd.get() < 0) {
+ ALOGE("installd cannot open '%s' for input during dexopt\n", dex_path);
+ return -1;
+ }
+
+ // Create the output OAT file.
+ char out_oat_path[PKG_PATH_MAX];
+ Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
+ instruction_set, is_secondary_dex, 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(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
+ is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
+ return -1;
+ }
+
+ // Ensure that the oat dir and the compiler artifacts of secondary dex files have the correct
+ // selinux context (we generate them on the fly during the dexopt invocation and they don't
+ // fully inherit their parent context).
+ // Note that for primary apk the oat files are created before, in a separate installd
+ // call which also does the restorecon. TODO(calin): unify the paths.
+ if (is_secondary_dex) {
+ if (selinux_android_restorecon_pkgdir(oat_dir, se_info, uid,
+ SELINUX_ANDROID_RESTORECON_RECURSE)) {
+ LOG(ERROR) << "Failed to restorecon " << oat_dir;
+ return -1;
+ }
+ }
+
+ // Create a swap file if necessary.
+ unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
+
+ // Create the app image file if needed.
+ Dex2oatFileWrapper image_fd =
+ maybe_open_app_image(out_oat_path, profile_guided, is_public, uid, is_secondary_dex);
+
+ // Open the reference profile if needed.
+ Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
+ pkgname, dex_path, profile_guided, is_public, uid, is_secondary_dex);
+
+ ALOGV("DexInv: --- BEGIN '%s' ---\n", dex_path);
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ drop_capabilities(uid);
+
+ 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);
+ }
+
+ run_dex2oat(input_fd.get(),
+ out_oat_fd.get(),
+ in_vdex_fd.get(),
+ out_vdex_fd.get(),
+ image_fd.get(),
+ dex_path,
+ out_oat_path,
+ swap_fd.get(),
+ instruction_set,
+ compiler_filter,
+ 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", dex_path);
+ } else {
+ ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", dex_path, res);
+ return res;
+ }
+ }
+
+ update_out_oat_access_times(dex_path, out_oat_path);
+
+ // We've been successful, don't delete output.
+ out_oat_fd.SetCleanup(false);
+ out_vdex_fd.SetCleanup(false);
+ image_fd.SetCleanup(false);
+ reference_profile_fd.SetCleanup(false);
+
+ return 0;
+}
+
+// Try to remove the given directory. Log an error if the directory exists
+// and is empty but could not be removed.
+static bool rmdir_if_empty(const char* dir) {
+ if (rmdir(dir) == 0) {
+ return true;
+ }
+ if (errno == ENOENT || errno == ENOTEMPTY) {
+ return true;
+ }
+ PLOG(ERROR) << "Failed to remove dir: " << dir;
+ return false;
+}
+
+// Try to unlink the given file. Log an error if the file exists and could not
+// be unlinked.
+static bool unlink_if_exists(const std::string& file) {
+ if (unlink(file.c_str()) == 0) {
+ return true;
+ }
+ if (errno == ENOENT) {
+ return true;
+
+ }
+ PLOG(ERROR) << "Could not unlink: " << file;
+ return false;
+}
+
+// Create the oat file structure for the secondary dex 'dex_path' and assign
+// the individual path component to the 'out_' parameters.
+static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
+ /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) {
+ size_t dirIndex = dex_path.rfind('/');
+ if (dirIndex == std::string::npos) {
+ LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
+ return false;
+ }
+ // TODO(calin): we have similar computations in at lest 3 other places
+ // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
+ // use string append.
+ std::string apk_dir = dex_path.substr(0, dirIndex);
+ snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+ snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
+
+ if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
+ /*is_secondary_dex*/true, out_oat_path)) {
+ LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
+ return false;
+ }
+ return true;
+}
+
+// Reconcile the secondary dex 'dex_path' and its generated oat files.
+// Return true if all the parameters are valid and the secondary dex file was
+// processed successfully (i.e. the dex_path either exists, or if not, its corresponding
+// oat/vdex/art files where deleted successfully). In this case, out_secondary_dex_exists
+// will be true if the secondary dex file still exists. If the secondary dex file does not exist,
+// the method cleans up any previously generated compiler artifacts (oat, vdex, art).
+// Return false if there were errors during processing. In this case
+// out_secondary_dex_exists will be set to false.
+bool reconcile_secondary_dex_file(const std::string& dex_path,
+ const std::string& pkgname, int uid, const std::vector<std::string>& isas,
+ const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+ /*out*/bool* out_secondary_dex_exists) {
+ // Set out to false to start with, just in case we have validation errors.
+ *out_secondary_dex_exists = false;
+ if (isas.size() == 0) {
+ LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector";
+ return false;
+ }
+
+ const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+ if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+ uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ return false;
+ }
+
+ if (access(dex_path.c_str(), F_OK) == 0) {
+ // The path exists, nothing to do. The odex files (if any) will be left untouched.
+ *out_secondary_dex_exists = true;
+ return true;
+ } else if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path;
+ return false;
+ }
+
+ // The secondary dex does not exist anymore. Clear any generated files.
+ char oat_path[PKG_PATH_MAX];
+ char oat_dir[PKG_PATH_MAX];
+ char oat_isa_dir[PKG_PATH_MAX];
+ bool result = true;
+ for (size_t i = 0; i < isas.size(); i++) {
+ if (!create_secondary_dex_oat_layout(dex_path, isas[i], oat_dir, oat_isa_dir, oat_path)) {
+ LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
+ result = false;
+ continue;
+ }
+
+ // Delete oat/vdex/art files.
+ result = unlink_if_exists(oat_path) && result;
+ result = unlink_if_exists(create_vdex_filename(oat_path)) && result;
+ result = unlink_if_exists(create_image_filename(oat_path)) && result;
+
+ // Delete profiles.
+ std::string current_profile = create_current_profile_path(
+ multiuser_get_user_id(uid), dex_path, /*is_secondary*/true);
+ std::string reference_profile = create_reference_profile_path(
+ dex_path, /*is_secondary*/true);
+ result = unlink_if_exists(current_profile) && result;
+ result = unlink_if_exists(reference_profile) && result;
+
+ // Try removing the directories as well, they might be empty.
+ result = rmdir_if_empty(oat_isa_dir) && result;
+ result = rmdir_if_empty(oat_dir) && result;
+ }
+
+ return result;
+}
+
+// Helper for move_ab, so that we can have common failure-case cleanup.
+static bool unlink_and_rename(const char* from, const char* to) {
+ // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
+ // return a failure.
+ struct stat s;
+ if (stat(to, &s) == 0) {
+ if (!S_ISREG(s.st_mode)) {
+ LOG(ERROR) << from << " is not a regular file to replace for A/B.";
+ return false;
+ }
+ if (unlink(to) != 0) {
+ LOG(ERROR) << "Could not unlink " << to << " to move A/B.";
+ return false;
+ }
+ } else {
+ // This may be a permission problem. We could investigate the error code, but we'll just
+ // let the rename failure do the work for us.
+ }
+
+ // Try to rename "to" to "from."
+ if (rename(from, to) != 0) {
+ PLOG(ERROR) << "Could not rename " << from << " to " << to;
+ return false;
+ }
+ return true;
+}
+
+// Move/rename a B artifact (from) to an A artifact (to).
+static bool move_ab_path(const std::string& b_path, const std::string& a_path) {
+ // Check whether B exists.
+ {
+ struct stat s;
+ if (stat(b_path.c_str(), &s) != 0) {
+ // Silently ignore for now. The service calling this isn't smart enough to understand
+ // lack of artifacts at the moment.
+ return false;
+ }
+ if (!S_ISREG(s.st_mode)) {
+ LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
+ // Try to unlink, but swallow errors.
+ unlink(b_path.c_str());
+ return false;
+ }
+ }
+
+ // Rename B to A.
+ if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) {
+ // Delete the b_path so we don't try again (or fail earlier).
+ if (unlink(b_path.c_str()) != 0) {
+ PLOG(ERROR) << "Could not unlink " << b_path;
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+bool move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+ // Get the current slot suffix. No suffix, no A/B.
+ std::string slot_suffix;
+ {
+ char buf[kPropertyValueMax];
+ if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) {
+ return false;
+ }
+ slot_suffix = buf;
+
+ if (!ValidateTargetSlotSuffix(slot_suffix)) {
+ LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
+ return false;
+ }
+ }
+
+ // Validate other inputs.
+ if (validate_apk_path(apk_path) != 0) {
+ LOG(ERROR) << "Invalid apk_path: " << apk_path;
+ return false;
+ }
+ if (validate_apk_path(oat_dir) != 0) {
+ LOG(ERROR) << "Invalid oat_dir: " << oat_dir;
+ return false;
+ }
+
+ char a_path[PKG_PATH_MAX];
+ if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
+ return false;
+ }
+ const std::string a_vdex_path = create_vdex_filename(a_path);
+ const std::string a_image_path = create_image_filename(a_path);
+
+ // B path = A path + slot suffix.
+ const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str());
+ const std::string b_vdex_path = StringPrintf("%s.%s", a_vdex_path.c_str(), slot_suffix.c_str());
+ const std::string b_image_path = StringPrintf("%s.%s",
+ a_image_path.c_str(),
+ slot_suffix.c_str());
+
+ bool success = true;
+ if (move_ab_path(b_path, a_path)) {
+ if (move_ab_path(b_vdex_path, a_vdex_path)) {
+ // Note: we can live without an app image. As such, ignore failure to move the image file.
+ // If we decide to require the app image, or the app image being moved correctly,
+ // then change accordingly.
+ constexpr bool kIgnoreAppImageFailure = true;
+
+ if (!a_image_path.empty()) {
+ if (!move_ab_path(b_image_path, a_image_path)) {
+ unlink(a_image_path.c_str());
+ if (!kIgnoreAppImageFailure) {
+ success = false;
+ }
+ }
+ }
+ } else {
+ // Cleanup: delete B image, ignore errors.
+ unlink(b_image_path.c_str());
+ success = false;
+ }
+ } else {
+ // Cleanup: delete B image, ignore errors.
+ unlink(b_vdex_path.c_str());
+ unlink(b_image_path.c_str());
+ success = false;
+ }
+ return success;
+}
+
+bool delete_odex(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+ // Delete the oat/odex file.
+ char out_path[PKG_PATH_MAX];
+ if (!create_oat_out_path(apk_path, instruction_set, oat_dir,
+ /*is_secondary_dex*/false, out_path)) {
+ return false;
+ }
+
+ // In case of a permission failure report the issue. Otherwise just print a warning.
+ auto unlink_and_check = [](const char* path) -> bool {
+ int result = unlink(path);
+ if (result != 0) {
+ if (errno == EACCES || errno == EPERM) {
+ PLOG(ERROR) << "Could not unlink " << path;
+ return false;
+ }
+ PLOG(WARNING) << "Could not unlink " << path;
+ }
+ return true;
+ };
+
+ // Delete the oat/odex file.
+ bool return_value_oat = unlink_and_check(out_path);
+
+ // Derive and delete the app image.
+ bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
+
+ // Derive and delete the vdex file.
+ bool return_value_vdex = unlink_and_check(create_vdex_filename(out_path).c_str());
+
+ // Report success.
+ return return_value_oat && return_value_art && return_value_vdex;
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
new file mode 100644
index 0000000..355adb1
--- /dev/null
+++ b/cmds/installd/dexopt.h
@@ -0,0 +1,68 @@
+/*
+ * 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 DEXOPT_H_
+#define DEXOPT_H_
+
+#include <sys/types.h>
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace installd {
+
+/* dexopt needed flags matching those in dalvik.system.DexFile */
+static constexpr int NO_DEXOPT_NEEDED = 0;
+static constexpr int DEX2OAT_FROM_SCRATCH = 1;
+static constexpr int DEX2OAT_FOR_BOOT_IMAGE = 2;
+static constexpr int DEX2OAT_FOR_FILTER = 3;
+static constexpr int DEX2OAT_FOR_RELOCATION = 4;
+static constexpr int PATCHOAT_FOR_RELOCATION = 5;
+
+// Clear the reference profile for the primary apk of the given package.
+bool clear_primary_reference_profile(const std::string& pkgname);
+// Clear the current profile for the primary apk of the given package and user.
+bool clear_primary_current_profile(const std::string& pkgname, userid_t user);
+// Clear all current profile for the primary apk of the given package.
+bool clear_primary_current_profiles(const std::string& pkgname);
+
+bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
+
+// Decide if profile guided compilation is needed or not based on existing profiles.
+// The analysis is done for the primary apks (base + splits) of the given package.
+// Returns true if there is enough information in the current profiles that makes it
+// worth to recompile the package.
+// If the return value is true all the current profiles would have been merged into
+// the reference profiles accessible with open_reference_profile().
+bool analyze_primary_profiles(uid_t uid, const std::string& pkgname);
+
+bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths);
+
+bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
+
+bool reconcile_secondary_dex_file(const std::string& dex_path,
+ const std::string& pkgname, int uid, const std::vector<std::string>& isas,
+ const std::unique_ptr<std::string>& volumeUuid, int storage_flag,
+ /*out*/bool* out_secondary_dex_exists);
+
+int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
+ int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
+ const char* volume_uuid, const char* shared_libraries, const char* se_info);
+
+} // namespace installd
+} // namespace android
+
+#endif // DEXOPT_H_
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index 93e1ce5..edcdb6a 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -14,19 +14,17 @@
** limitations under the License.
*/
+#define LOG_TAG "installd"
+
#include <stdlib.h>
#include <string.h>
-#include <cutils/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>
#include <utils.h>
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-
namespace android {
namespace installd {
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 8f883db..35936a2 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -13,6 +13,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
+#define LOG_TAG "installd"
#include <fcntl.h>
#include <selinux/android.h>
@@ -20,30 +21,19 @@
#include <sys/capability.h>
#include <sys/fsuid.h>
#include <sys/prctl.h>
-#include <sys/socket.h>
#include <sys/stat.h>
#include <android-base/logging.h>
#include <cutils/fs.h>
-#include <cutils/log.h> // TODO: Move everything to base::logging.
#include <cutils/properties.h>
-#include <cutils/sockets.h>
+#include <log/log.h> // TODO: Move everything to base::logging.
#include <private/android_filesystem_config.h>
-#include <commands.h>
-#include <globals.h>
-#include <installd_constants.h>
-#include <installd_deps.h> // Need to fill in requirements of commands.
-#include <utils.h>
-
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-#define SOCKET_PATH "installd"
-
-#define BUFFER_MAX 1024 /* input buffer for commands */
-#define TOKEN_MAX 16 /* max number of arguments in buffer */
-#define REPLY_MAX 256 /* largest reply allowed */
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "installd_constants.h"
+#include "installd_deps.h" // Need to fill in requirements of commands.
+#include "utils.h"
namespace android {
namespace installd {
@@ -65,17 +55,17 @@
const char *oat_dir,
const char *apk_path,
const char *instruction_set) {
- char *file_name_start;
- char *file_name_end;
+ const char *file_name_start;
+ const char *file_name_end;
file_name_start = strrchr(apk_path, '/');
if (file_name_start == NULL) {
- ALOGE("apk_path '%s' has no '/'s in it\n", apk_path);
+ SLOGE("apk_path '%s' has no '/'s in it\n", apk_path);
return false;
}
file_name_end = strrchr(apk_path, '.');
if (file_name_end < file_name_start) {
- ALOGE("apk_path '%s' has no extension\n", apk_path);
+ SLOGE("apk_path '%s' has no extension\n", apk_path);
return false;
}
@@ -101,14 +91,14 @@
const char *instruction_set) {
if (strlen(apk_path) + strlen("oat/") + strlen(instruction_set)
+ strlen("/") + strlen("odex") + 1 > PKG_PATH_MAX) {
- ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
+ SLOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
return false;
}
strcpy(path, apk_path);
char *end = strrchr(path, '/');
if (end == NULL) {
- ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
+ SLOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
return false;
}
const char *apk_end = apk_path + (end - path); // strrchr(apk_path, '/');
@@ -118,7 +108,7 @@
strcat(path, apk_end); // path = /system/framework/oat/<isa>/whatever.jar\0
end = strrchr(path, '.');
if (end == NULL) {
- ALOGE("apk_path '%s' has no extension.\n", apk_path);
+ SLOGE("apk_path '%s' has no extension.\n", apk_path);
return false;
}
strcpy(end + 1, "odex");
@@ -151,12 +141,11 @@
return false;
}
- sprintf(path,"%s%s/%s/%s%s",
+ sprintf(path,"%s%s/%s/%s",
android_data_dir.path,
DALVIK_CACHE,
instruction_set,
- src + 1, /* skip the leading / */
- DALVIK_CACHE_POSTFIX);
+ src + 1 /* skip the leading / */);
char* tmp =
path +
@@ -171,407 +160,19 @@
}
}
+ strcat(path, DALVIK_CACHE_POSTFIX);
return true;
}
-
-static char* parse_null(char* arg) {
- if (strcmp(arg, "!") == 0) {
- return nullptr;
- } else {
- return arg;
- }
-}
-
-static int do_ping(char **arg ATTRIBUTE_UNUSED, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-
-static int do_create_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char *uuid, const char *pkgname, userid_t userid, int flags,
- appid_t appid, const char* seinfo, int target_sdk_version */
- return create_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]),
- atoi(arg[4]), arg[5], atoi(arg[6]));
-}
-
-static int do_restorecon_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char* uuid, const char* pkgName, userid_t userid, int flags,
- appid_t appid, const char* seinfo */
- return restorecon_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atoi(arg[4]), arg[5]);
-}
-
-static int do_migrate_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char *uuid, const char *pkgname, userid_t userid, int flags */
- return migrate_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
-}
-
-static int do_clear_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
- return clear_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
-}
-
-static int do_destroy_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
- return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
-}
-
-// We use otapreopt_chroot to get into the chroot.
-static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
-
-static int do_ota_dexopt(const char* args[DEXOPT_PARAM_COUNT],
- char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- // Time to fork and run otapreopt.
-
- // Check that the tool exists.
- struct stat s;
- if (stat(kOtaPreopt, &s) != 0) {
- LOG(ERROR) << "Otapreopt chroot tool not found.";
- return -1;
- }
-
- pid_t pid = fork();
- if (pid == 0) {
- const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
- argv[0] = kOtaPreopt;
-
- for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
- argv[i + 1] = args[i];
- }
-
- argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
-
- execv(argv[0], (char * const *)argv);
- PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
- exit(99);
- } else {
- int res = wait_child(pid);
- if (res == 0) {
- ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
- } else {
- ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
- }
- return res;
- }
-}
-
-static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT],
- char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- return dexopt(args);
-}
-
-using DexoptFn = int (*)(const char* args[DEXOPT_PARAM_COUNT],
- char reply[REPLY_MAX]);
-
-static int do_dexopt(char **arg, char reply[REPLY_MAX])
-{
- const char* args[DEXOPT_PARAM_COUNT];
- for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
- CHECK(arg[i] != nullptr);
- args[i] = arg[i];
- }
-
- int dexopt_flags = atoi(arg[6]);
- DexoptFn dexopt_fn;
- if ((dexopt_flags & DEXOPT_OTA) != 0) {
- dexopt_fn = do_ota_dexopt;
- } else {
- dexopt_fn = do_regular_dexopt;
- }
- return dexopt_fn(args, reply);
-}
-
-static int do_merge_profiles(char **arg, char reply[REPLY_MAX])
-{
- uid_t uid = static_cast<uid_t>(atoi(arg[0]));
- const char* pkgname = arg[1];
- if (merge_profiles(uid, pkgname)) {
- strncpy(reply, "true", REPLY_MAX);
- } else {
- strncpy(reply, "false", REPLY_MAX);
- }
- return 0;
-}
-
-static int do_dump_profiles(char **arg, char reply[REPLY_MAX])
-{
- uid_t uid = static_cast<uid_t>(atoi(arg[0]));
- const char* pkgname = arg[1];
- const char* dex_files = arg[2];
- if (dump_profile(uid, pkgname, dex_files)) {
- strncpy(reply, "true", REPLY_MAX);
- } else {
- strncpy(reply, "false", REPLY_MAX);
- }
- return 0;
-}
-
-static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return mark_boot_complete(arg[0] /* instruction set */);
-}
-
-static int do_rm_dex(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return rm_dex(arg[0], arg[1]); /* pkgname, instruction_set */
-}
-
-static int do_free_cache(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) /* TODO int:free_size */
-{
- return free_cache(parse_null(arg[0]), (int64_t)atoll(arg[1])); /* uuid, free_size */
-}
-
-static int do_get_app_size(char **arg, char reply[REPLY_MAX]) {
- int64_t codesize = 0;
- int64_t datasize = 0;
- int64_t cachesize = 0;
- int64_t asecsize = 0;
- int res = 0;
-
- /* const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
- const char* code_path */
- res = get_app_size(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]),
- arg[5], &codesize, &datasize, &cachesize, &asecsize);
-
- /*
- * Each int64_t can take up 22 characters printed out. Make sure it
- * doesn't go over REPLY_MAX in the future.
- */
- snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64,
- codesize, datasize, cachesize, asecsize);
- return res;
-}
-
-static int do_get_app_data_inode(char **arg, char reply[REPLY_MAX]) {
- ino_t inode = 0;
- int res = 0;
-
- /* const char *uuid, const char *pkgname, int userid, int flags */
- res = get_app_data_inode(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), &inode);
-
- snprintf(reply, REPLY_MAX, "%" PRId64, (int64_t) inode);
- return res;
-}
-
-static int do_move_complete_app(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char* from_uuid, const char *to_uuid, const char *package_name,
- const char *data_app_name, appid_t appid, const char* seinfo,
- int target_sdk_version */
- return move_complete_app(parse_null(arg[0]), parse_null(arg[1]), arg[2], arg[3],
- atoi(arg[4]), arg[5], atoi(arg[6]));
-}
-
-static int do_create_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* const char *uuid, userid_t userid, int user_serial, int flags */
- return create_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]), atoi(arg[3]));
-}
-
-static int do_destroy_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* const char *uuid, userid_t userid, int flags */
- return destroy_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]));
-}
-
-static int do_linklib(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return linklib(parse_null(arg[0]), arg[1], arg[2], atoi(arg[3]));
-}
-
-static int do_idmap(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return idmap(arg[0], arg[1], atoi(arg[2]));
-}
-
-static int do_create_oat_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* oat_dir, instruction_set */
- return create_oat_dir(arg[0], arg[1]);
-}
-
-static int do_rm_package_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* oat_dir */
- return rm_package_dir(arg[0]);
-}
-
-static int do_clear_app_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* package_name */
- return clear_app_profiles(arg[0]);
-}
-
-static int do_destroy_app_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* package_name */
- return destroy_app_profiles(arg[0]);
-}
-
-static int do_link_file(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* relative_path, from_base, to_base */
- return link_file(arg[0], arg[1], arg[2]);
-}
-
-static int do_move_ab(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- // apk_path, instruction_set, oat_dir
- return move_ab(arg[0], arg[1], arg[2]);
-}
-
-static int do_delete_odex(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- // apk_path, instruction_set, oat_dir
- return delete_odex(arg[0], arg[1], arg[2]) ? 0 : -1;
-}
-
-struct cmdinfo {
- const char *name;
- unsigned numargs;
- int (*func)(char **arg, char reply[REPLY_MAX]);
-};
-
-struct cmdinfo cmds[] = {
- { "ping", 0, do_ping },
-
- { "create_app_data", 7, do_create_app_data },
- { "restorecon_app_data", 6, do_restorecon_app_data },
- { "migrate_app_data", 4, do_migrate_app_data },
- { "clear_app_data", 5, do_clear_app_data },
- { "destroy_app_data", 5, do_destroy_app_data },
- { "move_complete_app", 7, do_move_complete_app },
- { "get_app_size", 6, do_get_app_size },
- { "get_app_data_inode", 4, do_get_app_data_inode },
-
- { "create_user_data", 4, do_create_user_data },
- { "destroy_user_data", 3, do_destroy_user_data },
-
- { "dexopt", 10, do_dexopt },
- { "markbootcomplete", 1, do_mark_boot_complete },
- { "rmdex", 2, do_rm_dex },
- { "freecache", 2, do_free_cache },
- { "linklib", 4, do_linklib },
- { "idmap", 3, do_idmap },
- { "createoatdir", 2, do_create_oat_dir },
- { "rmpackagedir", 1, do_rm_package_dir },
- { "clear_app_profiles", 1, do_clear_app_profiles },
- { "destroy_app_profiles", 1, do_destroy_app_profiles },
- { "linkfile", 3, do_link_file },
- { "move_ab", 3, do_move_ab },
- { "merge_profiles", 2, do_merge_profiles },
- { "dump_profiles", 3, do_dump_profiles },
- { "delete_odex", 3, do_delete_odex },
-};
-
-static int readx(int s, void *_buf, int count)
-{
- char *buf = (char *) _buf;
- int n = 0, r;
- if (count < 0) return -1;
- while (n < count) {
- r = read(s, buf + n, count - n);
- if (r < 0) {
- if (errno == EINTR) continue;
- ALOGE("read error: %s\n", strerror(errno));
- return -1;
- }
- if (r == 0) {
- ALOGE("eof\n");
- return -1; /* EOF */
- }
- n += r;
- }
- return 0;
-}
-
-static int writex(int s, const void *_buf, int count)
-{
- const char *buf = (const char *) _buf;
- int n = 0, r;
- if (count < 0) return -1;
- while (n < count) {
- r = write(s, buf + n, count - n);
- if (r < 0) {
- if (errno == EINTR) continue;
- ALOGE("write error: %s\n", strerror(errno));
- return -1;
- }
- n += r;
- }
- return 0;
-}
-
-
-/* Tokenize the command buffer, locate a matching command,
- * ensure that the required number of arguments are provided,
- * call the function(), return the result.
- */
-static int execute(int s, char cmd[BUFFER_MAX])
-{
- char reply[REPLY_MAX];
- char *arg[TOKEN_MAX+1];
- unsigned i;
- unsigned n = 0;
- unsigned short count;
- int ret = -1;
-
- // ALOGI("execute('%s')\n", cmd);
-
- /* default reply is "" */
- reply[0] = 0;
-
- /* n is number of args (not counting arg[0]) */
- arg[0] = cmd;
- while (*cmd) {
- if (isspace(*cmd)) {
- *cmd++ = 0;
- n++;
- arg[n] = cmd;
- if (n == TOKEN_MAX) {
- ALOGE("too many arguments\n");
- goto done;
- }
- }
- if (*cmd) {
- cmd++;
- }
- }
-
- for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
- if (!strcmp(cmds[i].name,arg[0])) {
- if (n != cmds[i].numargs) {
- ALOGE("%s requires %d arguments (%d given)\n",
- cmds[i].name, cmds[i].numargs, n);
- } else {
- ret = cmds[i].func(arg + 1, reply);
- }
- goto done;
- }
- }
- ALOGE("unsupported command '%s'\n", arg[0]);
-
-done:
- if (reply[0]) {
- n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
- } else {
- n = snprintf(cmd, BUFFER_MAX, "%d", ret);
- }
- if (n > BUFFER_MAX) n = BUFFER_MAX;
- count = n;
-
- // ALOGI("reply: '%s'\n", cmd);
- if (writex(s, &count, sizeof(count))) return -1;
- if (writex(s, cmd, count)) return -1;
- return 0;
-}
-
static bool initialize_globals() {
const char* data_path = getenv("ANDROID_DATA");
if (data_path == nullptr) {
- ALOGE("Could not find ANDROID_DATA");
+ SLOGE("Could not find ANDROID_DATA");
return false;
}
const char* root_path = getenv("ANDROID_ROOT");
if (root_path == nullptr) {
- ALOGE("Could not find ANDROID_ROOT");
+ SLOGE("Could not find ANDROID_ROOT");
return false;
}
@@ -597,12 +198,12 @@
}
if (ensure_config_user_dirs(0) == -1) {
- ALOGE("Failed to setup misc for user 0");
+ SLOGE("Failed to setup misc for user 0");
goto fail;
}
if (version == 2) {
- ALOGD("Upgrading to /data/misc/user directories");
+ SLOGD("Upgrading to /data/misc/user directories");
char misc_dir[PATH_MAX];
snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.path);
@@ -643,12 +244,12 @@
gid_t gid = uid;
if (access(keychain_added_dir, F_OK) == 0) {
if (copy_dir_files(keychain_added_dir, misc_added_dir, uid, gid) != 0) {
- ALOGE("Some files failed to copy");
+ SLOGE("Some files failed to copy");
}
}
if (access(keychain_removed_dir, F_OK) == 0) {
if (copy_dir_files(keychain_removed_dir, misc_removed_dir, uid, gid) != 0) {
- ALOGE("Some files failed to copy");
+ SLOGE("Some files failed to copy");
}
}
}
@@ -668,7 +269,7 @@
// Persist layout version if changed
if (version != oldVersion) {
if (fs_write_atomic_int(version_path, version) == -1) {
- ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
+ SLOGE("Failed to save version to %s: %s", version_path, strerror(errno));
goto fail;
}
}
@@ -702,80 +303,41 @@
}
static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
- char buf[BUFFER_MAX];
- struct sockaddr addr;
- socklen_t alen;
- int lsocket, s;
+ int ret;
int selinux_enabled = (is_selinux_enabled() > 0);
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(argv);
- ALOGI("installd firing up\n");
+ SLOGI("installd firing up");
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
if (!initialize_globals()) {
- ALOGE("Could not initialize globals; exiting.\n");
+ SLOGE("Could not initialize globals; exiting.\n");
exit(1);
}
if (initialize_directories() < 0) {
- ALOGE("Could not create directories; exiting.\n");
+ SLOGE("Could not create directories; exiting.\n");
exit(1);
}
if (selinux_enabled && selinux_status_open(true) < 0) {
- ALOGE("Could not open selinux status; exiting.\n");
+ SLOGE("Could not open selinux status; exiting.\n");
exit(1);
}
- lsocket = android_get_control_socket(SOCKET_PATH);
- if (lsocket < 0) {
- ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
+ if ((ret = InstalldNativeService::start()) != android::OK) {
+ SLOGE("Unable to start InstalldNativeService: %d", ret);
exit(1);
}
- if (listen(lsocket, 5)) {
- ALOGE("Listen on socket failed: %s\n", strerror(errno));
- exit(1);
- }
- fcntl(lsocket, F_SETFD, FD_CLOEXEC);
- for (;;) {
- alen = sizeof(addr);
- s = accept(lsocket, &addr, &alen);
- if (s < 0) {
- ALOGE("Accept failed: %s\n", strerror(errno));
- continue;
- }
- fcntl(s, F_SETFD, FD_CLOEXEC);
+ IPCThreadState::self()->joinThreadPool();
- ALOGI("new connection\n");
- for (;;) {
- unsigned short count;
- if (readx(s, &count, sizeof(count))) {
- ALOGE("failed to read size\n");
- break;
- }
- if ((count < 1) || (count >= BUFFER_MAX)) {
- ALOGE("invalid size %d\n", count);
- break;
- }
- if (readx(s, buf, count)) {
- ALOGE("failed to read command\n");
- break;
- }
- buf[count] = 0;
- if (selinux_enabled && selinux_status_updated() > 0) {
- selinux_android_seapp_context_reload();
- }
- if (execute(s, buf)) break;
- }
- ALOGI("closing connection\n");
- close(s);
- }
+ LOG(INFO) << "installd shutting down";
return 0;
}
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index 5e4c925..240aa49 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,3 +1,103 @@
+
service installd /system/bin/installd
class main
- socket installd stream 600 system system
+
+on early-boot
+ mkdir /config/sdcardfs/extensions/1055
+ mkdir /config/sdcardfs/extensions/1056
+ mkdir /config/sdcardfs/extensions/1057
+ mkdir /config/sdcardfs/extensions/1056/3gpp
+ mkdir /config/sdcardfs/extensions/1056/3gp
+ mkdir /config/sdcardfs/extensions/1056/3gpp2
+ mkdir /config/sdcardfs/extensions/1056/3g2
+ mkdir /config/sdcardfs/extensions/1056/avi
+ mkdir /config/sdcardfs/extensions/1056/dl
+ mkdir /config/sdcardfs/extensions/1056/dif
+ mkdir /config/sdcardfs/extensions/1056/dv
+ mkdir /config/sdcardfs/extensions/1056/fli
+ mkdir /config/sdcardfs/extensions/1056/m4v
+ mkdir /config/sdcardfs/extensions/1056/ts
+ mkdir /config/sdcardfs/extensions/1056/mpeg
+ mkdir /config/sdcardfs/extensions/1056/mpg
+ mkdir /config/sdcardfs/extensions/1056/mpe
+ mkdir /config/sdcardfs/extensions/1056/mp4
+ mkdir /config/sdcardfs/extensions/1056/vob
+ mkdir /config/sdcardfs/extensions/1056/qt
+ mkdir /config/sdcardfs/extensions/1056/mov
+ mkdir /config/sdcardfs/extensions/1056/mxu
+ mkdir /config/sdcardfs/extensions/1056/webm
+ mkdir /config/sdcardfs/extensions/1056/lsf
+ mkdir /config/sdcardfs/extensions/1056/lsx
+ mkdir /config/sdcardfs/extensions/1056/mkv
+ mkdir /config/sdcardfs/extensions/1056/mng
+ mkdir /config/sdcardfs/extensions/1056/asf
+ mkdir /config/sdcardfs/extensions/1056/asx
+ mkdir /config/sdcardfs/extensions/1056/wm
+ mkdir /config/sdcardfs/extensions/1056/wmv
+ mkdir /config/sdcardfs/extensions/1056/wmx
+ mkdir /config/sdcardfs/extensions/1056/wvx
+ mkdir /config/sdcardfs/extensions/1056/movie
+ mkdir /config/sdcardfs/extensions/1056/wrf
+ mkdir /config/sdcardfs/extensions/1057/bmp
+ mkdir /config/sdcardfs/extensions/1057/gif
+ mkdir /config/sdcardfs/extensions/1057/jpg
+ mkdir /config/sdcardfs/extensions/1057/jpeg
+ mkdir /config/sdcardfs/extensions/1057/jpe
+ mkdir /config/sdcardfs/extensions/1057/pcx
+ mkdir /config/sdcardfs/extensions/1057/png
+ mkdir /config/sdcardfs/extensions/1057/svg
+ mkdir /config/sdcardfs/extensions/1057/svgz
+ mkdir /config/sdcardfs/extensions/1057/tiff
+ mkdir /config/sdcardfs/extensions/1057/tif
+ mkdir /config/sdcardfs/extensions/1057/wbmp
+ mkdir /config/sdcardfs/extensions/1057/webp
+ mkdir /config/sdcardfs/extensions/1057/dng
+ mkdir /config/sdcardfs/extensions/1057/cr2
+ mkdir /config/sdcardfs/extensions/1057/ras
+ mkdir /config/sdcardfs/extensions/1057/art
+ mkdir /config/sdcardfs/extensions/1057/jng
+ mkdir /config/sdcardfs/extensions/1057/nef
+ mkdir /config/sdcardfs/extensions/1057/nrw
+ mkdir /config/sdcardfs/extensions/1057/orf
+ mkdir /config/sdcardfs/extensions/1057/rw2
+ mkdir /config/sdcardfs/extensions/1057/pef
+ mkdir /config/sdcardfs/extensions/1057/psd
+ mkdir /config/sdcardfs/extensions/1057/pnm
+ mkdir /config/sdcardfs/extensions/1057/pbm
+ mkdir /config/sdcardfs/extensions/1057/pgm
+ mkdir /config/sdcardfs/extensions/1057/ppm
+ mkdir /config/sdcardfs/extensions/1057/srw
+ mkdir /config/sdcardfs/extensions/1057/arw
+ mkdir /config/sdcardfs/extensions/1057/rgb
+ mkdir /config/sdcardfs/extensions/1057/xbm
+ mkdir /config/sdcardfs/extensions/1057/xpm
+ mkdir /config/sdcardfs/extensions/1057/xwd
+ mkdir /config/sdcardfs/extensions/1055/aac
+ mkdir /config/sdcardfs/extensions/1055/aac
+ mkdir /config/sdcardfs/extensions/1055/amr
+ mkdir /config/sdcardfs/extensions/1055/awb
+ mkdir /config/sdcardfs/extensions/1055/snd
+ mkdir /config/sdcardfs/extensions/1055/flac
+ mkdir /config/sdcardfs/extensions/1055/flac
+ mkdir /config/sdcardfs/extensions/1055/mp3
+ mkdir /config/sdcardfs/extensions/1055/mpga
+ mkdir /config/sdcardfs/extensions/1055/mpega
+ mkdir /config/sdcardfs/extensions/1055/mp2
+ mkdir /config/sdcardfs/extensions/1055/m4a
+ mkdir /config/sdcardfs/extensions/1055/aif
+ mkdir /config/sdcardfs/extensions/1055/aiff
+ mkdir /config/sdcardfs/extensions/1055/aifc
+ mkdir /config/sdcardfs/extensions/1055/gsm
+ mkdir /config/sdcardfs/extensions/1055/mka
+ mkdir /config/sdcardfs/extensions/1055/m3u
+ mkdir /config/sdcardfs/extensions/1055/wma
+ mkdir /config/sdcardfs/extensions/1055/wax
+ mkdir /config/sdcardfs/extensions/1055/ra
+ mkdir /config/sdcardfs/extensions/1055/rm
+ mkdir /config/sdcardfs/extensions/1055/ram
+ mkdir /config/sdcardfs/extensions/1055/ra
+ mkdir /config/sdcardfs/extensions/1055/pls
+ mkdir /config/sdcardfs/extensions/1055/sd2
+ mkdir /config/sdcardfs/extensions/1055/wav
+ mkdir /config/sdcardfs/extensions/1055/ogg
+ mkdir /config/sdcardfs/extensions/1055/oga
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 41732cc..6a81cfc 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -28,8 +28,7 @@
// This is used as a string literal, can't be constants. TODO: std::string...
#define DALVIK_CACHE "dalvik-cache"
-constexpr const char* DALVIK_CACHE_POSTFIX = "/classes.dex";
-constexpr const char* DALVIK_CACHE_POSTFIX2 = "@classes.dex";
+constexpr const char* DALVIK_CACHE_POSTFIX = "@classes.dex";
constexpr size_t PKG_NAME_MAX = 128u; /* largest allowed package name */
constexpr size_t PKG_PATH_MAX = 256u; /* max size of any path we use */
@@ -39,20 +38,32 @@
* frameworks/base/services/core/java/com/android/server/pm/Installer.java
***************************************************************************/
constexpr int DEXOPT_PUBLIC = 1 << 1;
-constexpr int DEXOPT_SAFEMODE = 1 << 2;
-constexpr int DEXOPT_DEBUGGABLE = 1 << 3;
-constexpr int DEXOPT_BOOTCOMPLETE = 1 << 4;
-constexpr int DEXOPT_PROFILE_GUIDED = 1 << 5;
-constexpr int DEXOPT_OTA = 1 << 6;
+constexpr int DEXOPT_DEBUGGABLE = 1 << 2;
+constexpr int DEXOPT_BOOTCOMPLETE = 1 << 3;
+constexpr int DEXOPT_PROFILE_GUIDED = 1 << 4;
+constexpr int DEXOPT_SECONDARY_DEX = 1 << 5;
+// DEXOPT_FORCE, DEXOPT_STORAGE_CE, DEXOPT_STORAGE_DE are exposed for secondary
+// dex files only. Primary apks are analyzed in PackageManager and installd
+// does not need to know if the compilation is forced or on what kind of storage
+// the dex files are.
+constexpr int DEXOPT_FORCE = 1 << 6;
+constexpr int DEXOPT_STORAGE_CE = 1 << 7;
+constexpr int DEXOPT_STORAGE_DE = 1 << 8;
/* all known values for dexopt flags */
constexpr int DEXOPT_MASK =
DEXOPT_PUBLIC
- | DEXOPT_SAFEMODE
| DEXOPT_DEBUGGABLE
| DEXOPT_BOOTCOMPLETE
| DEXOPT_PROFILE_GUIDED
- | DEXOPT_OTA;
+ | DEXOPT_SECONDARY_DEX
+ | DEXOPT_FORCE
+ | DEXOPT_STORAGE_CE
+ | DEXOPT_STORAGE_DE;
+
+// NOTE: keep in sync with StorageManager
+constexpr int FLAG_STORAGE_DE = 1 << 0;
+constexpr int FLAG_STORAGE_CE = 1 << 1;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
diff --git a/cmds/installd/matchgen.py b/cmds/installd/matchgen.py
new file mode 100644
index 0000000..131487d
--- /dev/null
+++ b/cmds/installd/matchgen.py
@@ -0,0 +1,93 @@
+#!/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, sys
+
+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"]
+}
+
+if "--rc" in sys.argv:
+ print "on early-boot"
+ print " mkdir /config/sdcardfs/extensions/1055"
+ print " mkdir /config/sdcardfs/extensions/1056"
+ print " mkdir /config/sdcardfs/extensions/1057"
+ for gid, exts in TYPES.iteritems():
+ if gid is "AID_MEDIA_AUDIO": gid = "1055"
+ if gid is "AID_MEDIA_VIDEO": gid = "1056"
+ if gid is "AID_MEDIA_IMAGE": gid = "1057"
+ for ext in exts:
+ print " mkdir /config/sdcardfs/extensions/%s/%s" % (gid, ext)
+ exit()
+
+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 5fa972a..68cb0d7 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -16,6 +16,7 @@
#include <algorithm>
#include <inttypes.h>
+#include <limits>
#include <random>
#include <regex>
#include <selinux/android.h>
@@ -32,17 +33,19 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/fs.h>
-#include <cutils/log.h>
#include <cutils/properties.h>
+#include <dex2oat_return_codes.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
-#include <commands.h>
-#include <file_parsing.h>
-#include <globals.h>
-#include <installd_deps.h> // Need to fill in requirements of commands.
-#include <otapreopt_utils.h>
-#include <system_properties.h>
-#include <utils.h>
+#include "dexopt.h"
+#include "file_parsing.h"
+#include "globals.h"
+#include "installd_constants.h"
+#include "installd_deps.h" // Need to fill in requirements of commands.
+#include "otapreopt_utils.h"
+#include "system_properties.h"
+#include "utils.h"
#ifndef LOG_TAG
#define LOG_TAG "otapreopt"
@@ -61,6 +64,25 @@
namespace android {
namespace installd {
+// Check expected values for dexopt flags. If you need to change this:
+//
+// RUN AN A/B OTA TO MAKE SURE THINGS STILL WORK!
+//
+// You most likely need to increase the protocol version and all that entails!
+
+static_assert(DEXOPT_PUBLIC == 1 << 1, "DEXOPT_PUBLIC unexpected.");
+static_assert(DEXOPT_DEBUGGABLE == 1 << 2, "DEXOPT_DEBUGGABLE unexpected.");
+static_assert(DEXOPT_BOOTCOMPLETE == 1 << 3, "DEXOPT_BOOTCOMPLETE unexpected.");
+static_assert(DEXOPT_PROFILE_GUIDED == 1 << 4, "DEXOPT_PROFILE_GUIDED unexpected.");
+static_assert(DEXOPT_SECONDARY_DEX == 1 << 5, "DEXOPT_SECONDARY_DEX unexpected.");
+static_assert(DEXOPT_FORCE == 1 << 6, "DEXOPT_FORCE unexpected.");
+static_assert(DEXOPT_STORAGE_CE == 1 << 7, "DEXOPT_STORAGE_CE unexpected.");
+static_assert(DEXOPT_STORAGE_DE == 1 << 8, "DEXOPT_STORAGE_DE unexpected.");
+
+static_assert(DEXOPT_MASK == 0x1fe, "DEXOPT_MASK unexpected.");
+
+
+
template<typename T>
static constexpr T RoundDown(T x, typename std::decay<T>::type n) {
return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n);
@@ -144,6 +166,20 @@
private:
+ struct Parameters {
+ const char *apk_path;
+ uid_t uid;
+ const char *pkgName;
+ const char *instruction_set;
+ int dexopt_needed;
+ const char* oat_dir;
+ int dexopt_flags;
+ const char* compiler_filter;
+ const char* volume_uuid;
+ const char* shared_libraries;
+ const char* se_info;
+ };
+
bool ReadSystemProperties() {
static constexpr const char* kPropertyFiles[] = {
"/default.prop", "/system/build.prop"
@@ -245,15 +281,23 @@
return true;
}
- bool ReadArguments(int argc ATTRIBUTE_UNUSED, char** argv) {
- // Expected command line:
- // target-slot dexopt {DEXOPT_PARAMETERS}
- // The DEXOPT_PARAMETERS are passed on to dexopt(), so we expect DEXOPT_PARAM_COUNT
- // of them. We store them in package_parameters_ (size checks are done when
- // parsing the special parameters and when copying into package_parameters_.
+ bool ParseUInt(const char* in, uint32_t* out) {
+ char* end;
+ long long int result = strtoll(in, &end, 0);
+ if (in == end || *end != '\0') {
+ return false;
+ }
+ if (result < std::numeric_limits<uint32_t>::min() ||
+ std::numeric_limits<uint32_t>::max() < result) {
+ return false;
+ }
+ *out = static_cast<uint32_t>(result);
+ return true;
+ }
- static_assert(DEXOPT_PARAM_COUNT == ARRAY_SIZE(package_parameters_),
- "Unexpected dexopt param count");
+ bool ReadArguments(int argc, char** argv) {
+ // Expected command line:
+ // target-slot [version] dexopt {DEXOPT_PARAMETERS}
const char* target_slot_arg = argv[1];
if (target_slot_arg == nullptr) {
@@ -267,28 +311,229 @@
return false;
}
- // Check for "dexopt" next.
+ // Check for version or "dexopt" next.
+ if (argv[2] == nullptr) {
+ LOG(ERROR) << "Missing parameters";
+ return false;
+ }
+
+ if (std::string("dexopt").compare(argv[2]) == 0) {
+ // This is version 1 (N) or pre-versioning version 2.
+ constexpr int kV2ArgCount = 1 // "otapreopt"
+ + 1 // slot
+ + 1 // "dexopt"
+ + 1 // apk_path
+ + 1 // uid
+ + 1 // pkg
+ + 1 // isa
+ + 1 // dexopt_needed
+ + 1 // oat_dir
+ + 1 // dexopt_flags
+ + 1 // filter
+ + 1 // volume
+ + 1 // libs
+ + 1; // seinfo
+ if (argc == kV2ArgCount) {
+ return ReadArgumentsV2(argc, argv, false);
+ } else {
+ return ReadArgumentsV1(argc, argv);
+ }
+ }
+
+ uint32_t version;
+ if (!ParseUInt(argv[2], &version)) {
+ LOG(ERROR) << "Could not parse version: " << argv[2];
+ return false;
+ }
+
+ switch (version) {
+ case 2:
+ return ReadArgumentsV2(argc, argv, true);
+
+ default:
+ LOG(ERROR) << "Unsupported version " << version;
+ return false;
+ }
+ }
+
+ bool ReadArgumentsV2(int argc ATTRIBUTE_UNUSED, char** argv, bool versioned) {
+ size_t dexopt_index = versioned ? 3 : 2;
+
+ // Check for "dexopt".
+ if (argv[dexopt_index] == nullptr) {
+ LOG(ERROR) << "Missing parameters";
+ return false;
+ }
+ if (std::string("dexopt").compare(argv[dexopt_index]) != 0) {
+ LOG(ERROR) << "Expected \"dexopt\"";
+ return false;
+ }
+
+ size_t param_index = 0;
+ for (;; ++param_index) {
+ const char* param = argv[dexopt_index + 1 + param_index];
+ if (param == nullptr) {
+ break;
+ }
+
+ switch (param_index) {
+ case 0:
+ package_parameters_.apk_path = param;
+ break;
+
+ case 1:
+ package_parameters_.uid = atoi(param);
+ break;
+
+ case 2:
+ package_parameters_.pkgName = param;
+ break;
+
+ case 3:
+ package_parameters_.instruction_set = param;
+ break;
+
+ case 4:
+ package_parameters_.dexopt_needed = atoi(param);
+ break;
+
+ case 5:
+ package_parameters_.oat_dir = param;
+ break;
+
+ case 6:
+ package_parameters_.dexopt_flags = atoi(param);
+ break;
+
+ case 7:
+ package_parameters_.compiler_filter = param;
+ break;
+
+ case 8:
+ package_parameters_.volume_uuid = ParseNull(param);
+ break;
+
+ case 9:
+ package_parameters_.shared_libraries = ParseNull(param);
+ break;
+
+ case 10:
+ package_parameters_.se_info = ParseNull(param);
+ break;
+
+ default:
+ LOG(ERROR) << "Too many arguments, got " << param;
+ return false;
+ }
+ }
+
+ if (param_index != 11) {
+ LOG(ERROR) << "Not enough parameters";
+ return false;
+ }
+
+ return true;
+ }
+
+ static int ReplaceMask(int input, int old_mask, int new_mask) {
+ return (input & old_mask) != 0 ? new_mask : 0;
+ }
+
+ bool ReadArgumentsV1(int argc ATTRIBUTE_UNUSED, char** argv) {
+ // Check for "dexopt".
if (argv[2] == nullptr) {
LOG(ERROR) << "Missing parameters";
return false;
}
if (std::string("dexopt").compare(argv[2]) != 0) {
- LOG(ERROR) << "Second parameter not dexopt: " << argv[2];
+ LOG(ERROR) << "Expected \"dexopt\"";
return false;
}
- // Copy the rest into package_parameters_, but be careful about over- and underflow.
- size_t index = 0;
- while (index < DEXOPT_PARAM_COUNT &&
- argv[index + 3] != nullptr) {
- package_parameters_[index] = argv[index + 3];
- index++;
+ size_t param_index = 0;
+ for (;; ++param_index) {
+ const char* param = argv[3 + param_index];
+ if (param == nullptr) {
+ break;
+ }
+
+ switch (param_index) {
+ case 0:
+ package_parameters_.apk_path = param;
+ break;
+
+ case 1:
+ package_parameters_.uid = atoi(param);
+ break;
+
+ case 2:
+ package_parameters_.pkgName = param;
+ break;
+
+ case 3:
+ package_parameters_.instruction_set = param;
+ break;
+
+ case 4: {
+ // Version 1 had:
+ // DEXOPT_DEX2OAT_NEEDED = 1
+ // DEXOPT_PATCHOAT_NEEDED = 2
+ // DEXOPT_SELF_PATCHOAT_NEEDED = 3
+ // We will simply use DEX2OAT_FROM_SCRATCH.
+ package_parameters_.dexopt_needed = DEX2OAT_FROM_SCRATCH;
+ break;
+ }
+
+ case 5:
+ package_parameters_.oat_dir = param;
+ break;
+
+ case 6: {
+ // Version 1 had:
+ constexpr int OLD_DEXOPT_PUBLIC = 1 << 1;
+ // Note: DEXOPT_SAFEMODE has been removed.
+ // constexpr int OLD_DEXOPT_SAFEMODE = 1 << 2;
+ constexpr int OLD_DEXOPT_DEBUGGABLE = 1 << 3;
+ constexpr int OLD_DEXOPT_BOOTCOMPLETE = 1 << 4;
+ constexpr int OLD_DEXOPT_PROFILE_GUIDED = 1 << 5;
+ constexpr int OLD_DEXOPT_OTA = 1 << 6;
+ int input = atoi(param);
+ package_parameters_.dexopt_flags =
+ ReplaceMask(input, OLD_DEXOPT_PUBLIC, DEXOPT_PUBLIC) |
+ ReplaceMask(input, OLD_DEXOPT_DEBUGGABLE, DEXOPT_DEBUGGABLE) |
+ ReplaceMask(input, OLD_DEXOPT_BOOTCOMPLETE, DEXOPT_BOOTCOMPLETE) |
+ ReplaceMask(input, OLD_DEXOPT_PROFILE_GUIDED, DEXOPT_PROFILE_GUIDED) |
+ ReplaceMask(input, OLD_DEXOPT_OTA, 0);
+ break;
+ }
+
+ case 7:
+ package_parameters_.compiler_filter = param;
+ break;
+
+ case 8:
+ package_parameters_.volume_uuid = ParseNull(param);
+ break;
+
+ case 9:
+ package_parameters_.shared_libraries = ParseNull(param);
+ break;
+
+ default:
+ LOG(ERROR) << "Too many arguments, got " << param;
+ return false;
+ }
}
- if (index != ARRAY_SIZE(package_parameters_) || argv[index + 3] != nullptr) {
- LOG(ERROR) << "Wrong number of parameters";
+
+ if (param_index != 10) {
+ LOG(ERROR) << "Not enough parameters";
return false;
}
+ // Set se_info to null. It is only relevant for secondary dex files, which we won't
+ // receive from a v1 A side.
+ package_parameters_.se_info = nullptr;
+
return true;
}
@@ -305,11 +550,11 @@
// Ensure that we have the right boot image. The first time any app is
// compiled, we'll try to generate it.
bool PrepareBootImage(bool force) const {
- if (package_parameters_[kISAIndex] == nullptr) {
+ if (package_parameters_.instruction_set == nullptr) {
LOG(ERROR) << "Instruction set missing.";
return false;
}
- const char* isa = package_parameters_[kISAIndex];
+ const char* isa = package_parameters_.instruction_set;
// Check whether the file exists where expected.
std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE;
@@ -508,6 +753,10 @@
}
static const char* ParseNull(const char* arg) {
+ // b/38186355. Revert soon.
+ if (strcmp(arg, "!null") == 0) {
+ return nullptr;
+ }
return (strcmp(arg, "!") == 0) ? nullptr : arg;
}
@@ -535,14 +784,12 @@
// (This is ugly as it's the only thing where we need to understand the contents
// of package_parameters_, but it beats postponing the decision or using the call-
// backs to do weird things.)
- constexpr size_t kApkPathIndex = 0;
- CHECK_GT(DEXOPT_PARAM_COUNT, kApkPathIndex);
- CHECK(package_parameters_[kApkPathIndex] != nullptr);
- if (StartsWith(package_parameters_[kApkPathIndex], android_root_.c_str())) {
- const char* last_slash = strrchr(package_parameters_[kApkPathIndex], '/');
+ const char* apk_path = package_parameters_.apk_path;
+ CHECK(apk_path != nullptr);
+ if (StartsWith(apk_path, android_root_.c_str())) {
+ const char* last_slash = strrchr(apk_path, '/');
if (last_slash != nullptr) {
- std::string path(package_parameters_[kApkPathIndex],
- last_slash - package_parameters_[kApkPathIndex] + 1);
+ std::string path(apk_path, last_slash - apk_path + 1);
CHECK(EndsWith(path, "/"));
path = path + "oat";
if (access(path.c_str(), F_OK) == 0) {
@@ -556,36 +803,64 @@
// partition will not be available and fail to build. This is problematic, as
// this tool will wipe the OTA artifact cache and try again (for robustness after
// a failed OTA with remaining cache artifacts).
- if (access(package_parameters_[kApkPathIndex], F_OK) != 0) {
- LOG(WARNING) << "Skipping preopt of non-existing package "
- << package_parameters_[kApkPathIndex];
+ if (access(apk_path, F_OK) != 0) {
+ LOG(WARNING) << "Skipping preopt of non-existing package " << apk_path;
return true;
}
return false;
}
+ // Run dexopt with the parameters of package_parameters_.
+ int Dexopt() {
+ return dexopt(package_parameters_.apk_path,
+ package_parameters_.uid,
+ package_parameters_.pkgName,
+ package_parameters_.instruction_set,
+ package_parameters_.dexopt_needed,
+ package_parameters_.oat_dir,
+ package_parameters_.dexopt_flags,
+ package_parameters_.compiler_filter,
+ package_parameters_.volume_uuid,
+ package_parameters_.shared_libraries,
+ package_parameters_.se_info);
+ }
+
int RunPreopt() {
if (ShouldSkipPreopt()) {
return 0;
}
- int dexopt_result = dexopt(package_parameters_);
+ int dexopt_result = Dexopt();
if (dexopt_result == 0) {
return 0;
}
// If the dexopt failed, we may have a stale boot image from a previous OTA run.
- // Try to delete and retry.
+ // Then regenerate and retry.
+ if (WEXITSTATUS(dexopt_result) ==
+ static_cast<int>(art::dex2oat::ReturnCode::kCreateRuntime)) {
+ if (!PrepareBootImage(/* force */ true)) {
+ LOG(ERROR) << "Forced boot image creating failed. Original error return was "
+ << dexopt_result;
+ return dexopt_result;
+ }
- if (!PrepareBootImage(/* force */ true)) {
- LOG(ERROR) << "Forced boot image creating failed. Original error return was "
- << dexopt_result;
+ int dexopt_result_boot_image_retry = Dexopt();
+ if (dexopt_result_boot_image_retry == 0) {
+ return 0;
+ }
+ }
+
+ // If this was a profile-guided run, we may have profile version issues. Try to downgrade,
+ // if possible.
+ if ((package_parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
return dexopt_result;
}
- LOG(WARNING) << "Original dexopt failed, re-trying after boot image was regenerated.";
- return dexopt(package_parameters_);
+ LOG(WARNING) << "Downgrading compiler filter in an attempt to progress compilation";
+ package_parameters_.dexopt_flags &= ~DEXOPT_PROFILE_GUIDED;
+ return Dexopt();
}
////////////////////////////////////
@@ -715,7 +990,7 @@
std::string boot_classpath_;
std::string asec_mountpoint_;
- const char* package_parameters_[DEXOPT_PARAM_COUNT];
+ Parameters package_parameters_;
// Store environment values we need to set.
std::vector<std::string> environ_;
@@ -823,7 +1098,7 @@
DALVIK_CACHE,
instruction_set,
from_src.c_str(),
- DALVIK_CACHE_POSTFIX2);
+ DALVIK_CACHE_POSTFIX);
if (assembled_path.length() + 1 > PKG_PATH_MAX) {
return false;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 5ea89e6..2030997 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -25,8 +25,8 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
-#include <commands.h>
-#include <otapreopt_utils.h>
+#include "installd_constants.h"
+#include "otapreopt_utils.h"
#ifndef LOG_TAG
#define LOG_TAG "otapreopt"
@@ -136,44 +136,18 @@
// Now go on and run otapreopt.
- // Incoming: cmd + status-fd + target-slot + "dexopt" + dexopt-params + null
- // Outgoing: cmd + target-slot + "dexopt" + dexopt-params + null
- constexpr size_t kInArguments = 1 // Binary name.
- + 1 // status file descriptor.
- + 1 // target-slot.
- + 1 // "dexopt."
- + DEXOPT_PARAM_COUNT // dexopt parameters.
- + 1; // null termination.
- constexpr size_t kOutArguments = 1 // Binary name.
- + 1 // target-slot.
- + 1 // "dexopt."
- + DEXOPT_PARAM_COUNT // dexopt parameters.
- + 1; // null termination.
- const char* argv[kOutArguments];
- if (static_cast<size_t>(argc) != kInArguments - 1 /* null termination */) {
- LOG(ERROR) << "Unexpected argument size "
- << argc
- << " vs "
- << (kInArguments - 1);
- for (size_t i = 0; i < static_cast<size_t>(argc); ++i) {
- if (arg[i] == nullptr) {
- LOG(ERROR) << "(null)";
- } else {
- LOG(ERROR) << "\"" << arg[i] << "\"";
- }
- }
- exit(206);
- }
+ // Incoming: cmd + status-fd + target-slot + cmd... + null | Incoming | = argc + 1
+ // Outgoing: cmd + target-slot + cmd... + null | Outgoing | = argc
+ const char** argv = new const char*[argc];
+
argv[0] = "/system/bin/otapreopt";
// The first parameter is the status file descriptor, skip.
-
- for (size_t i = 1; i <= kOutArguments - 2 /* cmd + null */; ++i) {
- argv[i] = arg[i + 1];
+ for (size_t i = 2; i <= static_cast<size_t>(argc); ++i) {
+ argv[i - 1] = arg[i];
}
- argv[kOutArguments - 1] = nullptr;
- execv(argv[0], (char * const *)argv);
+ execv(argv[0], static_cast<char * const *>(const_cast<char**>(argv)));
PLOG(ERROR) << "execv(OTAPREOPT) failed.";
exit(99);
}
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
new file mode 100644
index 0000000..630c1f3
--- /dev/null
+++ b/cmds/installd/tests/Android.bp
@@ -0,0 +1,54 @@
+// Build the unit tests for installd
+cc_test {
+ name: "installd_utils_test",
+ clang: true,
+ srcs: ["installd_utils_test.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libutils",
+ "libcutils",
+ ],
+ static_libs: [
+ "libinstalld",
+ "libdiskusage",
+ ],
+}
+
+cc_test {
+ name: "installd_cache_test",
+ clang: true,
+ srcs: ["installd_cache_test.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "liblogwrap",
+ "libselinux",
+ "libutils",
+ ],
+ static_libs: [
+ "libinstalld",
+ "libdiskusage",
+ ],
+}
+
+cc_test {
+ name: "installd_service_test",
+ clang: true,
+ srcs: ["installd_service_test.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "liblogwrap",
+ "libselinux",
+ "libutils",
+ ],
+ static_libs: [
+ "libinstalld",
+ "libdiskusage",
+ ],
+}
diff --git a/cmds/installd/tests/Android.mk b/cmds/installd/tests/Android.mk
deleted file mode 100644
index 38a9f69..0000000
--- a/cmds/installd/tests/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Build the unit tests for installd
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-# Build the unit tests.
-test_src_files := \
- installd_utils_test.cpp
-
-shared_libraries := \
- libbase \
- libutils \
- libcutils \
-
-static_libraries := \
- libinstalld \
- libdiskusage \
-
-c_includes := \
- frameworks/native/cmds/installd
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_C_INCLUDES := $(c_includes)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval LOCAL_CLANG := true) \
- $(eval include $(BUILD_NATIVE_TEST)) \
-)
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
new file mode 100644
index 0000000..aed068c
--- /dev/null
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -0,0 +1,332 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/statvfs.h>
+#include <sys/xattr.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+constexpr const char* kTestUuid = "TEST";
+
+constexpr int64_t kKbInBytes = 1024;
+constexpr int64_t kMbInBytes = 1024 * kKbInBytes;
+constexpr int64_t kGbInBytes = 1024 * kMbInBytes;
+constexpr int64_t kTbInBytes = 1024 * kGbInBytes;
+
+static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
+static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
+
+int get_property(const char *key, char *value, const char *default_value) {
+ return property_get(key, value, default_value);
+}
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+ const char *oat_dir ATTRIBUTE_UNUSED,
+ const char *apk_path ATTRIBUTE_UNUSED,
+ const char *instruction_set ATTRIBUTE_UNUSED) {
+ return false;
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+ const char *apk_path ATTRIBUTE_UNUSED,
+ const char *instruction_set ATTRIBUTE_UNUSED) {
+ return false;
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+ const char *src ATTRIBUTE_UNUSED,
+ const char *instruction_set ATTRIBUTE_UNUSED) {
+ return false;
+}
+
+static void mkdir(const char* path) {
+ const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+ ::mkdir(fullPath, 0755);
+}
+
+static void touch(const char* path, int len, int time) {
+ const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+ int fd = ::open(fullPath, O_RDWR | O_CREAT, 0644);
+ ::fallocate(fd, 0, 0, len);
+ ::close(fd);
+ struct utimbuf times;
+ times.actime = times.modtime = std::time(0) + time;
+ ::utime(fullPath, ×);
+}
+
+static int exists(const char* path) {
+ const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+ return ::access(fullPath, F_OK);
+}
+
+static int64_t size(const char* path) {
+ const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+ struct stat buf;
+ if (!stat(fullPath, &buf)) {
+ return buf.st_size;
+ } else {
+ return -1;
+ }
+}
+
+static int64_t free() {
+ struct statvfs buf;
+ if (!statvfs("/data/local/tmp", &buf)) {
+ return buf.f_bavail * buf.f_frsize;
+ } else {
+ PLOG(ERROR) << "Failed to statvfs";
+ return -1;
+ }
+}
+
+static void setxattr(const char* path, const char* key) {
+ const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+ ::setxattr(fullPath, key, "", 0, 0);
+}
+
+class CacheTest : public testing::Test {
+protected:
+ InstalldNativeService* service;
+ std::unique_ptr<std::string> testUuid;
+
+ virtual void SetUp() {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(nullptr);
+
+ service = new InstalldNativeService();
+ testUuid = std::make_unique<std::string>();
+ *testUuid = std::string(kTestUuid);
+ system("mkdir -p /data/local/tmp/user/0");
+ }
+
+ virtual void TearDown() {
+ delete service;
+ system("rm -rf /data/local/tmp/user");
+ }
+};
+
+TEST_F(CacheTest, FreeCache_All) {
+ LOG(INFO) << "FreeCache_All";
+
+ mkdir("com.example");
+ touch("com.example/normal", 1 * kMbInBytes, 60);
+ mkdir("com.example/cache");
+ mkdir("com.example/cache/foo");
+ touch("com.example/cache/foo/one", 1 * kMbInBytes, 60);
+ touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
+
+ EXPECT_EQ(0, exists("com.example/normal"));
+ EXPECT_EQ(0, exists("com.example/cache/foo/one"));
+ EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+ service->freeCache(testUuid, kTbInBytes, 0,
+ FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+ EXPECT_EQ(0, exists("com.example/normal"));
+ EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+ EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
+}
+
+TEST_F(CacheTest, FreeCache_Age) {
+ LOG(INFO) << "FreeCache_Age";
+
+ mkdir("com.example");
+ mkdir("com.example/cache");
+ mkdir("com.example/cache/foo");
+ touch("com.example/cache/foo/one", kMbInBytes, 60);
+ touch("com.example/cache/foo/two", kMbInBytes, 120);
+
+ service->freeCache(testUuid, free() + kKbInBytes, 0,
+ FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+ EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+ EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+ service->freeCache(testUuid, free() + kKbInBytes, 0,
+ FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+ EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+ EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
+}
+
+TEST_F(CacheTest, FreeCache_Tombstone) {
+ LOG(INFO) << "FreeCache_Tombstone";
+
+ mkdir("com.example");
+ mkdir("com.example/cache");
+ mkdir("com.example/cache/foo");
+ touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
+ touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 60);
+ mkdir("com.example/cache/bar");
+ touch("com.example/cache/bar/bar1", 2 * kMbInBytes, 120);
+ touch("com.example/cache/bar/bar2", 2 * kMbInBytes, 120);
+
+ setxattr("com.example/cache/bar", "user.cache_tombstone");
+
+ EXPECT_EQ(0, exists("com.example/cache/foo/foo1"));
+ EXPECT_EQ(0, exists("com.example/cache/foo/foo2"));
+ EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
+ EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
+ EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
+ EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
+
+ service->freeCache(testUuid, kTbInBytes, 0,
+ FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+ EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
+ EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
+ EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
+ EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
+ EXPECT_EQ(0, size("com.example/cache/bar/bar1"));
+ EXPECT_EQ(0, size("com.example/cache/bar/bar2"));
+}
+
+TEST_F(CacheTest, FreeCache_Group) {
+ LOG(INFO) << "FreeCache_Group";
+
+ mkdir("com.example");
+ mkdir("com.example/cache");
+ mkdir("com.example/cache/foo");
+ touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
+ touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 120);
+
+ setxattr("com.example/cache/foo", "user.cache_group");
+
+ service->freeCache(testUuid, free() + kKbInBytes, 0,
+ FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+ EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
+ EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
+}
+
+TEST_F(CacheTest, FreeCache_GroupTombstone) {
+ LOG(INFO) << "FreeCache_GroupTombstone";
+
+ mkdir("com.example");
+ mkdir("com.example/cache");
+
+ // this dir must look really old for some reason?
+ mkdir("com.example/cache/group");
+ touch("com.example/cache/group/file1", kMbInBytes, 120);
+ touch("com.example/cache/group/file2", kMbInBytes, 120);
+ mkdir("com.example/cache/group/dir");
+ touch("com.example/cache/group/dir/file1", kMbInBytes, 120);
+ touch("com.example/cache/group/dir/file2", kMbInBytes, 120);
+ mkdir("com.example/cache/group/tomb");
+ touch("com.example/cache/group/tomb/file1", kMbInBytes, 120);
+ touch("com.example/cache/group/tomb/file2", kMbInBytes, 120);
+ mkdir("com.example/cache/group/tomb/dir");
+ touch("com.example/cache/group/tomb/dir/file1", kMbInBytes, 120);
+ touch("com.example/cache/group/tomb/dir/file2", kMbInBytes, 120);
+
+ mkdir("com.example/cache/tomb");
+ touch("com.example/cache/tomb/file1", kMbInBytes, 240);
+ touch("com.example/cache/tomb/file2", kMbInBytes, 240);
+ mkdir("com.example/cache/tomb/dir");
+ touch("com.example/cache/tomb/dir/file1", kMbInBytes, 240);
+ touch("com.example/cache/tomb/dir/file2", kMbInBytes, 240);
+ mkdir("com.example/cache/tomb/group");
+ touch("com.example/cache/tomb/group/file1", kMbInBytes, 60);
+ touch("com.example/cache/tomb/group/file2", kMbInBytes, 60);
+ mkdir("com.example/cache/tomb/group/dir");
+ touch("com.example/cache/tomb/group/dir/file1", kMbInBytes, 60);
+ touch("com.example/cache/tomb/group/dir/file2", kMbInBytes, 60);
+
+ setxattr("com.example/cache/group", "user.cache_group");
+ setxattr("com.example/cache/group/tomb", "user.cache_tombstone");
+ setxattr("com.example/cache/tomb", "user.cache_tombstone");
+ setxattr("com.example/cache/tomb/group", "user.cache_group");
+
+ service->freeCache(testUuid, free() + kKbInBytes, 0,
+ FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file2"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file1"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file2"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file1"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file2"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file1"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file2"));
+
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
+
+ service->freeCache(testUuid, free() + kKbInBytes, 0,
+ FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+ EXPECT_EQ(-1, size("com.example/cache/group/file1"));
+ EXPECT_EQ(-1, size("com.example/cache/group/file2"));
+ EXPECT_EQ(-1, size("com.example/cache/group/dir/file1"));
+ EXPECT_EQ(-1, size("com.example/cache/group/dir/file2"));
+ EXPECT_EQ(0, size("com.example/cache/group/tomb/file1"));
+ EXPECT_EQ(0, size("com.example/cache/group/tomb/file2"));
+ EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1"));
+ EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2"));
+
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
+ EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
+
+ service->freeCache(testUuid, kTbInBytes, 0,
+ FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+ EXPECT_EQ(-1, size("com.example/cache/group/file1"));
+ EXPECT_EQ(-1, size("com.example/cache/group/file2"));
+ EXPECT_EQ(-1, size("com.example/cache/group/dir/file1"));
+ EXPECT_EQ(-1, size("com.example/cache/group/dir/file2"));
+ EXPECT_EQ(0, size("com.example/cache/group/tomb/file1"));
+ EXPECT_EQ(0, size("com.example/cache/group/tomb/file2"));
+ EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1"));
+ EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2"));
+
+ EXPECT_EQ(0, size("com.example/cache/tomb/file1"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/file2"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/dir/file1"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/dir/file2"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
+ EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
new file mode 100644
index 0000000..34818f6
--- /dev/null
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/statvfs.h>
+#include <sys/xattr.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+constexpr const char* kTestUuid = "TEST";
+
+static constexpr int FLAG_FORCE = 1 << 16;
+
+int get_property(const char *key, char *value, const char *default_value) {
+ return property_get(key, value, default_value);
+}
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+ const char *oat_dir ATTRIBUTE_UNUSED,
+ const char *apk_path ATTRIBUTE_UNUSED,
+ const char *instruction_set ATTRIBUTE_UNUSED) {
+ return false;
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+ const char *apk_path ATTRIBUTE_UNUSED,
+ const char *instruction_set ATTRIBUTE_UNUSED) {
+ return false;
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX],
+ const char *src,
+ const char *instruction_set) {
+ // Not really a valid path but it's good enough for testing.
+ sprintf(path,"/data/dalvik-cache/%s/%s", instruction_set, src);
+ return true;
+}
+
+static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
+ const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+ ::mkdir(fullPath, mode);
+ ::chown(fullPath, owner, group);
+ ::chmod(fullPath, mode);
+}
+
+static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
+ int fd = ::open(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(),
+ O_RDWR | O_CREAT, mode);
+ ::fchown(fd, owner, group);
+ ::fchmod(fd, mode);
+ ::close(fd);
+}
+
+static int stat_gid(const char* path) {
+ struct stat buf;
+ ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf);
+ return buf.st_gid;
+}
+
+static int stat_mode(const char* path) {
+ struct stat buf;
+ ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf);
+ return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
+}
+
+class ServiceTest : public testing::Test {
+protected:
+ InstalldNativeService* service;
+ std::unique_ptr<std::string> testUuid;
+
+ virtual void SetUp() {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(nullptr);
+
+ service = new InstalldNativeService();
+ testUuid = std::make_unique<std::string>();
+ *testUuid = std::string(kTestUuid);
+ system("mkdir -p /data/local/tmp/user/0");
+ }
+
+ virtual void TearDown() {
+ delete service;
+ system("rm -rf /data/local/tmp/user");
+ }
+};
+
+TEST_F(ServiceTest, FixupAppData_Upgrade) {
+ LOG(INFO) << "FixupAppData_Upgrade";
+
+ mkdir("com.example", 10000, 10000, 0700);
+ mkdir("com.example/normal", 10000, 10000, 0700);
+ mkdir("com.example/cache", 10000, 10000, 0700);
+ touch("com.example/cache/file", 10000, 10000, 0700);
+
+ service->fixupAppData(testUuid, 0);
+
+ EXPECT_EQ(10000, stat_gid("com.example/normal"));
+ EXPECT_EQ(20000, stat_gid("com.example/cache"));
+ EXPECT_EQ(20000, stat_gid("com.example/cache/file"));
+
+ EXPECT_EQ(0700, stat_mode("com.example/normal"));
+ EXPECT_EQ(02771, stat_mode("com.example/cache"));
+ EXPECT_EQ(0700, stat_mode("com.example/cache/file"));
+}
+
+TEST_F(ServiceTest, FixupAppData_Moved) {
+ LOG(INFO) << "FixupAppData_Moved";
+
+ mkdir("com.example", 10000, 10000, 0700);
+ mkdir("com.example/foo", 10000, 10000, 0700);
+ touch("com.example/foo/file", 10000, 20000, 0700);
+ mkdir("com.example/bar", 10000, 20000, 0700);
+ touch("com.example/bar/file", 10000, 20000, 0700);
+
+ service->fixupAppData(testUuid, 0);
+
+ EXPECT_EQ(10000, stat_gid("com.example/foo"));
+ EXPECT_EQ(20000, stat_gid("com.example/foo/file"));
+ EXPECT_EQ(10000, stat_gid("com.example/bar"));
+ EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+
+ service->fixupAppData(testUuid, FLAG_FORCE);
+
+ EXPECT_EQ(10000, stat_gid("com.example/foo"));
+ EXPECT_EQ(10000, stat_gid("com.example/foo/file"));
+ EXPECT_EQ(10000, stat_gid("com.example/bar"));
+ EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+}
+
+TEST_F(ServiceTest, RmDexNoDalvikCache) {
+ LOG(INFO) << "RmDexNoDalvikCache";
+
+ // Try to remove a non existing dalvik cache dex. The call should be
+ // successful because there's nothing to remove.
+ EXPECT_TRUE(service->rmdex("com.example", "arm").isOk());
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 9b2de88..dab3236 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -19,9 +19,9 @@
#include <gtest/gtest.h>
-#include <commands.h>
-#include <globals.h>
-#include <utils.h>
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
#undef LOG_TAG
#define LOG_TAG "utils_test"
@@ -29,12 +29,15 @@
#define TEST_DATA_DIR "/data/"
#define TEST_APP_DIR "/data/app/"
#define TEST_APP_PRIVATE_DIR "/data/app-private/"
+#define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/"
#define TEST_ASEC_DIR "/mnt/asec/"
#define TEST_EXPAND_DIR "/mnt/expand/"
#define TEST_SYSTEM_DIR1 "/system/app/"
#define TEST_SYSTEM_DIR2 "/vendor/app/"
+#define TEST_PROFILE_DIR "/data/misc/profiles"
+
#define REALLY_LONG_APP_NAME "com.example." \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
@@ -57,6 +60,9 @@
android_app_private_dir.path = (char*) TEST_APP_PRIVATE_DIR;
android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR);
+ android_app_ephemeral_dir.path = (char*) TEST_APP_EPHEMERAL_DIR;
+ android_app_ephemeral_dir.len = strlen(TEST_APP_EPHEMERAL_DIR);
+
android_data_dir.path = (char*) TEST_DATA_DIR;
android_data_dir.len = strlen(TEST_DATA_DIR);
@@ -74,6 +80,9 @@
android_system_dirs.dirs[1].path = (char*) TEST_SYSTEM_DIR2;
android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2);
+
+ android_profiles_dir.path = (char*) TEST_PROFILE_DIR;
+ android_profiles_dir.len = strlen(TEST_PROFILE_DIR);
}
virtual void TearDown() {
@@ -85,19 +94,19 @@
// Bad prefixes directories
const char *badprefix1 = "/etc/passwd";
EXPECT_EQ(-1, validate_apk_path(badprefix1))
- << badprefix1 << " should be allowed as a valid path";
+ << badprefix1 << " should not be allowed as a valid path";
const char *badprefix2 = "../.." TEST_APP_DIR "../../../blah";
EXPECT_EQ(-1, validate_apk_path(badprefix2))
- << badprefix2 << " should be allowed as a valid path";
+ << badprefix2 << " should not be allowed as a valid path";
const char *badprefix3 = "init.rc";
EXPECT_EQ(-1, validate_apk_path(badprefix3))
- << badprefix3 << " should be allowed as a valid path";
+ << badprefix3 << " should not be allowed as a valid path";
const char *badprefix4 = "/init.rc";
EXPECT_EQ(-1, validate_apk_path(badprefix4))
- << badprefix4 << " should be allowed as a valid path";
+ << badprefix4 << " should not be allowed as a valid path";
}
TEST_F(UtilsTest, IsValidApkPath_Internal) {
@@ -317,6 +326,7 @@
size_t pkgnameSize = PKG_NAME_MAX;
char pkgname[pkgnameSize + 1];
memset(pkgname, 'a', pkgnameSize);
+ pkgname[1] = '.';
pkgname[pkgnameSize] = '\0';
EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0))
@@ -329,19 +339,6 @@
<< "Package path should be a really long string of a's";
}
-TEST_F(UtilsTest, CreatePkgPath_LongPkgNameFail) {
- char path[PKG_PATH_MAX];
-
- // Create long packagename of "aaaaa..."
- size_t pkgnameSize = PKG_NAME_MAX + 1;
- char pkgname[pkgnameSize + 1];
- memset(pkgname, 'a', pkgnameSize);
- pkgname[pkgnameSize] = '\0';
-
- EXPECT_EQ(-1, create_pkg_path(path, pkgname, "", 0))
- << "Should return error because package name is too long.";
-}
-
TEST_F(UtilsTest, CreatePkgPath_LongPostfixFail) {
char path[PKG_PATH_MAX];
@@ -508,5 +505,71 @@
create_data_user_ce_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10, "com.example"));
}
+TEST_F(UtilsTest, IsValidPackageName) {
+ EXPECT_EQ(true, is_valid_package_name("android"));
+ EXPECT_EQ(true, is_valid_package_name("com.example"));
+ EXPECT_EQ(true, is_valid_package_name("com.example-1"));
+ EXPECT_EQ(true, is_valid_package_name("com.example-1024"));
+ EXPECT_EQ(true, is_valid_package_name("com.example.foo---KiJFj4a_tePVw95pSrjg=="));
+ EXPECT_EQ(true, is_valid_package_name("really_LONG.a1234.package_name"));
+
+ EXPECT_EQ(false, is_valid_package_name("1234.package"));
+ EXPECT_EQ(false, is_valid_package_name("com.1234.package"));
+ EXPECT_EQ(false, is_valid_package_name(""));
+ EXPECT_EQ(false, is_valid_package_name("."));
+ EXPECT_EQ(false, is_valid_package_name(".."));
+ EXPECT_EQ(false, is_valid_package_name("../"));
+ EXPECT_EQ(false, is_valid_package_name("com.example/../com.evil/"));
+ EXPECT_EQ(false, is_valid_package_name("com.example-1/../com.evil/"));
+ EXPECT_EQ(false, is_valid_package_name("/com.evil"));
+}
+
+TEST_F(UtilsTest, CreateDataUserProfilePath) {
+ EXPECT_EQ("/data/misc/profiles/cur/0", create_primary_cur_profile_dir_path(0));
+ EXPECT_EQ("/data/misc/profiles/cur/1", create_primary_cur_profile_dir_path(1));
+}
+
+TEST_F(UtilsTest, CreateDataUserProfilePackagePath) {
+ EXPECT_EQ("/data/misc/profiles/cur/0/com.example",
+ create_primary_current_profile_package_dir_path(0, "com.example"));
+ EXPECT_EQ("/data/misc/profiles/cur/1/com.example",
+ create_primary_current_profile_package_dir_path(1, "com.example"));
+}
+
+TEST_F(UtilsTest, CreateDataRefProfilePath) {
+ EXPECT_EQ("/data/misc/profiles/ref", create_primary_ref_profile_dir_path());
+}
+
+TEST_F(UtilsTest, CreateDataRefProfilePackagePath) {
+ EXPECT_EQ("/data/misc/profiles/ref/com.example",
+ create_primary_reference_profile_package_dir_path("com.example"));
+}
+
+TEST_F(UtilsTest, CreatePrimaryCurrentProfile) {
+ std::string expected =
+ create_primary_current_profile_package_dir_path(0, "com.example") + "/primary.prof";
+ EXPECT_EQ(expected,
+ create_current_profile_path(/*user*/0, "com.example", /*is_secondary*/false));
+}
+
+TEST_F(UtilsTest, CreatePrimaryReferenceProfile) {
+ std::string expected =
+ create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof";
+ EXPECT_EQ(expected,
+ create_reference_profile_path("com.example", /*is_secondary*/false));
+}
+
+TEST_F(UtilsTest, CreateSecondaryCurrentProfile) {
+ EXPECT_EQ("/data/user/0/com.example/secondary.dex.prof",
+ create_current_profile_path(/*user*/0,
+ "/data/user/0/com.example/secondary.dex", /*is_secondary*/true));
+}
+
+TEST_F(UtilsTest, CreateSecondaryReferenceProfile) {
+ EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.prof",
+ create_reference_profile_path(
+ "/data/user/0/com.example/secondary.dex", /*is_secondary*/true));
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 674f760..c792082 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -18,21 +18,18 @@
#include <errno.h>
#include <fcntl.h>
+#include <fts.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/xattr.h>
-
-#if defined(__APPLE__)
-#include <sys/mount.h>
-#else
-#include <sys/statfs.h>
-#endif
+#include <sys/statvfs.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/fs.h>
-#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include "globals.h" // extern variables.
@@ -41,7 +38,6 @@
#define LOG_TAG "installd"
#endif
-#define CACHE_NOISY(x) //x
#define DEBUG_XATTRS 0
using android::base::StringPrintf;
@@ -53,7 +49,7 @@
* Check that given string is valid filename, and that it attempts no
* parent or child directory traversal.
*/
-static bool is_valid_filename(const std::string& name) {
+bool is_valid_filename(const std::string& name) {
if (name.empty() || (name == ".") || (name == "..")
|| (name.find('/') != std::string::npos)) {
return false;
@@ -64,7 +60,7 @@
static void check_package_name(const char* package_name) {
CHECK(is_valid_filename(package_name));
- CHECK(is_valid_package_name(package_name) == 0);
+ CHECK(is_valid_package_name(package_name));
}
/**
@@ -135,7 +131,7 @@
int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname,
const char *postfix, userid_t userid) {
- if (is_valid_package_name(pkgname) != 0) {
+ if (!is_valid_package_name(pkgname)) {
path[0] = '\0';
return -1;
}
@@ -154,6 +150,9 @@
std::string create_data_path(const char* volume_uuid) {
if (volume_uuid == nullptr) {
return "/data";
+ } else if (!strcmp(volume_uuid, "TEST")) {
+ CHECK(property_get_bool("ro.debuggable", false));
+ return "/data/local/tmp";
} else {
CHECK(is_valid_filename(volume_uuid));
return StringPrintf("/mnt/expand/%s", volume_uuid);
@@ -198,22 +197,76 @@
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(),
+ data_type, package_name);
+}
+
std::string create_data_misc_legacy_path(userid_t userid) {
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_primary_cur_profile_dir_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);
+std::string create_primary_current_profile_package_dir_path(userid_t user,
+ const std::string& package_name) {
+ check_package_name(package_name.c_str());
+ return StringPrintf("%s/%s",
+ create_primary_cur_profile_dir_path(user).c_str(), package_name.c_str());
}
-std::string create_data_ref_profile_package_path(const char* package_name) {
- check_package_name(package_name);
- return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name);
+std::string create_primary_ref_profile_dir_path() {
+ return StringPrintf("%s/ref", android_profiles_dir.path);
+}
+
+std::string create_primary_reference_profile_package_dir_path(const std::string& package_name) {
+ check_package_name(package_name.c_str());
+ return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name.c_str());
+}
+
+std::string create_data_dalvik_cache_path() {
+ return "/data/dalvik-cache";
+}
+
+// Keep profile paths in sync with ActivityThread and LoadedApk.
+const std::string PROFILE_EXT = ".prof";
+const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT;
+
+std::string create_current_profile_path(userid_t user, const std::string& location,
+ bool is_secondary_dex) {
+ if (is_secondary_dex) {
+ // Secondary dex profiles are stored next to the dex files using .prof extension.
+ return StringPrintf("%s%s", location.c_str(), PROFILE_EXT.c_str());
+ } else {
+ // Profiles for primary apks are under /data/misc/profiles/cur.
+ std::string profile_dir = create_primary_current_profile_package_dir_path(user, location);
+ return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str());
+ }
+}
+
+std::string create_reference_profile_path(const std::string& location, bool is_secondary_dex) {
+ if (is_secondary_dex) {
+ // Secondary dex reference profiles are stored next to the dex files under the oat folder.
+ size_t dirIndex = location.rfind('/');
+ CHECK(dirIndex != std::string::npos)
+ << "Unexpected dir structure for secondary dex " << location;
+
+ std::string dex_dir = location.substr(0, dirIndex);
+ std::string dex_name = location.substr(dirIndex +1);
+ return StringPrintf("%s/oat/%s%s",
+ dex_dir.c_str(), dex_name.c_str(), PROFILE_EXT.c_str());
+ } else {
+ // Reference profiles for primary apks are stored in /data/misc/profile/ref.
+ std::string profile_dir = create_primary_reference_profile_package_dir_path(location);
+ return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str());
+ }
}
std::vector<userid_t> get_known_users(const char* volume_uuid) {
@@ -248,6 +301,59 @@
return users;
}
+int calculate_tree_size(const std::string& path, int64_t* size,
+ 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_NOCHDIR | FTS_XDEV, NULL))) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to fts_open " << path;
+ }
+ return -1;
+ }
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_DEFAULT:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ 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 (include_gid != -1 && gid != include_gid) {
+ break;
+ }
+ 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;
+}
+
int create_move_path(char path[PKG_PATH_MAX],
const char* pkgname,
const char* leaf,
@@ -266,49 +372,46 @@
* Checks whether the package name is valid. Returns -1 on error and
* 0 on success.
*/
-int is_valid_package_name(const char* pkgname) {
- const char *x = pkgname;
- int alpha = -1;
+bool is_valid_package_name(const std::string& packageName) {
+ // This logic is borrowed from PackageParser.java
+ bool hasSep = false;
+ bool front = true;
- if (strlen(pkgname) > PKG_NAME_MAX) {
- return -1;
- }
-
- while (*x) {
- if (isalnum(*x) || (*x == '_')) {
- /* alphanumeric or underscore are fine */
- } else if (*x == '.') {
- if ((x == pkgname) || (x[1] == '.') || (x[1] == 0)) {
- /* periods must not be first, last, or doubled */
- ALOGE("invalid package name '%s'\n", pkgname);
- return -1;
- }
- } else if (*x == '-') {
- /* Suffix -X is fine to let versioning of packages.
- But whatever follows should be alphanumeric.*/
- alpha = 1;
- } else {
- /* anything not A-Z, a-z, 0-9, _, or . is invalid */
- ALOGE("invalid package name '%s'\n", pkgname);
- return -1;
+ auto it = packageName.begin();
+ for (; it != packageName.end() && *it != '-'; it++) {
+ char c = *it;
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ front = false;
+ continue;
}
-
- x++;
- }
-
- if (alpha == 1) {
- // Skip current character
- x++;
- while (*x) {
- if (!isalnum(*x)) {
- ALOGE("invalid package name '%s' should include only numbers after -\n", pkgname);
- return -1;
+ if (!front) {
+ if ((c >= '0' && c <= '9') || c == '_') {
+ continue;
}
- x++;
}
+ if (c == '.') {
+ hasSep = true;
+ front = true;
+ continue;
+ }
+ LOG(WARNING) << "Bad package character " << c << " in " << packageName;
+ return false;
}
- return 0;
+ if (front) {
+ LOG(WARNING) << "Missing separator in " << packageName;
+ return false;
+ }
+
+ for (; it != packageName.end(); it++) {
+ char c = *it;
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) continue;
+ if ((c >= '0' && c <= '9') || c == '_' || c == '-' || c == '=') continue;
+ LOG(WARNING) << "Bad suffix character " << c << " in " << packageName;
+ return false;
+ }
+
+ return true;
}
static int _delete_dir_contents(DIR *d,
@@ -526,264 +629,16 @@
return res;
}
-int64_t data_disk_free(const std::string& data_path)
-{
- struct statfs sfs;
- if (statfs(data_path.c_str(), &sfs) == 0) {
- return sfs.f_bavail * sfs.f_bsize;
+int64_t data_disk_free(const std::string& data_path) {
+ struct statvfs sfs;
+ if (statvfs(data_path.c_str(), &sfs) == 0) {
+ return sfs.f_bavail * sfs.f_frsize;
} else {
- PLOG(ERROR) << "Couldn't statfs " << data_path;
+ PLOG(ERROR) << "Couldn't statvfs " << data_path;
return -1;
}
}
-cache_t* start_cache_collection()
-{
- cache_t* cache = (cache_t*)calloc(1, sizeof(cache_t));
- return cache;
-}
-
-#define CACHE_BLOCK_SIZE (512*1024)
-
-static void* _cache_malloc(cache_t* cache, size_t len)
-{
- len = (len+3)&~3;
- if (len > (CACHE_BLOCK_SIZE/2)) {
- // It doesn't make sense to try to put this allocation into one
- // of our blocks, because it is so big. Instead, make a new dedicated
- // block for it.
- int8_t* res = (int8_t*)malloc(len+sizeof(void*));
- if (res == NULL) {
- return NULL;
- }
- CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %zu", res, len));
- // Link it into our list of blocks, not disrupting the current one.
- if (cache->memBlocks == NULL) {
- *(void**)res = NULL;
- cache->memBlocks = res;
- } else {
- *(void**)res = *(void**)cache->memBlocks;
- *(void**)cache->memBlocks = res;
- }
- return res + sizeof(void*);
- }
- int8_t* res = cache->curMemBlockAvail;
- int8_t* nextPos = res + len;
- if (cache->memBlocks == NULL || nextPos > cache->curMemBlockEnd) {
- int8_t* newBlock = (int8_t*) malloc(CACHE_BLOCK_SIZE);
- if (newBlock == NULL) {
- return NULL;
- }
- CACHE_NOISY(ALOGI("Allocated new cache mem block: %p", newBlock));
- *(void**)newBlock = cache->memBlocks;
- cache->memBlocks = newBlock;
- res = cache->curMemBlockAvail = newBlock + sizeof(void*);
- cache->curMemBlockEnd = newBlock + CACHE_BLOCK_SIZE;
- nextPos = res + len;
- }
- CACHE_NOISY(ALOGI("cache_malloc: ret %p size %zu, block=%p, nextPos=%p",
- res, len, cache->memBlocks, nextPos));
- cache->curMemBlockAvail = nextPos;
- return res;
-}
-
-static void* _cache_realloc(cache_t* cache, void* cur, size_t origLen, size_t len)
-{
- // This isn't really a realloc, but it is good enough for our purposes here.
- void* alloc = _cache_malloc(cache, len);
- if (alloc != NULL && cur != NULL) {
- memcpy(alloc, cur, origLen < len ? origLen : len);
- }
- return alloc;
-}
-
-static void _inc_num_cache_collected(cache_t* cache)
-{
- cache->numCollected++;
- if ((cache->numCollected%20000) == 0) {
- ALOGI("Collected cache so far: %zd directories, %zd files",
- cache->numDirs, cache->numFiles);
- }
-}
-
-static cache_dir_t* _add_cache_dir_t(cache_t* cache, cache_dir_t* parent, const char *name)
-{
- size_t nameLen = strlen(name);
- cache_dir_t* dir = (cache_dir_t*)_cache_malloc(cache, sizeof(cache_dir_t)+nameLen+1);
- if (dir != NULL) {
- dir->parent = parent;
- dir->childCount = 0;
- dir->hiddenCount = 0;
- dir->deleted = 0;
- strcpy(dir->name, name);
- if (cache->numDirs >= cache->availDirs) {
- size_t newAvail = cache->availDirs < 1000 ? 1000 : cache->availDirs*2;
- cache_dir_t** newDirs = (cache_dir_t**)_cache_realloc(cache, cache->dirs,
- cache->availDirs*sizeof(cache_dir_t*), newAvail*sizeof(cache_dir_t*));
- if (newDirs == NULL) {
- ALOGE("Failure growing cache dirs array for %s\n", name);
- return NULL;
- }
- cache->availDirs = newAvail;
- cache->dirs = newDirs;
- }
- cache->dirs[cache->numDirs] = dir;
- cache->numDirs++;
- if (parent != NULL) {
- parent->childCount++;
- }
- _inc_num_cache_collected(cache);
- } else {
- ALOGE("Failure allocating cache_dir_t for %s\n", name);
- }
- return dir;
-}
-
-static cache_file_t* _add_cache_file_t(cache_t* cache, cache_dir_t* dir, time_t modTime,
- const char *name)
-{
- size_t nameLen = strlen(name);
- cache_file_t* file = (cache_file_t*)_cache_malloc(cache, sizeof(cache_file_t)+nameLen+1);
- if (file != NULL) {
- file->dir = dir;
- file->modTime = modTime;
- strcpy(file->name, name);
- if (cache->numFiles >= cache->availFiles) {
- size_t newAvail = cache->availFiles < 1000 ? 1000 : cache->availFiles*2;
- cache_file_t** newFiles = (cache_file_t**)_cache_realloc(cache, cache->files,
- cache->availFiles*sizeof(cache_file_t*), newAvail*sizeof(cache_file_t*));
- if (newFiles == NULL) {
- ALOGE("Failure growing cache file array for %s\n", name);
- return NULL;
- }
- cache->availFiles = newAvail;
- cache->files = newFiles;
- }
- CACHE_NOISY(ALOGI("Setting file %p at position %zd in array %p", file,
- cache->numFiles, cache->files));
- cache->files[cache->numFiles] = file;
- cache->numFiles++;
- dir->childCount++;
- _inc_num_cache_collected(cache);
- } else {
- ALOGE("Failure allocating cache_file_t for %s\n", name);
- }
- return file;
-}
-
-static int _add_cache_files(cache_t *cache, cache_dir_t *parentDir, const char *dirName,
- DIR* dir, char *pathBase, char *pathPos, size_t pathAvailLen)
-{
- struct dirent *de;
- cache_dir_t* cacheDir = NULL;
- int dfd;
-
- CACHE_NOISY(ALOGI("_add_cache_files: parent=%p dirName=%s dir=%p pathBase=%s",
- parentDir, dirName, dir, pathBase));
-
- dfd = dirfd(dir);
-
- if (dfd < 0) return 0;
-
- // Sub-directories always get added to the data structure, so if they
- // are empty we will know about them to delete them later.
- cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
-
- while ((de = readdir(dir))) {
- const char *name = de->d_name;
-
- if (de->d_type == DT_DIR) {
- int subfd;
- DIR *subdir;
-
- /* always skip "." and ".." */
- if (name[0] == '.') {
- if (name[1] == 0) continue;
- if ((name[1] == '.') && (name[2] == 0)) continue;
- }
-
- subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
- if (subfd < 0) {
- ALOGE("Couldn't openat %s: %s\n", name, strerror(errno));
- continue;
- }
- subdir = fdopendir(subfd);
- if (subdir == NULL) {
- ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno));
- close(subfd);
- continue;
- }
- if (cacheDir == NULL) {
- cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
- }
- if (cacheDir != NULL) {
- // Update pathBase for the new path... this may change dirName
- // if that is also pointing to the path, but we are done with it
- // now.
- size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name);
- CACHE_NOISY(ALOGI("Collecting dir %s\n", pathBase));
- if (finallen < pathAvailLen) {
- _add_cache_files(cache, cacheDir, name, subdir, pathBase,
- pathPos+finallen, pathAvailLen-finallen);
- } else {
- // Whoops, the final path is too long! We'll just delete
- // this directory.
- ALOGW("Cache dir %s truncated in path %s; deleting dir\n",
- name, pathBase);
- _delete_dir_contents(subdir, NULL);
- if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) {
- ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno));
- }
- }
- }
- closedir(subdir);
- } else if (de->d_type == DT_REG) {
- // Skip files that start with '.'; they will be deleted if
- // their entire directory is deleted. This allows for metadata
- // like ".nomedia" to remain in the directory until the entire
- // directory is deleted.
- if (cacheDir == NULL) {
- cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
- }
- if (name[0] == '.') {
- cacheDir->hiddenCount++;
- continue;
- }
- if (cacheDir != NULL) {
- // Build final full path for file... this may change dirName
- // if that is also pointing to the path, but we are done with it
- // now.
- size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name);
- CACHE_NOISY(ALOGI("Collecting file %s\n", pathBase));
- if (finallen < pathAvailLen) {
- struct stat s;
- if (stat(pathBase, &s) >= 0) {
- _add_cache_file_t(cache, cacheDir, s.st_mtime, name);
- } else {
- ALOGW("Unable to stat cache file %s; deleting\n", pathBase);
- if (unlink(pathBase) < 0) {
- ALOGE("Couldn't unlink %s: %s\n", pathBase, strerror(errno));
- }
- }
- } else {
- // Whoops, the final path is too long! We'll just delete
- // this file.
- ALOGW("Cache file %s truncated in path %s; deleting\n",
- name, pathBase);
- if (unlinkat(dfd, name, 0) < 0) {
- *pathPos = 0;
- ALOGE("Couldn't unlinkat %s in %s: %s\n", name, pathBase,
- strerror(errno));
- }
- }
- }
- } else {
- cacheDir->hiddenCount++;
- }
- }
- return 0;
-}
-
int get_path_inode(const std::string& path, ino_t *inode) {
struct stat buf;
memset(&buf, 0, sizeof(buf));
@@ -877,172 +732,6 @@
}
}
-void add_cache_files(cache_t* cache, const std::string& data_path) {
- DIR *d;
- struct dirent *de;
- char dirname[PATH_MAX];
-
- const char* basepath = data_path.c_str();
- CACHE_NOISY(ALOGI("add_cache_files: basepath=%s\n", basepath));
-
- d = opendir(basepath);
- if (d == NULL) {
- return;
- }
-
- while ((de = readdir(d))) {
- if (de->d_type == DT_DIR) {
- DIR* subdir;
- const char *name = de->d_name;
-
- /* always skip "." and ".." */
- if (name[0] == '.') {
- if (name[1] == 0) continue;
- if ((name[1] == '.') && (name[2] == 0)) continue;
- }
-
- auto parent = StringPrintf("%s/%s", basepath, name);
- auto resolved = read_path_inode(parent, "cache", kXattrInodeCache);
- strcpy(dirname, resolved.c_str());
- CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname));
-
- subdir = opendir(dirname);
- if (subdir != NULL) {
- size_t dirnameLen = strlen(dirname);
- _add_cache_files(cache, NULL, dirname, subdir, dirname, dirname+dirnameLen,
- PATH_MAX - dirnameLen);
- closedir(subdir);
- }
- }
- }
-
- closedir(d);
-}
-
-static char *create_dir_path(char path[PATH_MAX], cache_dir_t* dir)
-{
- char *pos = path;
- if (dir->parent != NULL) {
- pos = create_dir_path(path, dir->parent);
- }
- // Note that we don't need to worry about going beyond the buffer,
- // since when we were constructing the cache entries our maximum
- // buffer size for full paths was PATH_MAX.
- strcpy(pos, dir->name);
- pos += strlen(pos);
- *pos = '/';
- pos++;
- *pos = 0;
- return pos;
-}
-
-static void delete_cache_dir(char path[PATH_MAX], cache_dir_t* dir)
-{
- if (dir->parent != NULL) {
- create_dir_path(path, dir);
- ALOGI("DEL DIR %s\n", path);
- if (dir->hiddenCount <= 0) {
- if (rmdir(path)) {
- ALOGE("Couldn't rmdir %s: %s\n", path, strerror(errno));
- return;
- }
- } else {
- // The directory contains hidden files so we need to delete
- // them along with the directory itself.
- if (delete_dir_contents(path, 1, NULL)) {
- return;
- }
- }
- dir->parent->childCount--;
- dir->deleted = 1;
- if (dir->parent->childCount <= 0) {
- delete_cache_dir(path, dir->parent);
- }
- } else if (dir->hiddenCount > 0) {
- // This is a root directory, but it has hidden files. Get rid of
- // all of those files, but not the directory itself.
- create_dir_path(path, dir);
- ALOGI("DEL CONTENTS %s\n", path);
- delete_dir_contents(path, 0, NULL);
- }
-}
-
-static int cache_modtime_sort(const void *lhsP, const void *rhsP)
-{
- const cache_file_t *lhs = *(const cache_file_t**)lhsP;
- const cache_file_t *rhs = *(const cache_file_t**)rhsP;
- return lhs->modTime < rhs->modTime ? -1 : (lhs->modTime > rhs->modTime ? 1 : 0);
-}
-
-void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size)
-{
- size_t i;
- int skip = 0;
- char path[PATH_MAX];
-
- ALOGI("Collected cache files: %zd directories, %zd files",
- cache->numDirs, cache->numFiles);
-
- CACHE_NOISY(ALOGI("Sorting files..."));
- qsort(cache->files, cache->numFiles, sizeof(cache_file_t*),
- cache_modtime_sort);
-
- CACHE_NOISY(ALOGI("Cleaning empty directories..."));
- for (i=cache->numDirs; i>0; i--) {
- cache_dir_t* dir = cache->dirs[i-1];
- if (dir->childCount <= 0 && !dir->deleted) {
- delete_cache_dir(path, dir);
- }
- }
-
- CACHE_NOISY(ALOGI("Trimming files..."));
- for (i=0; i<cache->numFiles; i++) {
- skip++;
- if (skip > 10) {
- if (data_disk_free(data_path) > free_size) {
- return;
- }
- skip = 0;
- }
- cache_file_t* file = cache->files[i];
- strcpy(create_dir_path(path, file->dir), file->name);
- ALOGI("DEL (mod %d) %s\n", (int)file->modTime, path);
- if (unlink(path) < 0) {
- ALOGE("Couldn't unlink %s: %s\n", path, strerror(errno));
- }
- file->dir->childCount--;
- if (file->dir->childCount <= 0) {
- delete_cache_dir(path, file->dir);
- }
- }
-}
-
-void finish_cache_collection(cache_t* cache)
-{
- CACHE_NOISY(size_t i;)
-
- CACHE_NOISY(ALOGI("clear_cache_files: %zu dirs, %zu files\n", cache->numDirs, cache->numFiles));
- CACHE_NOISY(
- for (i=0; i<cache->numDirs; i++) {
- cache_dir_t* dir = cache->dirs[i];
- ALOGI("dir #%zu: %p %s parent=%p\n", i, dir, dir->name, dir->parent);
- })
- CACHE_NOISY(
- for (i=0; i<cache->numFiles; i++) {
- cache_file_t* file = cache->files[i];
- ALOGI("file #%zu: %p %s time=%d dir=%p\n", i, file, file->name,
- (int)file->modTime, file->dir);
- })
- void* block = cache->memBlocks;
- while (block != NULL) {
- void* nextBlock = *(void**)block;
- CACHE_NOISY(ALOGI("Freeing cache mem block: %p", block));
- free(block);
- block = nextBlock;
- }
- free(cache);
-}
-
/**
* Validate that the path is valid in the context of the provided directory.
* The path is allowed to have at most one subdirectory and no indirections
@@ -1087,6 +776,27 @@
return -1;
}
+bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
+ const char* volume_uuid, int uid, int storage_flag) {
+ CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE);
+
+ std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
+ ? create_data_user_ce_package_path(
+ volume_uuid, multiuser_get_user_id(uid), pkgname.c_str())
+ : create_data_user_de_package_path(
+ volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
+ dir_rec_t dir;
+ if (get_path_from_string(&dir, app_private_dir.c_str()) != 0) {
+ LOG(WARNING) << "Could not get dir rec for " << app_private_dir;
+ return false;
+ }
+ // Usually secondary dex files have a nested directory structure.
+ // Pick at most 10 subdirectories when validating (arbitrary value).
+ // If the secondary dex file is >10 directory nested then validation will
+ // fail and the file will not be compiled.
+ return validate_path(&dir, dex_path.c_str(), /*max_subdirs*/ 10) == 0;
+}
+
/**
* Get the contents of a environment variable that contains a path. Caller
* owns the string that is inserted into the directory record. Returns
@@ -1286,5 +996,77 @@
}
}
+/**
+ * Prepare an app cache directory, which offers to fix-up the GID and
+ * directory mode flags during a platform upgrade.
+ * The app cache directory path will be 'parent'/'name'.
+ */
+int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+ uid_t uid, gid_t gid) {
+ auto path = StringPrintf("%s/%s", parent.c_str(), name);
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0) {
+ if (errno == ENOENT) {
+ // This is fine, just create it
+ if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << path;
+ return -1;
+ } else {
+ return 0;
+ }
+ } else {
+ PLOG(ERROR) << "Failed to stat " << path;
+ return -1;
+ }
+ }
+
+ 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 && actual_mode == target_mode) {
+ // Everything looks good!
+ return 0;
+ } else {
+ // Mismatched GID/mode is recoverable; fall through to update
+ LOG(DEBUG) << "Mismatched cache GID/mode at " << path << ": found " << st.st_gid
+ << " but expected " << gid;
+ }
+
+ // Directory is owned correctly, but GID or mode mismatch means it's
+ // probably a platform upgrade so we need to fix them
+ FTS *fts;
+ FTSENT *p;
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+ PLOG(ERROR) << "Failed to fts_open " << path;
+ return -1;
+ }
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_DP:
+ if (chmod(p->fts_path, target_mode) != 0) {
+ PLOG(WARNING) << "Failed to chmod " << p->fts_path;
+ }
+ // Intentional fall through to also set GID
+ case FTS_F:
+ if (chown(p->fts_path, -1, gid) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ break;
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_path, -1, gid) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ return 0;
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 8123e9b..070da84 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -30,40 +30,21 @@
#include <installd_constants.h>
+#define MEASURE_DEBUG 0
+#define FIXUP_DEBUG 0
+
+#define BYPASS_QUOTA 0
+#define BYPASS_SDCARDFS 0
+
namespace android {
namespace installd {
struct dir_rec_t;
-typedef struct cache_dir_struct {
- struct cache_dir_struct* parent;
- int32_t childCount;
- int32_t hiddenCount;
- int32_t deleted;
- char name[];
-} cache_dir_t;
-
-typedef struct {
- cache_dir_t* dir;
- time_t modTime;
- char name[];
-} cache_file_t;
-
-typedef struct {
- size_t numDirs;
- size_t availDirs;
- cache_dir_t** dirs;
- size_t numFiles;
- size_t availFiles;
- cache_file_t** files;
- size_t numCollected;
- void* memBlocks;
- int8_t* curMemBlockAvail;
- int8_t* curMemBlockEnd;
-} cache_t;
-
constexpr const char* kXattrInodeCache = "user.inode_cache";
constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache";
+constexpr const char* kXattrCacheGroup = "user.cache_group";
+constexpr const char* kXattrCacheTombstone = "user.cache_tombstone";
int create_pkg_path(char path[PKG_PATH_MAX],
const char *pkgname,
@@ -73,7 +54,6 @@
std::string create_data_path(const char* volume_uuid);
std::string create_data_app_path(const char* volume_uuid);
-
std::string create_data_app_package_path(const char* volume_uuid, const char* package_name);
std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid);
@@ -87,15 +67,31 @@
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_package_path(userid_t user, const char* package_name);
-std::string create_data_ref_profile_package_path(const char* package_name);
+std::string create_data_dalvik_cache_path();
+
+std::string create_primary_cur_profile_dir_path(userid_t userid);
+std::string create_primary_current_profile_package_dir_path(
+ userid_t user, const std::string& package_name);
+
+std::string create_primary_ref_profile_dir_path();
+std::string create_primary_reference_profile_package_dir_path(const std::string& package_name);
+
+std::string create_current_profile_path(
+ userid_t user, const std::string& package_name, bool is_secondary_dex);
+std::string create_reference_profile_path(
+ const std::string& package_name, bool is_secondary_dex);
std::vector<userid_t> get_known_users(const char* volume_uuid);
+int calculate_tree_size(const std::string& path, int64_t* size,
+ 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);
int create_move_path(char path[PKG_PATH_MAX],
@@ -103,7 +99,8 @@
const char* leaf,
userid_t userid);
-int is_valid_package_name(const char* pkgname);
+bool is_valid_filename(const std::string& name);
+bool is_valid_package_name(const std::string& packageName);
int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false);
int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false);
@@ -119,20 +116,14 @@
int64_t data_disk_free(const std::string& data_path);
-cache_t* start_cache_collection();
-
int get_path_inode(const std::string& path, ino_t *inode);
int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
-void add_cache_files(cache_t* cache, const std::string& data_path);
-
-void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size);
-
-void finish_cache_collection(cache_t* cache);
-
int validate_system_app_path(const char* path);
+bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
+ const char* volume_uuid, int uid, int storage_flag);
int get_path_from_env(dir_rec_t* rec, const char* var);
@@ -152,6 +143,9 @@
int wait_child(pid_t pid);
+int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+ uid_t uid, gid_t gid);
+
} // namespace installd
} // namespace android
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
index 75b907c..3b8955b 100644
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -14,22 +14,22 @@
* limitations under the License.
*/
+#define LOG_TAG "ip-up-vpn"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <linux/route.h>
+#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <errno.h>
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <linux/if.h>
-#include <linux/route.h>
-#define LOG_TAG "ip-up-vpn"
-#include <cutils/log.h>
+#include <log/log.h>
#define DIR "/data/misc/vpn/"
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
new file mode 100644
index 0000000..38647eb
--- /dev/null
+++ b/cmds/lshal/Android.bp
@@ -0,0 +1,68 @@
+// 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.
+
+cc_library_shared {
+ name: "liblshal",
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhidl-gen-utils",
+ "libvintf",
+ "android.hidl.manager@1.0",
+ ],
+ srcs: [
+ "DebugCommand.cpp",
+ "Lshal.cpp",
+ "ListCommand.cpp",
+ "PipeRelay.cpp",
+ "utils.cpp",
+ ],
+}
+
+cc_defaults {
+ name: "lshal_defaults",
+ shared_libs: [
+ "libbase",
+ "libhidlbase",
+ "libhidltransport",
+ "liblshal",
+ "libutils",
+ ]
+}
+
+cc_binary {
+ name: "lshal",
+ defaults: ["lshal_defaults"],
+ srcs: [
+ "main.cpp"
+ ]
+}
+
+cc_test {
+ name: "lshal_test",
+ defaults: ["lshal_defaults"],
+ gtest: true,
+ static_libs: [
+ "libgmock"
+ ],
+ shared_libs: [
+ "android.hardware.tests.baz@1.0"
+ ],
+ srcs: [
+ "test.cpp"
+ ]
+}
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp
new file mode 100644
index 0000000..672cad6
--- /dev/null
+++ b/cmds/lshal/DebugCommand.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "DebugCommand.h"
+
+#include "Lshal.h"
+
+namespace android {
+namespace lshal {
+
+DebugCommand::DebugCommand(Lshal &lshal) : mLshal(lshal) {
+}
+
+Status DebugCommand::parseArgs(const std::string &command, const Arg &arg) {
+ if (optind >= arg.argc) {
+ mLshal.usage(command);
+ return USAGE;
+ }
+ mInterfaceName = arg.argv[optind];
+ ++optind;
+ for (; optind < arg.argc; ++optind) {
+ mOptions.push_back(arg.argv[optind]);
+ }
+ return OK;
+}
+
+Status DebugCommand::main(const std::string &command, const Arg &arg) {
+ Status status = parseArgs(command, arg);
+ if (status != OK) {
+ return status;
+ }
+ auto pair = splitFirst(mInterfaceName, '/');
+ return mLshal.emitDebugInfo(
+ pair.first, pair.second.empty() ? "default" : pair.second, mOptions,
+ mLshal.out().buf(),
+ mLshal.err());
+}
+
+} // namespace lshal
+} // namespace android
+
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h
new file mode 100644
index 0000000..fa0f0fa
--- /dev/null
+++ b/cmds/lshal/DebugCommand.h
@@ -0,0 +1,49 @@
+/*
+ * 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 FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_
+
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal;
+
+class DebugCommand {
+public:
+ DebugCommand(Lshal &lshal);
+ Status main(const std::string &command, const Arg &arg);
+private:
+ Status parseArgs(const std::string &command, const Arg &arg);
+
+ Lshal &mLshal;
+ std::string mInterfaceName;
+ std::vector<std::string> mOptions;
+
+ DISALLOW_COPY_AND_ASSIGN(DebugCommand);
+};
+
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
new file mode 100644
index 0000000..710b6e4
--- /dev/null
+++ b/cmds/lshal/ListCommand.cpp
@@ -0,0 +1,705 @@
+/*
+ * 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 "ListCommand.h"
+
+#include <getopt.h>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <regex>
+
+#include <android-base/parseint.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl-util/FQName.h>
+#include <private/android_filesystem_config.h>
+#include <sys/stat.h>
+#include <vintf/HalManifest.h>
+#include <vintf/parse_xml.h>
+
+#include "Lshal.h"
+#include "PipeRelay.h"
+#include "Timeout.h"
+#include "utils.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hidl::manager::V1_0::IServiceManager;
+
+namespace android {
+namespace lshal {
+
+ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) {
+}
+
+std::string getCmdline(pid_t pid) {
+ std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
+ std::string cmdline;
+ if (!ifs.is_open()) {
+ return "";
+ }
+ ifs >> cmdline;
+ return cmdline;
+}
+
+const std::string &ListCommand::getCmdline(pid_t pid) {
+ auto pair = mCmdlines.find(pid);
+ if (pair != mCmdlines.end()) {
+ return pair->second;
+ }
+ mCmdlines[pid] = ::android::lshal::getCmdline(pid);
+ return mCmdlines[pid];
+}
+
+void ListCommand::removeDeadProcesses(Pids *pids) {
+ static const pid_t myPid = getpid();
+ pids->erase(std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
+ return pid == myPid || this->getCmdline(pid).empty();
+ }), pids->end());
+}
+
+bool ListCommand::getReferencedPids(
+ pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
+
+ std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
+ if (!ifs.is_open()) {
+ return false;
+ }
+
+ static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
+
+ std::string line;
+ std::smatch match;
+ while(getline(ifs, line)) {
+ if (!std::regex_search(line, match, prefix)) {
+ // the line doesn't start with the correct prefix
+ continue;
+ }
+ std::string ptrString = "0x" + match.str(2); // use number after c
+ uint64_t ptr;
+ if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+ // Should not reach here, but just be tolerant.
+ mErr << "Could not parse number " << ptrString << std::endl;
+ continue;
+ }
+ const std::string proc = " proc ";
+ auto pos = line.rfind(proc);
+ if (pos != std::string::npos) {
+ for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
+ int32_t pid;
+ if (!::android::base::ParseInt(pidStr, &pid)) {
+ mErr << "Could not parse number " << pidStr << std::endl;
+ continue;
+ }
+ (*objects)[ptr].push_back(pid);
+ }
+ }
+ }
+ return true;
+}
+
+// Must process hwbinder services first, then passthrough services.
+void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
+ f(mServicesTable);
+ f(mPassthroughRefTable);
+ f(mImplementationsTable);
+}
+void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
+ f(mServicesTable);
+ f(mPassthroughRefTable);
+ f(mImplementationsTable);
+}
+
+void ListCommand::postprocess() {
+ forEachTable([this](Table &table) {
+ if (mSortColumn) {
+ std::sort(table.begin(), table.end(), mSortColumn);
+ }
+ for (TableEntry &entry : table) {
+ entry.serverCmdline = getCmdline(entry.serverPid);
+ removeDeadProcesses(&entry.clientPids);
+ for (auto pid : entry.clientPids) {
+ entry.clientCmdlines.push_back(this->getCmdline(pid));
+ }
+ }
+ });
+ // use a double for loop here because lshal doesn't care about efficiency.
+ for (TableEntry &packageEntry : mImplementationsTable) {
+ std::string packageName = packageEntry.interfaceName;
+ FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
+ if (!fqPackageName.isValid()) {
+ continue;
+ }
+ for (TableEntry &interfaceEntry : mPassthroughRefTable) {
+ if (interfaceEntry.arch != ARCH_UNKNOWN) {
+ continue;
+ }
+ FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
+ if (!interfaceName.isValid()) {
+ continue;
+ }
+ if (interfaceName.getPackageAndVersion() == fqPackageName) {
+ interfaceEntry.arch = packageEntry.arch;
+ }
+ }
+ }
+}
+
+void ListCommand::printLine(
+ const std::string &interfaceName,
+ const std::string &transport,
+ const std::string &arch,
+ const std::string &server,
+ const std::string &serverCmdline,
+ const std::string &address, const std::string &clients,
+ const std::string &clientCmdlines) const {
+ if (mSelectedColumns & ENABLE_INTERFACE_NAME)
+ mOut << std::setw(80) << interfaceName << "\t";
+ if (mSelectedColumns & ENABLE_TRANSPORT)
+ mOut << std::setw(10) << transport << "\t";
+ if (mSelectedColumns & ENABLE_ARCH)
+ mOut << std::setw(5) << arch << "\t";
+ if (mSelectedColumns & ENABLE_SERVER_PID) {
+ if (mEnableCmdlines) {
+ mOut << std::setw(15) << serverCmdline << "\t";
+ } else {
+ mOut << std::setw(5) << server << "\t";
+ }
+ }
+ if (mSelectedColumns & ENABLE_SERVER_ADDR)
+ mOut << std::setw(16) << address << "\t";
+ if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
+ if (mEnableCmdlines) {
+ mOut << std::setw(0) << clientCmdlines;
+ } else {
+ mOut << std::setw(0) << clients;
+ }
+ }
+ mOut << std::endl;
+}
+
+void ListCommand::dumpVintf() const {
+ mOut << "<!-- " << std::endl
+ << " This is a skeleton device manifest. Notes: " << std::endl
+ << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
+ << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
+ << " only hwbinder is shown." << std::endl
+ << " 3. It is likely that HALs in passthrough transport does not have" << std::endl
+ << " <interface> declared; users will have to write them by hand." << std::endl
+ << " 4. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
+ << " is removed from the manifest file and written by assemble_vintf" << std::endl
+ << " at build time." << std::endl
+ << "-->" << std::endl;
+
+ vintf::HalManifest manifest;
+ forEachTable([this, &manifest] (const Table &table) {
+ for (const TableEntry &entry : table) {
+
+ std::string fqInstanceName = entry.interfaceName;
+
+ if (&table == &mImplementationsTable) {
+ // Quick hack to work around *'s
+ replaceAll(&fqInstanceName, '*', 'D');
+ }
+ auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
+ FQName fqName(splittedFqInstanceName.first);
+ if (!fqName.isValid()) {
+ mErr << "Warning: '" << splittedFqInstanceName.first
+ << "' is not a valid FQName." << std::endl;
+ continue;
+ }
+ // Strip out system libs.
+ if (fqName.inPackage("android.hidl") ||
+ fqName.inPackage("android.frameworks") ||
+ fqName.inPackage("android.system")) {
+ continue;
+ }
+ std::string interfaceName =
+ &table == &mImplementationsTable ? "" : fqName.name();
+ std::string instanceName =
+ &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
+
+ vintf::Version version{fqName.getPackageMajorVersion(),
+ fqName.getPackageMinorVersion()};
+ vintf::Transport transport;
+ vintf::Arch arch;
+ if (entry.transport == "hwbinder") {
+ transport = vintf::Transport::HWBINDER;
+ arch = vintf::Arch::ARCH_EMPTY;
+ } else if (entry.transport == "passthrough") {
+ transport = vintf::Transport::PASSTHROUGH;
+ switch (entry.arch) {
+ case lshal::ARCH32:
+ arch = vintf::Arch::ARCH_32; break;
+ case lshal::ARCH64:
+ arch = vintf::Arch::ARCH_64; break;
+ case lshal::ARCH_BOTH:
+ arch = vintf::Arch::ARCH_32_64; break;
+ case lshal::ARCH_UNKNOWN: // fallthrough
+ default:
+ mErr << "Warning: '" << fqName.package()
+ << "' doesn't have bitness info, assuming 32+64." << std::endl;
+ arch = vintf::Arch::ARCH_32_64;
+ }
+ } else {
+ mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
+ continue;
+ }
+
+ bool done = false;
+ for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
+ if (hal->transport() != transport) {
+ if (transport != vintf::Transport::PASSTHROUGH) {
+ mErr << "Fatal: should not reach here. Generated result may be wrong."
+ << std::endl;
+ }
+ done = true;
+ break;
+ }
+ if (hal->hasVersion(version)) {
+ if (&table != &mImplementationsTable) {
+ hal->interfaces[interfaceName].name = interfaceName;
+ hal->interfaces[interfaceName].instances.insert(instanceName);
+ }
+ done = true;
+ break;
+ }
+ }
+ if (done) {
+ continue; // to next TableEntry
+ }
+ decltype(vintf::ManifestHal::interfaces) interfaces;
+ if (&table != &mImplementationsTable) {
+ interfaces[interfaceName].name = interfaceName;
+ interfaces[interfaceName].instances.insert(instanceName);
+ }
+ if (!manifest.add(vintf::ManifestHal{
+ .format = vintf::HalFormat::HIDL,
+ .name = fqName.package(),
+ .versions = {version},
+ .transportArch = {transport, arch},
+ .interfaces = interfaces})) {
+ mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
+ }
+ }
+ });
+ mOut << vintf::gHalManifestConverter(manifest);
+}
+
+static const std::string &getArchString(Architecture arch) {
+ static const std::string sStr64 = "64";
+ static const std::string sStr32 = "32";
+ static const std::string sStrBoth = "32+64";
+ static const std::string sStrUnknown = "";
+ switch (arch) {
+ case ARCH64:
+ return sStr64;
+ case ARCH32:
+ return sStr32;
+ case ARCH_BOTH:
+ return sStrBoth;
+ case ARCH_UNKNOWN: // fall through
+ default:
+ return sStrUnknown;
+ }
+}
+
+static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
+ switch (a) {
+ case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
+ return ARCH64;
+ case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
+ return ARCH32;
+ case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
+ default:
+ return ARCH_UNKNOWN;
+ }
+}
+
+void ListCommand::dumpTable() {
+ mServicesTable.description =
+ "All binderized services (registered services through hwservicemanager)";
+ mPassthroughRefTable.description =
+ "All interfaces that getService() has ever return as a passthrough interface;\n"
+ "PIDs / processes shown below might be inaccurate because the process\n"
+ "might have relinquished the interface or might have died.\n"
+ "The Server / Server CMD column can be ignored.\n"
+ "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
+ "the library and successfully fetched the passthrough implementation.";
+ mImplementationsTable.description =
+ "All available passthrough implementations (all -impl.so files)";
+ forEachTable([this] (const Table &table) {
+ mOut << table.description << std::endl;
+ mOut << std::left;
+ printLine("Interface", "Transport", "Arch", "Server", "Server CMD",
+ "PTR", "Clients", "Clients CMD");
+
+ for (const auto &entry : table) {
+ printLine(entry.interfaceName,
+ entry.transport,
+ getArchString(entry.arch),
+ entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
+ entry.serverCmdline,
+ entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
+ join(entry.clientPids, " "),
+ join(entry.clientCmdlines, ";"));
+
+ // We're only interested in dumping debug info for already
+ // instantiated services. There's little value in dumping the
+ // debug info for a service we create on the fly, so we only operate
+ // on the "mServicesTable".
+ if (mEmitDebugInfo && &table == &mServicesTable) {
+ auto pair = splitFirst(entry.interfaceName, '/');
+ mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(),
+ NullableOStream<std::ostream>(nullptr));
+ }
+ }
+ mOut << std::endl;
+ });
+
+}
+
+void ListCommand::dump() {
+ if (mVintf) {
+ dumpVintf();
+ if (!!mFileOutput) {
+ mFileOutput.buf().close();
+ delete &mFileOutput.buf();
+ mFileOutput = nullptr;
+ }
+ mOut = std::cout;
+ } else {
+ dumpTable();
+ }
+}
+
+void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
+ Table *table = nullptr;
+ switch (source) {
+ case HWSERVICEMANAGER_LIST :
+ table = &mServicesTable; break;
+ case PTSERVICEMANAGER_REG_CLIENT :
+ table = &mPassthroughRefTable; break;
+ case LIST_DLLIB :
+ table = &mImplementationsTable; break;
+ default:
+ mErr << "Error: Unknown source of entry " << source << std::endl;
+ }
+ if (table) {
+ table->entries.push_back(std::forward<TableEntry>(entry));
+ }
+}
+
+Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
+ using namespace ::android::hardware;
+ using namespace ::android::hidl::manager::V1_0;
+ using namespace ::android::hidl::base::V1_0;
+ auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
+ std::map<std::string, TableEntry> entries;
+ for (const auto &info : infos) {
+ std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
+ std::string{info.instanceName.c_str()};
+ entries.emplace(interfaceName, TableEntry{
+ .interfaceName = interfaceName,
+ .transport = "passthrough",
+ .serverPid = NO_PID,
+ .serverObjectAddress = NO_PTR,
+ .clientPids = {},
+ .arch = ARCH_UNKNOWN
+ }).first->second.arch |= fromBaseArchitecture(info.arch);
+ }
+ for (auto &&pair : entries) {
+ putEntry(LIST_DLLIB, std::move(pair.second));
+ }
+ });
+ if (!ret.isOk()) {
+ mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
+ << ret.description() << std::endl;
+ return DUMP_ALL_LIBS_ERROR;
+ }
+ return OK;
+}
+
+Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
+ using namespace ::android::hardware;
+ using namespace ::android::hardware::details;
+ using namespace ::android::hidl::manager::V1_0;
+ using namespace ::android::hidl::base::V1_0;
+ auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
+ for (const auto &info : infos) {
+ if (info.clientPids.size() <= 0) {
+ continue;
+ }
+ putEntry(PTSERVICEMANAGER_REG_CLIENT, {
+ .interfaceName =
+ std::string{info.interfaceName.c_str()} + "/" +
+ std::string{info.instanceName.c_str()},
+ .transport = "passthrough",
+ .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
+ .serverObjectAddress = NO_PTR,
+ .clientPids = info.clientPids,
+ .arch = fromBaseArchitecture(info.arch)
+ });
+ }
+ });
+ if (!ret.isOk()) {
+ mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
+ << ret.description() << std::endl;
+ return DUMP_PASSTHROUGH_ERROR;
+ }
+ return OK;
+}
+
+Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
+ using namespace ::std;
+ using namespace ::android::hardware;
+ using namespace ::android::hidl::manager::V1_0;
+ using namespace ::android::hidl::base::V1_0;
+ const std::string mode = "hwbinder";
+
+ hidl_vec<hidl_string> fqInstanceNames;
+ // copying out for timeoutIPC
+ auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
+ fqInstanceNames = names;
+ });
+ if (!listRet.isOk()) {
+ mErr << "Error: Failed to list services for " << mode << ": "
+ << listRet.description() << std::endl;
+ return DUMP_BINDERIZED_ERROR;
+ }
+
+ Status status = OK;
+ // server pid, .ptr value of binder object, child pids
+ std::map<std::string, DebugInfo> allDebugInfos;
+ std::map<pid_t, std::map<uint64_t, Pids>> allPids;
+ for (const auto &fqInstanceName : fqInstanceNames) {
+ const auto pair = splitFirst(fqInstanceName, '/');
+ const auto &serviceName = pair.first;
+ const auto &instanceName = pair.second;
+ auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
+ if (!getRet.isOk()) {
+ mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+ << "cannot be fetched from service manager:"
+ << getRet.description() << std::endl;
+ status |= DUMP_BINDERIZED_ERROR;
+ continue;
+ }
+ sp<IBase> service = getRet;
+ if (service == nullptr) {
+ mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+ << "cannot be fetched from service manager (null)"
+ << std::endl;
+ status |= DUMP_BINDERIZED_ERROR;
+ continue;
+ }
+ auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
+ allDebugInfos[fqInstanceName] = debugInfo;
+ if (debugInfo.pid >= 0) {
+ allPids[static_cast<pid_t>(debugInfo.pid)].clear();
+ }
+ });
+ if (!debugRet.isOk()) {
+ mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+ << "debugging information cannot be retrieved:"
+ << debugRet.description() << std::endl;
+ status |= DUMP_BINDERIZED_ERROR;
+ }
+ }
+ for (auto &pair : allPids) {
+ pid_t serverPid = pair.first;
+ if (!getReferencedPids(serverPid, &allPids[serverPid])) {
+ mErr << "Warning: no information for PID " << serverPid
+ << ", are you root?" << std::endl;
+ status |= DUMP_BINDERIZED_ERROR;
+ }
+ }
+ for (const auto &fqInstanceName : fqInstanceNames) {
+ auto it = allDebugInfos.find(fqInstanceName);
+ if (it == allDebugInfos.end()) {
+ putEntry(HWSERVICEMANAGER_LIST, {
+ .interfaceName = fqInstanceName,
+ .transport = mode,
+ .serverPid = NO_PID,
+ .serverObjectAddress = NO_PTR,
+ .clientPids = {},
+ .arch = ARCH_UNKNOWN
+ });
+ continue;
+ }
+ const DebugInfo &info = it->second;
+ putEntry(HWSERVICEMANAGER_LIST, {
+ .interfaceName = fqInstanceName,
+ .transport = mode,
+ .serverPid = info.pid,
+ .serverObjectAddress = info.ptr,
+ .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
+ ? Pids{} : allPids[info.pid][info.ptr],
+ .arch = fromBaseArchitecture(info.arch),
+ });
+ }
+ return status;
+}
+
+Status ListCommand::fetch() {
+ Status status = OK;
+ auto bManager = mLshal.serviceManager();
+ if (bManager == nullptr) {
+ mErr << "Failed to get defaultServiceManager()!" << std::endl;
+ status |= NO_BINDERIZED_MANAGER;
+ } else {
+ status |= fetchBinderized(bManager);
+ // Passthrough PIDs are registered to the binderized manager as well.
+ status |= fetchPassthrough(bManager);
+ }
+
+ auto pManager = mLshal.passthroughManager();
+ if (pManager == nullptr) {
+ mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
+ status |= NO_PASSTHROUGH_MANAGER;
+ } else {
+ status |= fetchAllLibraries(pManager);
+ }
+ return status;
+}
+
+Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
+ static struct option longOptions[] = {
+ // long options with short alternatives
+ {"help", no_argument, 0, 'h' },
+ {"interface", no_argument, 0, 'i' },
+ {"transport", no_argument, 0, 't' },
+ {"arch", no_argument, 0, 'r' },
+ {"pid", no_argument, 0, 'p' },
+ {"address", no_argument, 0, 'a' },
+ {"clients", no_argument, 0, 'c' },
+ {"cmdline", no_argument, 0, 'm' },
+ {"debug", optional_argument, 0, 'd' },
+
+ // long options without short alternatives
+ {"sort", required_argument, 0, 's' },
+ {"init-vintf",optional_argument, 0, 'v' },
+ { 0, 0, 0, 0 }
+ };
+
+ int optionIndex;
+ int c;
+ // Lshal::parseArgs has set optind to the next option to parse
+ for (;;) {
+ // using getopt_long in case we want to add other options in the future
+ c = getopt_long(arg.argc, arg.argv,
+ "hitrpacmd", longOptions, &optionIndex);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 's': {
+ if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
+ mSortColumn = TableEntry::sortByInterfaceName;
+ } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
+ mSortColumn = TableEntry::sortByServerPid;
+ } else {
+ mErr << "Unrecognized sorting column: " << optarg << std::endl;
+ mLshal.usage(command);
+ return USAGE;
+ }
+ break;
+ }
+ case 'v': {
+ if (optarg) {
+ mFileOutput = new std::ofstream{optarg};
+ mOut = mFileOutput;
+ if (!mFileOutput.buf().is_open()) {
+ mErr << "Could not open file '" << optarg << "'." << std::endl;
+ return IO_ERROR;
+ }
+ }
+ mVintf = true;
+ }
+ case 'i': {
+ mSelectedColumns |= ENABLE_INTERFACE_NAME;
+ break;
+ }
+ case 't': {
+ mSelectedColumns |= ENABLE_TRANSPORT;
+ break;
+ }
+ case 'r': {
+ mSelectedColumns |= ENABLE_ARCH;
+ break;
+ }
+ case 'p': {
+ mSelectedColumns |= ENABLE_SERVER_PID;
+ break;
+ }
+ case 'a': {
+ mSelectedColumns |= ENABLE_SERVER_ADDR;
+ break;
+ }
+ case 'c': {
+ mSelectedColumns |= ENABLE_CLIENT_PIDS;
+ break;
+ }
+ case 'm': {
+ mEnableCmdlines = true;
+ break;
+ }
+ case 'd': {
+ mEmitDebugInfo = true;
+
+ if (optarg) {
+ mFileOutput = new std::ofstream{optarg};
+ mOut = mFileOutput;
+ if (!mFileOutput.buf().is_open()) {
+ mErr << "Could not open file '" << optarg << "'." << std::endl;
+ return IO_ERROR;
+ }
+ chown(optarg, AID_SHELL, AID_SHELL);
+ }
+ break;
+ }
+ case 'h': // falls through
+ default: // see unrecognized options
+ mLshal.usage(command);
+ return USAGE;
+ }
+ }
+ if (optind < arg.argc) {
+ // see non option
+ mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
+ }
+
+ if (mSelectedColumns == 0) {
+ mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
+ }
+ return OK;
+}
+
+Status ListCommand::main(const std::string &command, const Arg &arg) {
+ Status status = parseArgs(command, arg);
+ if (status != OK) {
+ return status;
+ }
+ status = fetch();
+ postprocess();
+ dump();
+ return status;
+}
+
+} // namespace lshal
+} // namespace android
+
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
new file mode 100644
index 0000000..42c965f
--- /dev/null
+++ b/cmds/lshal/ListCommand.h
@@ -0,0 +1,101 @@
+/*
+ * 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 FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
+
+#include <stdint.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+
+#include "NullableOStream.h"
+#include "TableEntry.h"
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal;
+
+class ListCommand {
+public:
+ ListCommand(Lshal &lshal);
+ Status main(const std::string &command, const Arg &arg);
+private:
+ Status parseArgs(const std::string &command, const Arg &arg);
+ Status fetch();
+ void postprocess();
+ void dump();
+ void putEntry(TableEntrySource source, TableEntry &&entry);
+ Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+ Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+ Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+ bool getReferencedPids(
+ pid_t serverPid, std::map<uint64_t, Pids> *objects) const;
+ void dumpTable();
+ void dumpVintf() const;
+ void printLine(
+ const std::string &interfaceName,
+ const std::string &transport,
+ const std::string &arch,
+ const std::string &server,
+ const std::string &serverCmdline,
+ const std::string &address, const std::string &clients,
+ const std::string &clientCmdlines) const ;
+ // Return /proc/{pid}/cmdline if it exists, else empty string.
+ const std::string &getCmdline(pid_t pid);
+ // Call getCmdline on all pid in pids. If it returns empty string, the process might
+ // have died, and the pid is removed from pids.
+ void removeDeadProcesses(Pids *pids);
+ void forEachTable(const std::function<void(Table &)> &f);
+ void forEachTable(const std::function<void(const Table &)> &f) const;
+
+ Lshal &mLshal;
+
+ Table mServicesTable{};
+ Table mPassthroughRefTable{};
+ Table mImplementationsTable{};
+
+ NullableOStream<std::ostream> mErr;
+ NullableOStream<std::ostream> mOut;
+ NullableOStream<std::ofstream> mFileOutput = nullptr;
+ TableEntryCompare mSortColumn = nullptr;
+ TableEntrySelect mSelectedColumns = 0;
+ // If true, cmdlines will be printed instead of pid.
+ bool mEnableCmdlines = false;
+
+ // If true, calls IBase::debug(...) on each service.
+ bool mEmitDebugInfo = false;
+
+ bool mVintf = false;
+ // If an entry does not exist, need to ask /proc/{pid}/cmdline to get it.
+ // If an entry exist but is an empty string, process might have died.
+ // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
+ std::map<pid_t, std::string> mCmdlines;
+
+ DISALLOW_COPY_AND_ASSIGN(ListCommand);
+};
+
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
new file mode 100644
index 0000000..9db42f1
--- /dev/null
+++ b/cmds/lshal/Lshal.cpp
@@ -0,0 +1,256 @@
+/*
+ * 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 LOG_TAG "lshal"
+#include <android-base/logging.h>
+
+#include "Lshal.h"
+
+#include <set>
+#include <string>
+
+#include <hidl/ServiceManagement.h>
+
+#include "DebugCommand.h"
+#include "ListCommand.h"
+#include "PipeRelay.h"
+
+namespace android {
+namespace lshal {
+
+using ::android::hidl::manager::V1_0::IServiceManager;
+
+Lshal::Lshal()
+ : mOut(std::cout), mErr(std::cerr),
+ mServiceManager(::android::hardware::defaultServiceManager()),
+ mPassthroughManager(::android::hardware::getPassthroughServiceManager()) {
+}
+
+Lshal::Lshal(std::ostream &out, std::ostream &err,
+ sp<hidl::manager::V1_0::IServiceManager> serviceManager,
+ sp<hidl::manager::V1_0::IServiceManager> passthroughManager)
+ : mOut(out), mErr(err),
+ mServiceManager(serviceManager),
+ mPassthroughManager(passthroughManager) {
+
+}
+
+void Lshal::usage(const std::string &command) const {
+ static const std::string helpSummary =
+ "lshal: List and debug HALs.\n"
+ "\n"
+ "commands:\n"
+ " help Print help message\n"
+ " list list HALs\n"
+ " debug debug a specified HAL\n"
+ "\n"
+ "If no command is specified, `list` is the default.\n";
+
+ static const std::string list =
+ "list:\n"
+ " lshal\n"
+ " lshal list\n"
+ " List all hals with default ordering and columns (`lshal list -ipc`)\n"
+ " lshal list [-h|--help]\n"
+ " -h, --help: Print help message for list (`lshal help list`)\n"
+ " lshal [list] [--interface|-i] [--transport|-t] [-r|--arch]\n"
+ " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]\n"
+ " [--sort={interface|i|pid|p}] [--init-vintf[=<output file>]]\n"
+ " [--debug|-d[=<output file>]]\n"
+ " -i, --interface: print the interface name column\n"
+ " -n, --instance: print the instance name column\n"
+ " -t, --transport: print the transport mode column\n"
+ " -r, --arch: print if the HAL is in 64-bit or 32-bit\n"
+ " -p, --pid: print the server PID, or server cmdline if -m is set\n"
+ " -a, --address: print the server object address column\n"
+ " -c, --clients: print the client PIDs, or client cmdlines if -m is set\n"
+ " -m, --cmdline: print cmdline instead of PIDs\n"
+ " -d[=<output file>], --debug[=<output file>]: emit debug info from \n"
+ " IBase::debug with empty options\n"
+ " --sort=i, --sort=interface: sort by interface name\n"
+ " --sort=p, --sort=pid: sort by server pid\n"
+ " --init-vintf=<output file>: form a skeleton HAL manifest to specified\n"
+ " file, or stdout if no file specified.\n";
+
+ static const std::string debug =
+ "debug:\n"
+ " lshal debug <interface> [options [options [...]]] \n"
+ " Print debug information of a specified interface.\n"
+ " <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n"
+ " If instance name is missing `default` is used.\n"
+ " options: space separated options to IBase::debug.\n";
+
+ static const std::string help =
+ "help:\n"
+ " lshal -h\n"
+ " lshal --help\n"
+ " lshal help\n"
+ " Print this help message\n"
+ " lshal help list\n"
+ " Print help message for list\n"
+ " lshal help debug\n"
+ " Print help message for debug\n";
+
+ if (command == "list") {
+ mErr << list;
+ return;
+ }
+ if (command == "debug") {
+ mErr << debug;
+ return;
+ }
+
+ mErr << helpSummary << "\n" << list << "\n" << debug << "\n" << help;
+}
+
+// A unique_ptr type using a custom deleter function.
+template<typename T>
+using deleted_unique_ptr = std::unique_ptr<T, std::function<void(T *)> >;
+
+static hardware::hidl_vec<hardware::hidl_string> convert(const std::vector<std::string> &v) {
+ hardware::hidl_vec<hardware::hidl_string> hv;
+ hv.resize(v.size());
+ for (size_t i = 0; i < v.size(); ++i) {
+ hv[i].setToExternal(v[i].c_str(), v[i].size());
+ }
+ return hv;
+}
+
+Status Lshal::emitDebugInfo(
+ const std::string &interfaceName,
+ const std::string &instanceName,
+ const std::vector<std::string> &options,
+ std::ostream &out,
+ NullableOStream<std::ostream> err) const {
+ using android::hidl::base::V1_0::IBase;
+
+ hardware::Return<sp<IBase>> retBase = serviceManager()->get(interfaceName, instanceName);
+
+ if (!retBase.isOk()) {
+ std::string msg = "Cannot get " + interfaceName + "/" + instanceName + ": "
+ + retBase.description();
+ err << msg << std::endl;
+ LOG(ERROR) << msg;
+ return TRANSACTION_ERROR;
+ }
+
+ sp<IBase> base = retBase;
+ if (base == nullptr) {
+ std::string msg = interfaceName + "/" + instanceName + " does not exist, or "
+ + "no permission to connect.";
+ err << msg << std::endl;
+ LOG(ERROR) << msg;
+ return NO_INTERFACE;
+ }
+
+ PipeRelay relay(out);
+
+ if (relay.initCheck() != OK) {
+ std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
+ err << msg << std::endl;
+ LOG(ERROR) << msg;
+ return IO_ERROR;
+ }
+
+ deleted_unique_ptr<native_handle_t> fdHandle(
+ native_handle_create(1 /* numFds */, 0 /* numInts */),
+ native_handle_delete);
+
+ fdHandle->data[0] = relay.fd();
+
+ hardware::Return<void> ret = base->debug(fdHandle.get(), convert(options));
+
+ if (!ret.isOk()) {
+ std::string msg = "debug() FAILED on " + interfaceName + "/" + instanceName + ": "
+ + ret.description();
+ err << msg << std::endl;
+ LOG(ERROR) << msg;
+ return TRANSACTION_ERROR;
+ }
+ return OK;
+}
+
+Status Lshal::parseArgs(const Arg &arg) {
+ static std::set<std::string> sAllCommands{"list", "debug", "help"};
+ optind = 1;
+ if (optind >= arg.argc) {
+ // no options at all.
+ return OK;
+ }
+ mCommand = arg.argv[optind];
+ if (sAllCommands.find(mCommand) != sAllCommands.end()) {
+ ++optind;
+ return OK; // mCommand is set correctly
+ }
+
+ if (mCommand.size() > 0 && mCommand[0] == '-') {
+ // first argument is an option, set command to "" (which is recognized as "list")
+ mCommand = "";
+ return OK;
+ }
+
+ mErr << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl;
+ usage();
+ return USAGE;
+}
+
+void signalHandler(int sig) {
+ if (sig == SIGINT) {
+ int retVal;
+ pthread_exit(&retVal);
+ }
+}
+
+Status Lshal::main(const Arg &arg) {
+ // Allow SIGINT to terminate all threads.
+ signal(SIGINT, signalHandler);
+
+ Status status = parseArgs(arg);
+ if (status != OK) {
+ return status;
+ }
+ if (mCommand == "help") {
+ usage(optind < arg.argc ? arg.argv[optind] : "");
+ return USAGE;
+ }
+ // Default command is list
+ if (mCommand == "list" || mCommand == "") {
+ return ListCommand{*this}.main(mCommand, arg);
+ }
+ if (mCommand == "debug") {
+ return DebugCommand{*this}.main(mCommand, arg);
+ }
+ usage();
+ return USAGE;
+}
+
+NullableOStream<std::ostream> Lshal::err() const {
+ return mErr;
+}
+NullableOStream<std::ostream> Lshal::out() const {
+ return mOut;
+}
+
+const sp<IServiceManager> &Lshal::serviceManager() const {
+ return mServiceManager;
+}
+
+const sp<IServiceManager> &Lshal::passthroughManager() const {
+ return mPassthroughManager;
+}
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
new file mode 100644
index 0000000..00db5d0
--- /dev/null
+++ b/cmds/lshal/Lshal.h
@@ -0,0 +1,68 @@
+/*
+ * 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 FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_
+
+#include <iostream>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <utils/StrongPointer.h>
+
+#include "NullableOStream.h"
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal {
+public:
+ Lshal();
+ Lshal(std::ostream &out, std::ostream &err,
+ sp<hidl::manager::V1_0::IServiceManager> serviceManager,
+ sp<hidl::manager::V1_0::IServiceManager> passthroughManager);
+ Status main(const Arg &arg);
+ void usage(const std::string &command = "") const;
+ NullableOStream<std::ostream> err() const;
+ NullableOStream<std::ostream> out() const;
+ const sp<hidl::manager::V1_0::IServiceManager> &serviceManager() const;
+ const sp<hidl::manager::V1_0::IServiceManager> &passthroughManager() const;
+
+ Status emitDebugInfo(
+ const std::string &interfaceName,
+ const std::string &instanceName,
+ const std::vector<std::string> &options,
+ std::ostream &out,
+ NullableOStream<std::ostream> err) const;
+private:
+ Status parseArgs(const Arg &arg);
+ std::string mCommand;
+ Arg mCmdArgs;
+ NullableOStream<std::ostream> mOut;
+ NullableOStream<std::ostream> mErr;
+
+ sp<hidl::manager::V1_0::IServiceManager> mServiceManager;
+ sp<hidl::manager::V1_0::IServiceManager> mPassthroughManager;
+
+ DISALLOW_COPY_AND_ASSIGN(Lshal);
+};
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_
diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h
new file mode 100644
index 0000000..ab37a04
--- /dev/null
+++ b/cmds/lshal/NullableOStream.h
@@ -0,0 +1,73 @@
+/*
+ * 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 FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+
+#include <iostream>
+
+namespace android {
+namespace lshal {
+
+template<typename S>
+class NullableOStream {
+public:
+ NullableOStream(S &os) : mOs(&os) {}
+ NullableOStream(S *os) : mOs(os) {}
+ NullableOStream &operator=(S &os) {
+ mOs = &os;
+ return *this;
+ }
+ NullableOStream &operator=(S *os) {
+ mOs = os;
+ return *this;
+ }
+ template<typename Other>
+ NullableOStream &operator=(const NullableOStream<Other> &other) {
+ mOs = other.mOs;
+ return *this;
+ }
+
+ const NullableOStream &operator<<(std::ostream& (*pf)(std::ostream&)) const {
+ if (mOs) {
+ (*mOs) << pf;
+ }
+ return *this;
+ }
+ template<typename T>
+ const NullableOStream &operator<<(const T &rhs) const {
+ if (mOs) {
+ (*mOs) << rhs;
+ }
+ return *this;
+ }
+ S& buf() const {
+ return *mOs;
+ }
+ operator bool() const {
+ return mOs != nullptr;
+ }
+private:
+ template<typename>
+ friend class NullableOStream;
+
+ S *mOs = nullptr;
+};
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
new file mode 100644
index 0000000..54d19f6
--- /dev/null
+++ b/cmds/lshal/PipeRelay.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 "PipeRelay.h"
+
+#include <sys/socket.h>
+#include <utils/Thread.h>
+
+namespace android {
+namespace lshal {
+
+struct PipeRelay::RelayThread : public Thread {
+ explicit RelayThread(int fd, std::ostream &os);
+
+ bool threadLoop() override;
+
+private:
+ int mFd;
+ std::ostream &mOutStream;
+
+ DISALLOW_COPY_AND_ASSIGN(RelayThread);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os)
+ : mFd(fd),
+ mOutStream(os) {
+}
+
+bool PipeRelay::RelayThread::threadLoop() {
+ char buffer[1024];
+ ssize_t n = read(mFd, buffer, sizeof(buffer));
+
+ if (n <= 0) {
+ return false;
+ }
+
+ mOutStream.write(buffer, n);
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+PipeRelay::PipeRelay(std::ostream &os)
+ : mOutStream(os),
+ mInitCheck(NO_INIT) {
+ int res = socketpair(AF_UNIX, SOCK_STREAM, 0 /* protocol */, mFds);
+
+ if (res < 0) {
+ mInitCheck = -errno;
+ return;
+ }
+
+ mThread = new RelayThread(mFds[0], os);
+ mInitCheck = mThread->run("RelayThread");
+}
+
+void PipeRelay::CloseFd(int *fd) {
+ if (*fd >= 0) {
+ close(*fd);
+ *fd = -1;
+ }
+}
+
+PipeRelay::~PipeRelay() {
+ if (mFds[1] >= 0) {
+ shutdown(mFds[1], SHUT_WR);
+ }
+
+ if (mFds[0] >= 0) {
+ shutdown(mFds[0], SHUT_RD);
+ }
+
+ if (mThread != NULL) {
+ mThread->join();
+ mThread.clear();
+ }
+
+ CloseFd(&mFds[1]);
+ CloseFd(&mFds[0]);
+}
+
+status_t PipeRelay::initCheck() const {
+ return mInitCheck;
+}
+
+int PipeRelay::fd() const {
+ return mFds[1];
+}
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
new file mode 100644
index 0000000..76b2b23
--- /dev/null
+++ b/cmds/lshal/PipeRelay.h
@@ -0,0 +1,59 @@
+/*
+ * 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 FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_
+
+#define FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_
+
+#include <android-base/macros.h>
+#include <ostream>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace lshal {
+
+/* Creates an AF_UNIX socketpair and spawns a thread that relays any data
+ * written to the "write"-end of the pair to the specified output stream "os".
+ */
+struct PipeRelay {
+ explicit PipeRelay(std::ostream &os);
+ ~PipeRelay();
+
+ status_t initCheck() const;
+
+ // Returns the file descriptor corresponding to the "write"-end of the
+ // connection.
+ int fd() const;
+
+private:
+ struct RelayThread;
+
+ std::ostream &mOutStream;
+ status_t mInitCheck;
+ int mFds[2];
+ sp<RelayThread> mThread;
+
+ static void CloseFd(int *fd);
+
+ DISALLOW_COPY_AND_ASSIGN(PipeRelay);
+};
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_
+
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
new file mode 100644
index 0000000..9ae8f78
--- /dev/null
+++ b/cmds/lshal/TableEntry.h
@@ -0,0 +1,96 @@
+/*
+ * 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 FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <iostream>
+
+namespace android {
+namespace lshal {
+
+using Pids = std::vector<int32_t>;
+
+enum : unsigned int {
+ HWSERVICEMANAGER_LIST, // through defaultServiceManager()->list()
+ PTSERVICEMANAGER_REG_CLIENT, // through registerPassthroughClient
+ LIST_DLLIB, // through listing dynamic libraries
+};
+using TableEntrySource = unsigned int;
+
+enum : unsigned int {
+ ARCH_UNKNOWN = 0,
+ ARCH32 = 1 << 0,
+ ARCH64 = 1 << 1,
+ ARCH_BOTH = ARCH32 | ARCH64
+};
+using Architecture = unsigned int;
+
+struct TableEntry {
+ std::string interfaceName;
+ std::string transport;
+ int32_t serverPid;
+ std::string serverCmdline;
+ uint64_t serverObjectAddress;
+ Pids clientPids;
+ std::vector<std::string> clientCmdlines;
+ Architecture arch;
+
+ static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
+ return a.interfaceName < b.interfaceName;
+ };
+ static bool sortByServerPid(const TableEntry &a, const TableEntry &b) {
+ return a.serverPid < b.serverPid;
+ };
+};
+
+struct Table {
+ using Entries = std::vector<TableEntry>;
+ std::string description;
+ Entries entries;
+
+ Entries::iterator begin() { return entries.begin(); }
+ Entries::const_iterator begin() const { return entries.begin(); }
+ Entries::iterator end() { return entries.end(); }
+ Entries::const_iterator end() const { return entries.end(); }
+};
+
+using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>;
+
+enum : unsigned int {
+ ENABLE_INTERFACE_NAME = 1 << 0,
+ ENABLE_TRANSPORT = 1 << 1,
+ ENABLE_SERVER_PID = 1 << 2,
+ ENABLE_SERVER_ADDR = 1 << 3,
+ ENABLE_CLIENT_PIDS = 1 << 4,
+ ENABLE_ARCH = 1 << 5
+};
+
+using TableEntrySelect = unsigned int;
+
+enum {
+ NO_PID = -1,
+ NO_PTR = 0
+};
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
new file mode 100644
index 0000000..ca477bf
--- /dev/null
+++ b/cmds/lshal/Timeout.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <condition_variable>
+#include <chrono>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include <hidl/Status.h>
+
+namespace android {
+namespace lshal {
+
+static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500};
+
+class BackgroundTaskState {
+public:
+ BackgroundTaskState(std::function<void(void)> &&func)
+ : mFunc(std::forward<decltype(func)>(func)) {}
+ void notify() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mFinished = true;
+ lock.unlock();
+ mCondVar.notify_all();
+ }
+ template<class C, class D>
+ bool wait(std::chrono::time_point<C, D> end) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
+ return mFinished;
+ }
+ void operator()() {
+ mFunc();
+ }
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondVar;
+ bool mFinished = false;
+ std::function<void(void)> mFunc;
+};
+
+void *callAndNotify(void *data) {
+ BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
+ state();
+ state.notify();
+ return NULL;
+}
+
+template<class R, class P>
+bool timeout(std::chrono::duration<R, P> delay, std::function<void(void)> &&func) {
+ auto now = std::chrono::system_clock::now();
+ BackgroundTaskState state{std::forward<decltype(func)>(func)};
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, callAndNotify, &state)) {
+ std::cerr << "FATAL: could not create background thread." << std::endl;
+ return false;
+ }
+ bool success = state.wait(now + delay);
+ if (!success) {
+ pthread_kill(thread, SIGINT);
+ }
+ pthread_join(thread, NULL);
+ return success;
+}
+
+template<class Function, class I, class... Args>
+typename std::result_of<Function(I *, Args...)>::type
+timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) {
+ using ::android::hardware::Status;
+ typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
+ auto boundFunc = std::bind(std::forward<Function>(func),
+ interfaceObject.get(), std::forward<Args>(args)...);
+ bool success = timeout(IPC_CALL_WAIT, [&ret, &boundFunc] {
+ ret = std::move(boundFunc());
+ });
+ if (!success) {
+ return Status::fromStatusT(TIMED_OUT);
+ }
+ return ret;
+}
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/main.cpp b/cmds/lshal/main.cpp
new file mode 100644
index 0000000..366c938
--- /dev/null
+++ b/cmds/lshal/main.cpp
@@ -0,0 +1,22 @@
+/*
+ * 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 "Lshal.h"
+
+int main(int argc, char **argv) {
+ using namespace ::android::lshal;
+ return Lshal{}.main(Arg{argc, argv});
+}
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
new file mode 100644
index 0000000..972d508
--- /dev/null
+++ b/cmds/lshal/test.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 LOG_TAG "Lshal"
+#include <android-base/logging.h>
+
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <android/hardware/tests/baz/1.0/IQuux.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "Lshal.h"
+
+#define NELEMS(array) static_cast<int>(sizeof(array) / sizeof(array[0]))
+
+using namespace testing;
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hidl::manager::V1_0::IServiceManager;
+using ::android::hidl::manager::V1_0::IServiceNotification;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+namespace hardware {
+namespace tests {
+namespace baz {
+namespace V1_0 {
+namespace implementation {
+struct Quux : android::hardware::tests::baz::V1_0::IQuux {
+ ::android::hardware::Return<void> debug(const hidl_handle& hh, const hidl_vec<hidl_string>& options) override {
+ const native_handle_t *handle = hh.getNativeHandle();
+ if (handle->numFds < 1) {
+ return Void();
+ }
+ int fd = handle->data[0];
+ std::string content{descriptor};
+ for (const auto &option : options) {
+ content += "\n";
+ content += option.c_str();
+ }
+ ssize_t written = write(fd, content.c_str(), content.size());
+ if (written != (ssize_t)content.size()) {
+ LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < "
+ << content.size() << " bytes, errno = " << errno;
+ }
+ return Void();
+ }
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace baz
+} // namespace tests
+} // namespace hardware
+
+namespace lshal {
+
+
+class MockServiceManager : public IServiceManager {
+public:
+ template<typename T>
+ using R = ::android::hardware::Return<T>;
+ using String = const hidl_string&;
+ ~MockServiceManager() = default;
+
+#define MOCK_METHOD_CB(name) MOCK_METHOD1(name, R<void>(IServiceManager::name##_cb))
+
+ MOCK_METHOD2(get, R<sp<IBase>>(String, String));
+ MOCK_METHOD2(add, R<bool>(String, const sp<IBase>&));
+ MOCK_METHOD2(getTransport, R<IServiceManager::Transport>(String, String));
+ MOCK_METHOD_CB(list);
+ MOCK_METHOD2(listByInterface, R<void>(String, listByInterface_cb));
+ MOCK_METHOD3(registerForNotifications, R<bool>(String, String, const sp<IServiceNotification>&));
+ MOCK_METHOD_CB(debugDump);
+ MOCK_METHOD2(registerPassthroughClient, R<void>(String, String));
+ MOCK_METHOD_CB(interfaceChain);
+ MOCK_METHOD2(debug, R<void>(const hidl_handle&, const hidl_vec<hidl_string>&));
+ MOCK_METHOD_CB(interfaceDescriptor);
+ MOCK_METHOD_CB(getHashChain);
+ MOCK_METHOD0(setHalInstrumentation, R<void>());
+ MOCK_METHOD2(linkToDeath, R<bool>(const sp<hidl_death_recipient>&, uint64_t));
+ MOCK_METHOD0(ping, R<void>());
+ MOCK_METHOD_CB(getDebugInfo);
+ MOCK_METHOD0(notifySyspropsChanged, R<void>());
+ MOCK_METHOD1(unlinkToDeath, R<bool>(const sp<hidl_death_recipient>&));
+
+};
+
+class LshalTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ using ::android::hardware::tests::baz::V1_0::IQuux;
+ using ::android::hardware::tests::baz::V1_0::implementation::Quux;
+
+ err.str("");
+ out.str("");
+ serviceManager = new testing::NiceMock<MockServiceManager>();
+ ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
+ [](const auto &iface, const auto &inst) -> ::android::hardware::Return<sp<IBase>> {
+ if (iface == IQuux::descriptor && inst == "default")
+ return new Quux();
+ return nullptr;
+ }));
+ }
+ void TearDown() override {}
+
+ std::stringstream err;
+ std::stringstream out;
+ sp<MockServiceManager> serviceManager;
+};
+
+TEST_F(LshalTest, Debug) {
+ const char *args[] = {
+ "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar"
+ };
+ EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager)
+ .main({NELEMS(args), const_cast<char **>(args)}));
+ EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar"));
+ EXPECT_THAT(err.str(), IsEmpty());
+}
+
+TEST_F(LshalTest, Debug2) {
+ const char *args[] = {
+ "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux"
+ };
+ EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager)
+ .main({NELEMS(args), const_cast<char **>(args)}));
+ EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux"));
+ EXPECT_THAT(err.str(), IsEmpty());
+}
+
+TEST_F(LshalTest, Debug3) {
+ const char *args[] = {
+ "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist",
+ };
+ EXPECT_NE(0u, Lshal(out, err, serviceManager, serviceManager)
+ .main({NELEMS(args), const_cast<char **>(args)}));
+ EXPECT_THAT(err.str(), HasSubstr("does not exist"));
+}
+
+} // namespace lshal
+} // namespace android
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleMock(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/cmds/lshal/utils.cpp b/cmds/lshal/utils.cpp
new file mode 100644
index 0000000..5550721
--- /dev/null
+++ b/cmds/lshal/utils.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "utils.h"
+
+namespace android {
+namespace lshal {
+
+std::string toHexString(uint64_t t) {
+ std::ostringstream os;
+ os << std::hex << std::setfill('0') << std::setw(16) << t;
+ return os.str();
+}
+
+std::vector<std::string> split(const std::string &s, char c) {
+ std::vector<std::string> components{};
+ size_t startPos = 0;
+ size_t matchPos;
+ while ((matchPos = s.find(c, startPos)) != std::string::npos) {
+ components.push_back(s.substr(startPos, matchPos - startPos));
+ startPos = matchPos + 1;
+ }
+
+ if (startPos <= s.length()) {
+ components.push_back(s.substr(startPos));
+ }
+ return components;
+}
+
+void replaceAll(std::string *s, char from, char to) {
+ for (size_t i = 0; i < s->size(); ++i) {
+ if (s->at(i) == from) {
+ s->at(i) = to;
+ }
+ }
+}
+
+} // namespace lshal
+} // namespace android
+
diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h
new file mode 100644
index 0000000..45b922c
--- /dev/null
+++ b/cmds/lshal/utils.h
@@ -0,0 +1,82 @@
+/*
+ * 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 FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_
+
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace lshal {
+
+enum : unsigned int {
+ OK = 0,
+ USAGE = 1 << 0,
+ NO_BINDERIZED_MANAGER = 1 << 1,
+ NO_PASSTHROUGH_MANAGER = 1 << 2,
+ DUMP_BINDERIZED_ERROR = 1 << 3,
+ DUMP_PASSTHROUGH_ERROR = 1 << 4,
+ DUMP_ALL_LIBS_ERROR = 1 << 5,
+ IO_ERROR = 1 << 6,
+ NO_INTERFACE = 1 << 7,
+ TRANSACTION_ERROR = 1 << 8,
+};
+using Status = unsigned int;
+
+struct Arg {
+ int argc;
+ char **argv;
+};
+
+template <typename A>
+std::string join(const A &components, const std::string &separator) {
+ std::stringstream out;
+ bool first = true;
+ for (const auto &component : components) {
+ if (!first) {
+ out << separator;
+ }
+ out << component;
+
+ first = false;
+ }
+ return out.str();
+}
+
+std::string toHexString(uint64_t t);
+
+template<typename String>
+std::pair<String, String> splitFirst(const String &s, char c) {
+ const char *pos = strchr(s.c_str(), c);
+ if (pos == nullptr) {
+ return {s, {}};
+ }
+ return {String(s.c_str(), pos - s.c_str()), String(pos + 1)};
+}
+
+std::vector<std::string> split(const std::string &s, char c);
+
+void replaceAll(std::string *s, char from, char to);
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_
diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp
new file mode 100644
index 0000000..b703ed4
--- /dev/null
+++ b/cmds/service/Android.bp
@@ -0,0 +1,26 @@
+cc_binary {
+ name: "service",
+
+ srcs: ["service.cpp"],
+
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ ],
+
+ cflags: ["-DXP_UNIX"],
+}
+
+cc_binary {
+ name: "vndservice",
+
+ proprietary: true,
+ srcs: ["service.cpp"],
+
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ ],
+
+ cflags: ["-DXP_UNIX", "-DVENDORSERVICES"],
+}
diff --git a/cmds/service/Android.mk b/cmds/service/Android.mk
deleted file mode 100644
index 275bbb2..0000000
--- a/cmds/service/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- service.cpp
-
-LOCAL_SHARED_LIBRARIES := libutils libbinder
-
-ifeq ($(TARGET_OS),linux)
- LOCAL_CFLAGS += -DXP_UNIX
- #LOCAL_SHARED_LIBRARIES += librt
-endif
-
-LOCAL_MODULE:= service
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 428b87c..bc11256 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -68,13 +68,6 @@
int main(int argc, char* const argv[])
{
- sp<IServiceManager> sm = defaultServiceManager();
- fflush(stdout);
- if (sm == NULL) {
- aerr << "service: Unable to get default service manager!" << endl;
- return 20;
- }
-
bool wantsUsage = false;
int result = 0;
@@ -95,6 +88,15 @@
break;
}
}
+#ifdef VENDORSERVICES
+ ProcessState::initWithDriver("/dev/vndbinder");
+#endif
+ sp<IServiceManager> sm = defaultServiceManager();
+ fflush(stdout);
+ if (sm == NULL) {
+ aerr << "service: Unable to get default service manager!" << endl;
+ return 20;
+ }
if (optind >= argc) {
wantsUsage = true;
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
new file mode 100644
index 0000000..39d92a7
--- /dev/null
+++ b/cmds/servicemanager/Android.bp
@@ -0,0 +1,52 @@
+cc_defaults {
+ name: "servicemanager_flags",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ product_variables: {
+ binder32bit: {
+ cflags: ["-DBINDER_IPC_32BIT=1"],
+ },
+ },
+
+ shared_libs: ["liblog"],
+}
+
+cc_binary {
+ name: "bctest",
+ defaults: ["servicemanager_flags"],
+ srcs: [
+ "bctest.c",
+ "binder.c",
+ ],
+}
+
+cc_binary {
+ name: "servicemanager",
+ defaults: ["servicemanager_flags"],
+ srcs: [
+ "service_manager.c",
+ "binder.c",
+ ],
+ shared_libs: ["libcutils", "libselinux"],
+ init_rc: ["servicemanager.rc"],
+}
+
+cc_binary {
+ name: "vndservicemanager",
+ defaults: ["servicemanager_flags"],
+ vendor: true,
+ srcs: [
+ "service_manager.c",
+ "binder.c",
+ ],
+ cflags: [
+ "-DVENDORSERVICEMANAGER=1",
+ ],
+ shared_libs: ["libcutils"],
+ static_libs: ["libselinux"],
+ init_rc: ["vndservicemanager.rc"],
+}
diff --git a/cmds/servicemanager/Android.mk b/cmds/servicemanager/Android.mk
deleted file mode 100644
index b214f19..0000000
--- a/cmds/servicemanager/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-svc_c_flags = \
- -Wall -Wextra -Werror \
-
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-svc_c_flags += -DBINDER_IPC_32BIT=1
-endif
-endif
-
-include $(CLEAR_VARS)
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_SRC_FILES := bctest.c binder.c
-LOCAL_CFLAGS += $(svc_c_flags)
-LOCAL_MODULE := bctest
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SHARED_LIBRARIES := liblog libcutils libselinux
-LOCAL_SRC_FILES := service_manager.c binder.c
-LOCAL_CFLAGS += $(svc_c_flags)
-LOCAL_MODULE := servicemanager
-LOCAL_INIT_RC := servicemanager.rc
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c
index 6466654..354df67 100644
--- a/cmds/servicemanager/bctest.c
+++ b/cmds/servicemanager/bctest.c
@@ -62,7 +62,7 @@
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
- bs = binder_open(128*1024);
+ bs = binder_open("/dev/binder", 128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 27c461a..93a18fc 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -1,14 +1,18 @@
/* Copyright 2008 The Android Open Source Project
*/
+#define LOG_TAG "Binder"
+
+#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <sys/mman.h>
+#include <unistd.h>
+
+#include <log/log.h>
#include "binder.h"
@@ -16,9 +20,6 @@
#define TRACE 0
-#define LOG_TAG "Binder"
-#include <cutils/log.h>
-
void bio_init_from_txn(struct binder_io *io, struct binder_transaction_data *txn);
#if TRACE
@@ -93,7 +94,7 @@
size_t mapsize;
};
-struct binder_state *binder_open(size_t mapsize)
+struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs;
struct binder_version vers;
@@ -104,10 +105,10 @@
return NULL;
}
- bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
+ bs->fd = open(driver, O_RDWR | O_CLOEXEC);
if (bs->fd < 0) {
- fprintf(stderr,"binder: cannot open device (%s)\n",
- strerror(errno));
+ fprintf(stderr,"binder: cannot open %s (%s)\n",
+ driver, strerror(errno));
goto fail_open;
}
diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h
index 7915fc2..c95b33f 100644
--- a/cmds/servicemanager/binder.h
+++ b/cmds/servicemanager/binder.h
@@ -5,7 +5,7 @@
#define _BINDER_H_
#include <sys/ioctl.h>
-#include <linux/binder.h>
+#include <linux/android/binder.h>
struct binder_state;
@@ -46,7 +46,7 @@
struct binder_io *msg,
struct binder_io *reply);
-struct binder_state *binder_open(size_t mapsize);
+struct binder_state *binder_open(const char* driver, size_t mapsize);
void binder_close(struct binder_state *bs);
/* initiate a blocking binder call
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 68e3ceb..45bb1d0 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -17,13 +17,12 @@
#include "binder.h"
-#if 0
-#define ALOGI(x...) fprintf(stderr, "svcmgr: " x)
-#define ALOGE(x...) fprintf(stderr, "svcmgr: " x)
+#ifdef VENDORSERVICEMANAGER
+#define LOG_TAG "VendorServiceManager"
#else
#define LOG_TAG "ServiceManager"
-#include <cutils/log.h>
#endif
+#include <log/log.h>
struct audit_data {
pid_t pid;
@@ -60,7 +59,6 @@
return 1;
}
-static int selinux_enabled;
static char *service_manager_context;
static struct selabel_handle* sehandle;
@@ -89,10 +87,6 @@
static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm)
{
- if (selinux_enabled <= 0) {
- return true;
- }
-
return check_mac_perms(spid, uid, service_manager_context, perm, NULL);
}
@@ -101,10 +95,6 @@
bool allowed;
char *tctx = NULL;
- if (selinux_enabled <= 0) {
- return true;
- }
-
if (!sehandle) {
ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n");
abort();
@@ -369,13 +359,28 @@
return 0;
}
-int main()
+int main(int argc, char** argv)
{
struct binder_state *bs;
+ union selinux_callback cb;
+ char *driver;
- bs = binder_open(128*1024);
+ if (argc > 1) {
+ driver = argv[1];
+ } else {
+ driver = "/dev/binder";
+ }
+
+ bs = binder_open(driver, 128*1024);
if (!bs) {
- ALOGE("failed to open binder driver\n");
+#ifdef VENDORSERVICEMANAGER
+ ALOGW("failed to open binder driver %s\n", driver);
+ while (true) {
+ sleep(UINT_MAX);
+ }
+#else
+ ALOGE("failed to open binder driver %s\n", driver);
+#endif
return -1;
}
@@ -384,28 +389,29 @@
return -1;
}
- selinux_enabled = is_selinux_enabled();
- sehandle = selinux_android_service_context_handle();
- selinux_status_open(true);
-
- if (selinux_enabled > 0) {
- if (sehandle == NULL) {
- ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
- abort();
- }
-
- if (getcon(&service_manager_context) != 0) {
- ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
- 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);
+#ifdef VENDORSERVICEMANAGER
+ sehandle = selinux_android_vendor_service_context_handle();
+#else
+ sehandle = selinux_android_service_context_handle();
+#endif
+ selinux_status_open(true);
+
+ if (sehandle == NULL) {
+ ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
+ abort();
+ }
+
+ if (getcon(&service_manager_context) != 0) {
+ ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
+ abort();
+ }
+
+
binder_loop(bs, svcmgr_handler);
return 0;
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index aee7bd8..aec211a 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -1,5 +1,5 @@
service servicemanager /system/bin/servicemanager
- class core
+ class core animation
user system
group system readproc
critical
@@ -12,4 +12,3 @@
onrestart restart drm
onrestart restart cameraserver
writepid /dev/cpuset/system-background/tasks
-
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
new file mode 100644
index 0000000..d5ddaaf
--- /dev/null
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -0,0 +1,6 @@
+service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder
+ class core
+ user system
+ group system readproc
+ writepid /dev/cpuset/system-background/tasks
+
diff --git a/cmds/surfacereplayer/proto/Android.mk b/cmds/surfacereplayer/proto/Android.mk
new file mode 100644
index 0000000..3cf1148
--- /dev/null
+++ b/cmds/surfacereplayer/proto/Android.mk
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-proto-files-under, src)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+
+LOCAL_MODULE := libtrace_proto
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
new file mode 100644
index 0000000..0bc08a9
--- /dev/null
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -0,0 +1,179 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message Trace {
+ repeated Increment increment = 1;
+}
+
+message Increment {
+ required int64 time_stamp = 1;
+
+ oneof increment {
+ Transaction transaction = 2;
+ SurfaceCreation surface_creation = 3;
+ SurfaceDeletion surface_deletion = 4;
+ BufferUpdate buffer_update = 5;
+ VSyncEvent vsync_event = 6;
+ DisplayCreation display_creation = 7;
+ DisplayDeletion display_deletion = 8;
+ PowerModeUpdate power_mode_update = 9;
+ }
+}
+
+message Transaction {
+ repeated SurfaceChange surface_change = 1;
+ repeated DisplayChange display_change = 2;
+
+ required bool synchronous = 3;
+ required bool animation = 4;
+}
+
+message SurfaceChange {
+ required int32 id = 1;
+
+ oneof SurfaceChange {
+ PositionChange position = 2;
+ SizeChange size = 3;
+ AlphaChange alpha = 4;
+ LayerChange layer = 5;
+ CropChange crop = 6;
+ FinalCropChange final_crop = 7;
+ MatrixChange matrix = 8;
+ OverrideScalingModeChange override_scaling_mode = 9;
+ TransparentRegionHintChange transparent_region_hint = 10;
+ LayerStackChange layer_stack = 11;
+ HiddenFlagChange hidden_flag = 12;
+ OpaqueFlagChange opaque_flag = 13;
+ SecureFlagChange secure_flag = 14;
+ DeferredTransactionChange deferred_transaction = 15;
+ }
+}
+
+message PositionChange {
+ required float x = 1;
+ required float y = 2;
+}
+
+message SizeChange {
+ required uint32 w = 1;
+ required uint32 h = 2;
+}
+
+message AlphaChange {
+ required float alpha = 1;
+}
+
+message LayerChange {
+ required uint32 layer = 1;
+}
+
+message CropChange {
+ required Rectangle rectangle = 1;
+}
+
+message FinalCropChange {
+ required Rectangle rectangle = 1;
+}
+
+message MatrixChange {
+ required float dsdx = 1;
+ required float dtdx = 2;
+ required float dsdy = 3;
+ required float dtdy = 4;
+}
+
+message OverrideScalingModeChange {
+ required int32 override_scaling_mode = 1;
+}
+
+message TransparentRegionHintChange {
+ repeated Rectangle region = 1;
+}
+
+message LayerStackChange {
+ required uint32 layer_stack = 1;
+}
+
+message HiddenFlagChange {
+ required bool hidden_flag = 1;
+}
+
+message OpaqueFlagChange {
+ required bool opaque_flag = 1;
+}
+
+message SecureFlagChange {
+ required bool secure_flag = 1;
+}
+
+message DeferredTransactionChange {
+ required int32 layer_id = 1;
+ required uint64 frame_number = 2;
+}
+
+message DisplayChange {
+ required int32 id = 1;
+
+ oneof DisplayChange {
+ DispSurfaceChange surface = 2;
+ LayerStackChange layer_stack = 3;
+ SizeChange size = 4;
+ ProjectionChange projection = 5;
+ }
+}
+
+message DispSurfaceChange {
+ required uint64 buffer_queue_id = 1;
+ required string buffer_queue_name = 2;
+}
+
+message ProjectionChange {
+ required int32 orientation = 1;
+ required Rectangle viewport = 2;
+ required Rectangle frame = 3;
+}
+
+message Rectangle {
+ required int32 left = 1;
+ required int32 top = 2;
+ required int32 right = 3;
+ required int32 bottom = 4;
+}
+
+message SurfaceCreation {
+ required int32 id = 1;
+ required string name = 2;
+ required uint32 w = 3;
+ required uint32 h = 4;
+}
+
+message SurfaceDeletion {
+ required int32 id = 1;
+}
+
+message BufferUpdate {
+ required int32 id = 1;
+ required uint32 w = 2;
+ required uint32 h = 3;
+ required uint64 frame_number = 4;
+}
+
+message VSyncEvent {
+ required int64 when = 1;
+}
+
+message DisplayCreation {
+ required int32 id = 1;
+ required string name = 2;
+ required int32 type = 3;
+ required bool is_secure = 4;
+}
+
+message DisplayDeletion {
+ required int32 id = 1;
+}
+
+message PowerModeUpdate {
+ required int32 id = 1;
+ required int32 mode = 2;
+}
diff --git a/cmds/surfacereplayer/replayer/Android.mk b/cmds/surfacereplayer/replayer/Android.mk
new file mode 100644
index 0000000..1dd926c
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -0,0 +1,75 @@
+# 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.
+
+LOCAL_TARGET_DIR := $(TARGET_OUT_DATA)/local/tmp
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call first-makefiles-under, /frameworks/native/cmds/surfacereplayer/proto)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPPFLAGS := -Weverything -Werror
+LOCAL_CPPFLAGS := -Wno-unused-parameter
+LOCAL_CPPFLAGS := -Wno-format
+
+LOCAL_MODULE := libsurfacereplayer
+
+LOCAL_SRC_FILES := \
+ BufferQueueScheduler.cpp \
+ Event.cpp \
+ Replayer.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libEGL \
+ libGLESv2 \
+ libbinder \
+ liblog \
+ libcutils \
+ libgui \
+ libui \
+ libutils \
+ libprotobuf-cpp-lite \
+ libbase \
+ libnativewindow \
+
+LOCAL_STATIC_LIBRARIES := \
+ libtrace_proto \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/..
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := surfacereplayer
+
+LOCAL_SRC_FILES := \
+ Main.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libprotobuf-cpp-lite \
+ libsurfacereplayer \
+ libutils \
+ libgui \
+
+LOCAL_STATIC_LIBRARIES := \
+ libtrace_proto \
+
+LOCAL_CPPFLAGS := -Weverything -Werror
+LOCAL_CPPFLAGS := -Wno-unused-parameter
+
+LOCAL_MODULE_PATH := $(LOCAL_TARGET_DIR)
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
new file mode 100644
index 0000000..77de8dc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "BufferQueueScheduler"
+
+#include "BufferQueueScheduler.h"
+
+#include <android/native_window.h>
+#include <gui/Surface.h>
+
+using namespace android;
+
+BufferQueueScheduler::BufferQueueScheduler(
+ const sp<SurfaceControl>& surfaceControl, const HSV& color, int id)
+ : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {}
+
+void BufferQueueScheduler::startScheduling() {
+ ALOGV("Starting Scheduler for %d Layer", mSurfaceId);
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mSurfaceControl == nullptr) {
+ mCondition.wait(lock, [&] { return (mSurfaceControl != nullptr); });
+ }
+
+ while (mContinueScheduling) {
+ while (true) {
+ if (mBufferEvents.empty()) {
+ break;
+ }
+
+ BufferEvent event = mBufferEvents.front();
+ lock.unlock();
+
+ bufferUpdate(event.dimensions);
+ fillSurface(event.event);
+ mColor.modulate();
+ lock.lock();
+ mBufferEvents.pop();
+ }
+ mCondition.wait(lock);
+ }
+}
+
+void BufferQueueScheduler::addEvent(const BufferEvent& event) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mBufferEvents.push(event);
+ mCondition.notify_one();
+}
+
+void BufferQueueScheduler::stopScheduling() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mContinueScheduling = false;
+ mCondition.notify_one();
+}
+
+void BufferQueueScheduler::setSurfaceControl(
+ const sp<SurfaceControl>& surfaceControl, const HSV& color) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mSurfaceControl = surfaceControl;
+ mColor = color;
+ mCondition.notify_one();
+}
+
+void BufferQueueScheduler::bufferUpdate(const Dimensions& dimensions) {
+ sp<Surface> s = mSurfaceControl->getSurface();
+ s->setBuffersDimensions(dimensions.width, dimensions.height);
+}
+
+void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) {
+ ANativeWindow_Buffer outBuffer;
+ sp<Surface> s = mSurfaceControl->getSurface();
+
+ status_t status = s->lock(&outBuffer, nullptr);
+
+ if (status != NO_ERROR) {
+ ALOGE("fillSurface: failed to lock buffer, (%d)", status);
+ return;
+ }
+
+ auto color = mColor.getRGB();
+
+ auto img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+ for (int y = 0; y < outBuffer.height; y++) {
+ for (int x = 0; x < outBuffer.width; x++) {
+ uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+ pixel[0] = color.r;
+ pixel[1] = color.g;
+ pixel[2] = color.b;
+ pixel[3] = LAYER_ALPHA;
+ }
+ }
+
+ event->readyToExecute();
+
+ status = s->unlockAndPost();
+
+ ALOGE_IF(status != NO_ERROR, "fillSurface: failed to unlock and post buffer, (%d)", status);
+}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
new file mode 100644
index 0000000..cb20fcc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
+#define ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
+
+#include "Color.h"
+#include "Event.h"
+
+#include <gui/SurfaceControl.h>
+
+#include <utils/StrongPointer.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <utility>
+
+namespace android {
+
+auto constexpr LAYER_ALPHA = 190;
+
+struct Dimensions {
+ Dimensions() = default;
+ Dimensions(int w, int h) : width(w), height(h) {}
+
+ int width = 0;
+ int height = 0;
+};
+
+struct BufferEvent {
+ BufferEvent() = default;
+ BufferEvent(std::shared_ptr<Event> e, Dimensions d) : event(e), dimensions(d) {}
+
+ std::shared_ptr<Event> event;
+ Dimensions dimensions;
+};
+
+class BufferQueueScheduler {
+ public:
+ BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id);
+
+ void startScheduling();
+ void addEvent(const BufferEvent&);
+ void stopScheduling();
+
+ void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color);
+
+ private:
+ void bufferUpdate(const Dimensions& dimensions);
+
+ // Lock and fill the surface, block until the event is signaled by the main loop,
+ // then unlock and post the buffer.
+ void fillSurface(const std::shared_ptr<Event>& event);
+
+ sp<SurfaceControl> mSurfaceControl;
+ HSV mColor;
+ const int mSurfaceId;
+
+ bool mContinueScheduling;
+
+ std::queue<BufferEvent> mBufferEvents;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+};
+
+} // namespace android
+#endif
diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h
new file mode 100644
index 0000000..ce644be
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Color.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_COLOR_H
+#define ANDROID_SURFACEREPLAYER_COLOR_H
+
+#include <cmath>
+#include <cstdlib>
+
+namespace android {
+
+constexpr double modulateFactor = .0001;
+constexpr double modulateLimit = .80;
+
+struct RGB {
+ RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {}
+
+ uint8_t r = 0;
+ uint8_t g = 0;
+ uint8_t b = 0;
+};
+
+struct HSV {
+ HSV() = default;
+ HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {}
+
+ double h = 0;
+ double s = 0;
+ double v = 0;
+
+ RGB getRGB() const;
+
+ bool modulateUp = false;
+
+ void modulate();
+};
+
+void inline HSV::modulate() {
+ if(modulateUp) {
+ v += modulateFactor;
+ } else {
+ v -= modulateFactor;
+ }
+
+ if(v <= modulateLimit || v >= 1) {
+ modulateUp = !modulateUp;
+ }
+}
+
+inline RGB HSV::getRGB() const {
+ using namespace std;
+ double r = 0, g = 0, b = 0;
+
+ if (s == 0) {
+ r = v;
+ g = v;
+ b = v;
+ } else {
+ auto tempHue = static_cast<int>(h) % 360;
+ tempHue = tempHue / 60;
+
+ int i = static_cast<int>(trunc(tempHue));
+ double f = h - i;
+
+ double x = v * (1.0 - s);
+ double y = v * (1.0 - (s * f));
+ double z = v * (1.0 - (s * (1.0 - f)));
+
+ switch (i) {
+ case 0:
+ r = v;
+ g = z;
+ b = x;
+ break;
+
+ case 1:
+ r = y;
+ g = v;
+ b = x;
+ break;
+
+ case 2:
+ r = x;
+ g = v;
+ b = z;
+ break;
+
+ case 3:
+ r = x;
+ g = y;
+ b = v;
+ break;
+
+ case 4:
+ r = z;
+ g = x;
+ b = v;
+ break;
+
+ default:
+ r = v;
+ g = x;
+ b = y;
+ break;
+ }
+ }
+
+ return RGB(round(r * 255), round(g * 255), round(b * 255));
+}
+}
+#endif
diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp
new file mode 100644
index 0000000..390d398
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Event.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include "Event.h"
+
+using namespace android;
+
+Event::Event(Increment::IncrementCase type) : mIncrementType(type) {}
+
+void Event::readyToExecute() {
+ changeState(Event::EventState::Waiting);
+ waitUntil(Event::EventState::Signaled);
+ changeState(Event::EventState::Running);
+}
+
+void Event::complete() {
+ waitUntil(Event::EventState::Waiting);
+ changeState(Event::EventState::Signaled);
+ waitUntil(Event::EventState::Running);
+}
+
+void Event::waitUntil(Event::EventState state) {
+ std::unique_lock<std::mutex> lock(mLock);
+ mCond.wait(lock, [this, state] { return (mState == state); });
+}
+
+void Event::changeState(Event::EventState state) {
+ std::unique_lock<std::mutex> lock(mLock);
+ mState = state;
+ lock.unlock();
+
+ mCond.notify_one();
+}
+
+Increment::IncrementCase Event::getIncrementType() {
+ return mIncrementType;
+}
diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h
new file mode 100644
index 0000000..44b60f5
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Event.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_EVENT_H
+#define ANDROID_SURFACEREPLAYER_EVENT_H
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <condition_variable>
+#include <mutex>
+
+namespace android {
+
+class Event {
+ public:
+ Event(Increment::IncrementCase);
+
+ enum class EventState {
+ SettingUp, // Completing as much time-independent work as possible
+ Waiting, // Waiting for signal from main thread to finish execution
+ Signaled, // Signaled by main thread, about to immediately switch to Running
+ Running // Finishing execution of rest of work
+ };
+
+ void readyToExecute();
+ void complete();
+
+ Increment::IncrementCase getIncrementType();
+
+ private:
+ void waitUntil(EventState state);
+ void changeState(EventState state);
+
+ std::mutex mLock;
+ std::condition_variable mCond;
+
+ EventState mState = EventState::SettingUp;
+
+ Increment::IncrementCase mIncrementType;
+};
+}
+#endif
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp
new file mode 100644
index 0000000..dd1dd7d
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Main.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+/*
+ * Replayer - Main.cpp
+ *
+ * 1. Get flags from command line
+ * 2. Commit actions or settings based on the flags
+ * 3. Initalize a replayer object with the filename passed in
+ * 4. Replay
+ * 5. Exit successfully or print error statement
+ */
+
+#include <replayer/Replayer.h>
+
+#include <csignal>
+#include <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace android;
+
+void printHelpMenu() {
+ std::cout << "SurfaceReplayer options:\n";
+ std::cout << "Usage: surfacereplayer [OPTIONS...] <TRACE FILE>\n";
+ std::cout << " File path must be absolute" << std::endl << std::endl;
+
+ std::cout << " -m Stops the replayer at the start of the trace and switches ";
+ "to manual replay\n";
+
+ std::cout << "\n -t [Number of Threads] Specifies the number of threads to be used while "
+ "replaying (default is " << android::DEFAULT_THREADS << ")\n";
+
+ std::cout << "\n -s [Timestamp] Specify at what timestamp should the replayer switch "
+ "to manual replay\n";
+
+ std::cout << " -n Ignore timestamps and run through trace as fast as possible\n";
+
+ std::cout << " -l Indefinitely loop the replayer\n";
+
+ std::cout << " -h Display help menu\n";
+
+ std::cout << std::endl;
+}
+
+int main(int argc, char** argv) {
+ std::string filename;
+ bool loop = false;
+ bool wait = true;
+ bool pauseBeginning = false;
+ int numThreads = DEFAULT_THREADS;
+ long stopHere = -1;
+
+ int opt = 0;
+ while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) {
+ switch (opt) {
+ case 'm':
+ pauseBeginning = true;
+ break;
+ case 't':
+ numThreads = atoi(optarg);
+ break;
+ case 's':
+ stopHere = atol(optarg);
+ break;
+ case 'n':
+ wait = false;
+ break;
+ case 'l':
+ loop = true;
+ break;
+ case 'h':
+ case '?':
+ printHelpMenu();
+ exit(0);
+ default:
+ std::cerr << "Invalid argument...exiting" << std::endl;
+ printHelpMenu();
+ exit(0);
+ }
+ }
+
+ char** input = argv + optind;
+ if (input[0] == NULL) {
+ std::cerr << "No trace file provided...exiting" << std::endl;
+ abort();
+ }
+ filename.assign(input[0]);
+
+ status_t status = NO_ERROR;
+ do {
+ android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere);
+ status = r.replay();
+ } while(loop);
+
+ if (status == NO_ERROR) {
+ std::cout << "Successfully finished replaying trace" << std::endl;
+ } else {
+ std::cerr << "Trace replayer returned error: " << status << std::endl;
+ }
+
+ return 0;
+}
diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md
new file mode 100644
index 0000000..893f0dc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/README.md
@@ -0,0 +1,262 @@
+SurfaceReplayer Documentation
+===================
+
+[go/SurfaceReplayer](go/SurfaceReplayer)
+
+SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by
+[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays
+
+* Creation and deletion of surfaces/displays
+* Alterations to the surfaces/displays called Transactions
+* Buffer Updates to surfaces
+* VSync events
+
+At their specified times to be as close to the original trace.
+
+Usage
+--------
+
+###Creating a trace
+
+SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to
+utilize it. To allow it to write to the device, run
+
+`setenforce 0`
+
+To start recording a trace, run
+
+`service call SurfaceFlinger 1020 i32 1`
+
+To stop recording, run
+
+`service call SurfaceFlinger 1020 i32 0`
+
+The default location for the trace is `/data/SurfaceTrace.dat`
+
+###Executable
+
+To replay a specific trace, execute
+
+`/data/local/tmp/surfacereplayer /absolute/path/to/trace`
+
+inside the android shell. This will replay the full trace and then exit. Running this command
+outside of the shell by prepending `adb shell` will not allow for manual control and will not turn
+off VSync injections if it interrupted in any way other than fully replaying the trace
+
+The replay will not fill surfaces with their contents during the capture. Rather they are given a
+random color which will be the same every time the trace is replayed. Surfaces modulate their color
+at buffer updates.
+
+**Options:**
+
+- -m pause the replayer at the start of the trace for manual replay
+- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3)
+- -s [Timestamp] switches to manual replay at specified timestamp
+- -n Ignore timestamps and run through trace as fast as possible
+- -l Indefinitely loop the replayer
+- -h displays help menu
+
+**Manual Replay:**
+When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled
+by the user. Pressing CTRL-C again will exit the replayer.
+
+Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to
+input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter
+without inputting a command repeats the previous command.
+
+- n - steps the replayer to the next VSync event
+- ni - steps the replayer to the next increment
+- c - continues normal replaying
+- c [milliseconds] - continue until specified number of milliseconds have passed
+- s [timestamp] - continue and stop at specified timestamp
+- l - list out timestamp of current increment
+- h - displays help menu
+
+###Shared Library
+
+To use the shared library include these shared libraries
+
+`libsurfacereplayer`
+`libprotobuf-cpp-full`
+`libutils`
+
+And the static library
+
+`libtrace_proto`
+
+Include the replayer header at the top of your file
+
+`#include <replayer/Replayer.h>`
+
+There are two constructors for the replayer
+
+`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)`
+`Replayer(Trace& trace, ... ditto ...)`
+
+The first constructor takes in the filepath where the trace is located and loads in the trace
+object internally.
+- replayManually - **True**: if the replayer will immediately switch to manual replay at the start
+- numThreads - Number of worker threads the replayer will use.
+- wait - **False**: Replayer ignores waits in between increments
+- stopHere - Time stamp of where the replayer should run to then switch to manual replay
+
+The second constructor includes all of the same parameters but takes in a preloaded trace object.
+To use add
+
+`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>`
+
+To your file
+
+After initializing the Replayer call
+
+ replayer.replay();
+
+And the trace will start replaying. Once the trace is finished replaying, the function will return.
+The layers that are visible at the end of the trace will remain on screen until the program
+terminates.
+
+
+**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was
+never executed. This can be fixed by executing
+
+`service call SurfaceFlinger 23 i32 0`
+
+in the android shell
+
+Code Breakdown
+-------------
+
+The Replayer is composed of 5 components.
+
+- The data format of the trace (Trace.proto)
+- The Replayer object (Replayer.cpp)
+- The synchronization mechanism to signal threads within the Replayer (Event.cpp)
+- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp)
+- The Main executable (Main.cpp)
+
+### Traces
+
+Traces are represented as a protobuf message located in surfacereplayer/proto/src.
+
+**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger).
+**Increments** contain the time stamp of when it occurred and a *oneof* which can be a
+
+ - Transaction
+ - SurfaceCreation
+ - SurfaceDeletion
+ - DisplayCreation
+ - DisplayDeleteion
+ - BufferUpdate
+ - VSyncEvent
+ - PowerModeUpdate
+
+**Transactions** contain whether the transaction was synchronous or animated and *repeated*
+**SurfaceChanges** and **DisplayChanges**
+
+- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as
+position, alpha, hidden, size, etc.
+- **DisplayChanges** contain the id of the display being manipulated and can be changes such as
+size, layer stack, projection, etc.
+
+**Surface/Display Creation** contain the id of the surface/display and the name of the
+surface/display
+
+**Surface/Display Deletion** contain the id of the surface/display to be deleted
+
+**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the
+buffer, and the frame number.
+
+**VSyncEvents** contain when the VSync event has occurred.
+
+**PowerModeUpdates** contain the id of the display being updated and what mode it is being
+changed to.
+
+To output the contents of a trace in a readable format, execute
+
+`**aprotoc** --decode=Trace \
+-I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \
+$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \
+ < **YourTraceFile.dat** > **YourOutputName.txt**`
+
+
+###Replayer
+
+Fundamentally the replayer loads a trace and iterates through each increment, waiting the required
+amount of time until the increment should be executed, then executing the increment. The first
+increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and
+goes from there.
+
+Increments from the trace are played asynchronously rather than one by one, being dispatched by
+the main thread, queued up in a thread pool and completed when the main thread deems they are
+ready to finish execution.
+
+When an increment is dispatched, it completes as much work as it can before it has to be
+synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action
+(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event
+object. The main thread holds a queue of these Event objects and completes the
+corresponding Event base on its time stamp. After completing an increment, the main thread will
+dispatch another increment and continue.
+
+The main thread's execution flow is outlined below
+
+ initReplay() //queue up the initial increments
+ while(!pendingIncrements.empty()) { //while increments remaining
+ event = pendingIncrement.pop();
+ wait(event.time_stamp(); //waitUntil it is time to complete this increment
+
+ event.complete() //signal to let event finish
+ if(increments remaing()) {
+ dispatchEvent() //queue up another increment
+ }
+ }
+
+A worker thread's flow looks like so
+
+ //dispatched!
+ Execute non-time sensitive work here
+ ...
+ event.readyToExecute() //time sensitive point...waiting for Main Thread
+ ...
+ Finish execution
+
+
+### Event
+
+An Event is a simple synchronization mechanism used to facilitate communication between the main
+and worker threads. Every time an increment is dispatched, an Event object is also created.
+
+An Event can be in 4 different states:
+
+- **SettingUp** - The worker is in the process of completing all non-time sensitive work
+- **Waiting** - The worker is waiting on the main thread to signal it.
+- **Signaled** - The worker has just been signaled by the main thread
+- **Running** - The worker is running again and finishing the rest of its work.
+
+When the main thread wants to finish the execution of a worker, the worker can either still be
+**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the
+main thread will **Signal** it to complete. The worker thread changes itself to the **Running**
+state once **Signaled**. This last step exists in order to communicate back to the main thread that
+the worker thread has actually started completing its execution, rather than being preempted right
+after signalling. Once this happens, the main thread schedules the next worker. This makes sure
+there is a constant amount of workers running at one time.
+
+This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the
+worker and main thread respectively.
+
+### BufferQueueScheduler
+
+During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a
+buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential
+**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it,
+which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by
+handling when **BufferUpdates** should be scheduled, making sure that they don't overlap.
+
+When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a
+**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one
+every time an Event is completed.
+
+### Main
+
+The main exectuable reads in the command line arguments. Creates the Replayer using those
+arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit
+gracefully, if there are then it will report the error and then exit.
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
new file mode 100644
index 0000000..35b63ec
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -0,0 +1,702 @@
+/* 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceReplayer"
+
+#include "Replayer.h"
+
+#include <android/native_window.h>
+
+#include <android-base/file.h>
+
+#include <gui/BufferQueue.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+
+#include <ui/DisplayInfo.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cmath>
+#include <condition_variable>
+#include <cstdlib>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+using namespace android;
+
+std::atomic_bool Replayer::sReplayingManually(false);
+
+Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads, bool wait,
+ nsecs_t stopHere)
+ : mTrace(),
+ mLoaded(false),
+ mIncrementIndex(0),
+ mCurrentTime(0),
+ mNumThreads(numThreads),
+ mWaitForTimeStamps(wait),
+ mStopTimeStamp(stopHere) {
+ srand(RAND_COLOR_SEED);
+
+ std::string input;
+ if (!android::base::ReadFileToString(filename, &input, true)) {
+ std::cerr << "Trace did not load. Does " << filename << " exist?" << std::endl;
+ abort();
+ }
+
+ mLoaded = mTrace.ParseFromString(input);
+ if (!mLoaded) {
+ std::cerr << "Trace did not load." << std::endl;
+ abort();
+ }
+
+ mCurrentTime = mTrace.increment(0).time_stamp();
+
+ sReplayingManually.store(replayManually);
+
+ if (stopHere < 0) {
+ mHasStopped = true;
+ }
+}
+
+Replayer::Replayer(const Trace& t, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)
+ : mTrace(t),
+ mLoaded(true),
+ mIncrementIndex(0),
+ mCurrentTime(0),
+ mNumThreads(numThreads),
+ mWaitForTimeStamps(wait),
+ mStopTimeStamp(stopHere) {
+ srand(RAND_COLOR_SEED);
+ mCurrentTime = mTrace.increment(0).time_stamp();
+
+ sReplayingManually.store(replayManually);
+
+ if (stopHere < 0) {
+ mHasStopped = true;
+ }
+}
+
+status_t Replayer::replay() {
+ signal(SIGINT, Replayer::stopAutoReplayHandler); //for manual control
+
+ ALOGV("There are %d increments.", mTrace.increment_size());
+
+ status_t status = loadSurfaceComposerClient();
+
+ if (status != NO_ERROR) {
+ ALOGE("Couldn't create SurfaceComposerClient (%d)", status);
+ return status;
+ }
+
+ SurfaceComposerClient::enableVSyncInjections(true);
+
+ initReplay();
+
+ ALOGV("Starting actual Replay!");
+ while (!mPendingIncrements.empty()) {
+ mCurrentIncrement = mTrace.increment(mIncrementIndex);
+
+ if (mHasStopped == false && mCurrentIncrement.time_stamp() >= mStopTimeStamp) {
+ mHasStopped = true;
+ sReplayingManually.store(true);
+ }
+
+ waitForConsoleCommmand();
+
+ if (mWaitForTimeStamps) {
+ waitUntilTimestamp(mCurrentIncrement.time_stamp());
+ }
+
+ auto event = mPendingIncrements.front();
+ mPendingIncrements.pop();
+
+ event->complete();
+
+ if (event->getIncrementType() == Increment::kVsyncEvent) {
+ mWaitingForNextVSync = false;
+ }
+
+ if (mIncrementIndex + mNumThreads < mTrace.increment_size()) {
+ status = dispatchEvent(mIncrementIndex + mNumThreads);
+
+ if (status != NO_ERROR) {
+ SurfaceComposerClient::enableVSyncInjections(false);
+ return status;
+ }
+ }
+
+ mIncrementIndex++;
+ mCurrentTime = mCurrentIncrement.time_stamp();
+ }
+
+ SurfaceComposerClient::enableVSyncInjections(false);
+
+ return status;
+}
+
+status_t Replayer::initReplay() {
+ for (int i = 0; i < mNumThreads && i < mTrace.increment_size(); i++) {
+ status_t status = dispatchEvent(i);
+
+ if (status != NO_ERROR) {
+ ALOGE("Unable to dispatch event (%d)", status);
+ return status;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void Replayer::stopAutoReplayHandler(int /*signal*/) {
+ if (sReplayingManually) {
+ SurfaceComposerClient::enableVSyncInjections(false);
+ exit(0);
+ }
+
+ sReplayingManually.store(true);
+}
+
+std::vector<std::string> split(const std::string& s, const char delim) {
+ std::vector<std::string> elems;
+ std::stringstream ss(s);
+ std::string item;
+ while (getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+ return elems;
+}
+
+bool isNumber(const std::string& s) {
+ return !s.empty() &&
+ std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
+}
+
+void Replayer::waitForConsoleCommmand() {
+ if (!sReplayingManually || mWaitingForNextVSync) {
+ return;
+ }
+
+ while (true) {
+ std::string input = "";
+ std::cout << "> ";
+ getline(std::cin, input);
+
+ if (input.empty()) {
+ input = mLastInput;
+ } else {
+ mLastInput = input;
+ }
+
+ if (mLastInput.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> inputs = split(input, ' ');
+
+ if (inputs[0] == "n") { // next vsync
+ mWaitingForNextVSync = true;
+ break;
+
+ } else if (inputs[0] == "ni") { // next increment
+ break;
+
+ } else if (inputs[0] == "c") { // continue
+ if (inputs.size() > 1 && isNumber(inputs[1])) {
+ long milliseconds = stoi(inputs[1]);
+ std::thread([&] {
+ std::cout << "Started!" << std::endl;
+ std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+ sReplayingManually.store(true);
+ std::cout << "Should have stopped!" << std::endl;
+ }).detach();
+ }
+ sReplayingManually.store(false);
+ mWaitingForNextVSync = false;
+ break;
+
+ } else if (inputs[0] == "s") { // stop at this timestamp
+ if (inputs.size() < 1) {
+ std::cout << "No time stamp given" << std::endl;
+ continue;
+ }
+ sReplayingManually.store(false);
+ mStopTimeStamp = stol(inputs[1]);
+ mHasStopped = false;
+ break;
+ } else if (inputs[0] == "l") { // list
+ std::cout << "Time stamp: " << mCurrentIncrement.time_stamp() << "\n";
+ continue;
+ } else if (inputs[0] == "q") { // quit
+ SurfaceComposerClient::enableVSyncInjections(false);
+ exit(0);
+
+ } else if (inputs[0] == "h") { // help
+ // add help menu
+ std::cout << "Manual Replay options:\n";
+ std::cout << " n - Go to next VSync\n";
+ std::cout << " ni - Go to next increment\n";
+ std::cout << " c - Continue\n";
+ std::cout << " c [milliseconds] - Continue until specified number of milliseconds\n";
+ std::cout << " s [timestamp] - Continue and stop at specified timestamp\n";
+ std::cout << " l - List out timestamp of current increment\n";
+ std::cout << " h - Display help menu\n";
+ std::cout << std::endl;
+ continue;
+ }
+
+ std::cout << "Invalid Command" << std::endl;
+ }
+}
+
+status_t Replayer::dispatchEvent(int index) {
+ auto increment = mTrace.increment(index);
+ std::shared_ptr<Event> event = std::make_shared<Event>(increment.increment_case());
+ mPendingIncrements.push(event);
+
+ status_t status = NO_ERROR;
+ switch (increment.increment_case()) {
+ case increment.kTransaction: {
+ std::thread(&Replayer::doTransaction, this, increment.transaction(), event).detach();
+ } break;
+ case increment.kSurfaceCreation: {
+ std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
+ .detach();
+ } break;
+ case increment.kSurfaceDeletion: {
+ std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event)
+ .detach();
+ } break;
+ case increment.kBufferUpdate: {
+ std::lock_guard<std::mutex> lock1(mLayerLock);
+ std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
+
+ Dimensions dimensions(increment.buffer_update().w(), increment.buffer_update().h());
+ BufferEvent bufferEvent(event, dimensions);
+
+ auto layerId = increment.buffer_update().id();
+ if (mBufferQueueSchedulers.count(layerId) == 0) {
+ mBufferQueueSchedulers[layerId] = std::make_shared<BufferQueueScheduler>(
+ mLayers[layerId], mColors[layerId], layerId);
+ mBufferQueueSchedulers[layerId]->addEvent(bufferEvent);
+
+ std::thread(&BufferQueueScheduler::startScheduling,
+ mBufferQueueSchedulers[increment.buffer_update().id()].get())
+ .detach();
+ } else {
+ auto bqs = mBufferQueueSchedulers[increment.buffer_update().id()];
+ bqs->addEvent(bufferEvent);
+ }
+ } break;
+ case increment.kVsyncEvent: {
+ std::thread(&Replayer::injectVSyncEvent, this, increment.vsync_event(), event).detach();
+ } break;
+ case increment.kDisplayCreation: {
+ std::thread(&Replayer::createDisplay, this, increment.display_creation(), event)
+ .detach();
+ } break;
+ case increment.kDisplayDeletion: {
+ std::thread(&Replayer::deleteDisplay, this, increment.display_deletion(), event)
+ .detach();
+ } break;
+ case increment.kPowerModeUpdate: {
+ std::thread(&Replayer::updatePowerMode, this, increment.power_mode_update(), event)
+ .detach();
+ } break;
+ default:
+ ALOGE("Unknown Increment Type: %d", increment.increment_case());
+ status = BAD_VALUE;
+ break;
+ }
+
+ return status;
+}
+
+status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr<Event>& event) {
+ ALOGV("Started Transaction");
+
+ SurfaceComposerClient::openGlobalTransaction();
+
+ status_t status = NO_ERROR;
+
+ status = doSurfaceTransaction(t.surface_change());
+ doDisplayTransaction(t.display_change());
+
+ if (t.animation()) {
+ SurfaceComposerClient::setAnimationTransaction();
+ }
+
+ event->readyToExecute();
+
+ SurfaceComposerClient::closeGlobalTransaction(t.synchronous());
+
+ ALOGV("Ended Transaction");
+
+ return status;
+}
+
+status_t Replayer::doSurfaceTransaction(const SurfaceChanges& surfaceChanges) {
+ status_t status = NO_ERROR;
+
+ for (const SurfaceChange& change : surfaceChanges) {
+ std::unique_lock<std::mutex> lock(mLayerLock);
+ if (mLayers[change.id()] == nullptr) {
+ mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
+ }
+
+ switch (change.SurfaceChange_case()) {
+ case SurfaceChange::SurfaceChangeCase::kPosition:
+ status = setPosition(change.id(), change.position());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSize:
+ status = setSize(change.id(), change.size());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kAlpha:
+ status = setAlpha(change.id(), change.alpha());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayer:
+ status = setLayer(change.id(), change.layer());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kCrop:
+ status = setCrop(change.id(), change.crop());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kMatrix:
+ status = setMatrix(change.id(), change.matrix());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+ status = setFinalCrop(change.id(), change.final_crop());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+ status = setOverrideScalingMode(change.id(), change.override_scaling_mode());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+ status = setTransparentRegionHint(change.id(), change.transparent_region_hint());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayerStack:
+ status = setLayerStack(change.id(), change.layer_stack());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+ status = setHiddenFlag(change.id(), change.hidden_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+ status = setOpaqueFlag(change.id(), change.opaque_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+ status = setSecureFlag(change.id(), change.secure_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+ waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
+ status = setDeferredTransaction(change.id(), change.deferred_transaction());
+ break;
+ default:
+ status = NO_ERROR;
+ break;
+ }
+
+ if (status != NO_ERROR) {
+ ALOGE("SET TRANSACTION FAILED");
+ return status;
+ }
+ }
+ return status;
+}
+
+void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) {
+ for (const DisplayChange& change : displayChanges) {
+ ALOGV("Doing display transaction");
+ std::unique_lock<std::mutex> lock(mDisplayLock);
+ if (mDisplays[change.id()] == nullptr) {
+ mDisplayCond.wait(lock, [&] { return (mDisplays[change.id()] != nullptr); });
+ }
+
+ switch (change.DisplayChange_case()) {
+ case DisplayChange::DisplayChangeCase::kSurface:
+ setDisplaySurface(change.id(), change.surface());
+ break;
+ case DisplayChange::DisplayChangeCase::kLayerStack:
+ setDisplayLayerStack(change.id(), change.layer_stack());
+ break;
+ case DisplayChange::DisplayChangeCase::kSize:
+ setDisplaySize(change.id(), change.size());
+ break;
+ case DisplayChange::DisplayChangeCase::kProjection:
+ setDisplayProjection(change.id(), change.projection());
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+status_t Replayer::setPosition(layer_id id, const PositionChange& pc) {
+ ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y());
+ return mLayers[id]->setPosition(pc.x(), pc.y());
+}
+
+status_t Replayer::setSize(layer_id id, const SizeChange& sc) {
+ ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h());
+ return mLayers[id]->setSize(sc.w(), sc.h());
+}
+
+status_t Replayer::setLayer(layer_id id, const LayerChange& lc) {
+ ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer());
+ return mLayers[id]->setLayer(lc.layer());
+}
+
+status_t Replayer::setAlpha(layer_id id, const AlphaChange& ac) {
+ ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha());
+ return mLayers[id]->setAlpha(ac.alpha());
+}
+
+status_t Replayer::setCrop(layer_id id, const CropChange& cc) {
+ ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
+ cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
+ cc.rectangle().bottom());
+
+ Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
+ cc.rectangle().bottom());
+ return mLayers[id]->setCrop(r);
+}
+
+status_t Replayer::setFinalCrop(layer_id id, const FinalCropChange& fcc) {
+ ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
+ fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
+ fcc.rectangle().bottom());
+ Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
+ fcc.rectangle().bottom());
+ return mLayers[id]->setFinalCrop(r);
+}
+
+status_t Replayer::setMatrix(layer_id id, const MatrixChange& mc) {
+ ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
+ mc.dtdx(), mc.dsdy(), mc.dtdy());
+ return mLayers[id]->setMatrix(mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
+}
+
+status_t Replayer::setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc) {
+ ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
+ return mLayers[id]->setOverrideScalingMode(osmc.override_scaling_mode());
+}
+
+status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trhc) {
+ ALOGV("Setting Transparent Region Hint");
+ Region re = Region();
+
+ for (auto r : trhc.region()) {
+ Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom());
+ re.merge(rect);
+ }
+
+ return mLayers[id]->setTransparentRegionHint(re);
+}
+
+status_t Replayer::setLayerStack(layer_id id, const LayerStackChange& lsc) {
+ ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
+ return mLayers[id]->setLayerStack(lsc.layer_stack());
+}
+
+status_t Replayer::setHiddenFlag(layer_id id, const HiddenFlagChange& hfc) {
+ ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag());
+ layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
+
+ return mLayers[id]->setFlags(flag, layer_state_t::eLayerHidden);
+}
+
+status_t Replayer::setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc) {
+ ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag());
+ layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
+
+ return mLayers[id]->setFlags(flag, layer_state_t::eLayerOpaque);
+}
+
+status_t Replayer::setSecureFlag(layer_id id, const SecureFlagChange& sfc) {
+ ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag());
+ layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
+
+ return mLayers[id]->setFlags(flag, layer_state_t::eLayerSecure);
+}
+
+status_t Replayer::setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc) {
+ ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, "
+ "frame_number=%llu",
+ id, dtc.layer_id(), dtc.frame_number());
+ if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
+ ALOGE("Layer %d not found in Deferred Transaction", dtc.layer_id());
+ return BAD_VALUE;
+ }
+
+ auto handle = mLayers[dtc.layer_id()]->getHandle();
+
+ return mLayers[id]->deferTransactionUntil(handle, dtc.frame_number());
+}
+
+void Replayer::setDisplaySurface(display_id id, const DispSurfaceChange& /*dsc*/) {
+ sp<IGraphicBufferProducer> outProducer;
+ sp<IGraphicBufferConsumer> outConsumer;
+ BufferQueue::createBufferQueue(&outProducer, &outConsumer);
+
+ SurfaceComposerClient::setDisplaySurface(mDisplays[id], outProducer);
+}
+
+void Replayer::setDisplayLayerStack(display_id id, const LayerStackChange& lsc) {
+ SurfaceComposerClient::setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+}
+
+void Replayer::setDisplaySize(display_id id, const SizeChange& sc) {
+ SurfaceComposerClient::setDisplaySize(mDisplays[id], sc.w(), sc.h());
+}
+
+void Replayer::setDisplayProjection(display_id id, const ProjectionChange& pc) {
+ Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(),
+ pc.viewport().bottom());
+ Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom());
+
+ SurfaceComposerClient::setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame);
+}
+
+status_t Replayer::createSurfaceControl(
+ const SurfaceCreation& create, const std::shared_ptr<Event>& event) {
+ event->readyToExecute();
+
+ ALOGV("Creating Surface Control: ID: %d", create.id());
+ sp<SurfaceControl> surfaceControl = mComposerClient->createSurface(
+ String8(create.name().c_str()), create.w(), create.h(), PIXEL_FORMAT_RGBA_8888, 0);
+
+ if (surfaceControl == nullptr) {
+ ALOGE("CreateSurfaceControl: unable to create surface control");
+ return BAD_VALUE;
+ }
+
+ std::lock_guard<std::mutex> lock1(mLayerLock);
+ auto& layer = mLayers[create.id()];
+ layer = surfaceControl;
+
+ mColors[create.id()] = HSV(rand() % 360, 1, 1);
+
+ mLayerCond.notify_all();
+
+ std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
+ if (mBufferQueueSchedulers.count(create.id()) != 0) {
+ mBufferQueueSchedulers[create.id()]->setSurfaceControl(
+ mLayers[create.id()], mColors[create.id()]);
+ }
+
+ return NO_ERROR;
+}
+
+status_t Replayer::deleteSurfaceControl(
+ const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) {
+ ALOGV("Deleting %d Surface Control", delete_.id());
+ event->readyToExecute();
+
+ std::lock_guard<std::mutex> lock1(mPendingLayersLock);
+
+ mLayersPendingRemoval.push_back(delete_.id());
+
+ const auto& iterator = mBufferQueueSchedulers.find(delete_.id());
+ if (iterator != mBufferQueueSchedulers.end()) {
+ (*iterator).second->stopScheduling();
+ }
+
+ std::lock_guard<std::mutex> lock2(mLayerLock);
+ if (mLayers[delete_.id()] != nullptr) {
+ mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
+ }
+
+ return NO_ERROR;
+}
+
+void Replayer::doDeleteSurfaceControls() {
+ std::lock_guard<std::mutex> lock1(mPendingLayersLock);
+ std::lock_guard<std::mutex> lock2(mLayerLock);
+ if (!mLayersPendingRemoval.empty()) {
+ for (int id : mLayersPendingRemoval) {
+ mLayers.erase(id);
+ mColors.erase(id);
+ mBufferQueueSchedulers.erase(id);
+ }
+ mLayersPendingRemoval.clear();
+ }
+}
+
+status_t Replayer::injectVSyncEvent(
+ const VSyncEvent& vSyncEvent, const std::shared_ptr<Event>& event) {
+ ALOGV("Injecting VSync Event");
+
+ doDeleteSurfaceControls();
+
+ event->readyToExecute();
+
+ SurfaceComposerClient::injectVSync(vSyncEvent.when());
+
+ return NO_ERROR;
+}
+
+void Replayer::createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event) {
+ ALOGV("Creating display");
+ event->readyToExecute();
+
+ std::lock_guard<std::mutex> lock(mDisplayLock);
+ sp<IBinder> display = SurfaceComposerClient::createDisplay(
+ String8(create.name().c_str()), create.is_secure());
+ mDisplays[create.id()] = display;
+
+ mDisplayCond.notify_all();
+
+ ALOGV("Done creating display");
+}
+
+void Replayer::deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event) {
+ ALOGV("Delete display");
+ event->readyToExecute();
+
+ std::lock_guard<std::mutex> lock(mDisplayLock);
+ SurfaceComposerClient::destroyDisplay(mDisplays[delete_.id()]);
+ mDisplays.erase(delete_.id());
+}
+
+void Replayer::updatePowerMode(const PowerModeUpdate& pmu, const std::shared_ptr<Event>& event) {
+ ALOGV("Updating power mode");
+ event->readyToExecute();
+ SurfaceComposerClient::setDisplayPowerMode(mDisplays[pmu.id()], pmu.mode());
+}
+
+void Replayer::waitUntilTimestamp(int64_t timestamp) {
+ ALOGV("Waiting for %lld nanoseconds...", static_cast<int64_t>(timestamp - mCurrentTime));
+ std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
+}
+
+void Replayer::waitUntilDeferredTransactionLayerExists(
+ const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock) {
+ if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
+ mLayerCond.wait(lock, [&] { return (mLayers[dtc.layer_id()] != nullptr); });
+ }
+}
+
+status_t Replayer::loadSurfaceComposerClient() {
+ mComposerClient = new SurfaceComposerClient;
+ return mComposerClient->initCheck();
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
new file mode 100644
index 0000000..f36c9fd
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_H
+#define ANDROID_SURFACEREPLAYER_H
+
+#include "BufferQueueScheduler.h"
+#include "Color.h"
+#include "Event.h"
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include <stdatomic.h>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+#include <utility>
+
+namespace android {
+
+const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
+const auto RAND_COLOR_SEED = 700;
+const auto DEFAULT_THREADS = 3;
+
+typedef int32_t layer_id;
+typedef int32_t display_id;
+
+typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
+typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
+
+class Replayer {
+ public:
+ Replayer(const std::string& filename, bool replayManually = false,
+ int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
+ Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
+ bool wait = true, nsecs_t stopHere = -1);
+
+ status_t replay();
+
+ private:
+ status_t initReplay();
+
+ void waitForConsoleCommmand();
+ static void stopAutoReplayHandler(int signal);
+
+ status_t dispatchEvent(int index);
+
+ status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
+ status_t createSurfaceControl(const SurfaceCreation& create,
+ const std::shared_ptr<Event>& event);
+ status_t deleteSurfaceControl(const SurfaceDeletion& delete_,
+ const std::shared_ptr<Event>& event);
+ status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
+ void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
+ void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
+ void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
+
+ status_t doSurfaceTransaction(const SurfaceChanges& surfaceChange);
+ void doDisplayTransaction(const DisplayChanges& displayChange);
+
+ status_t setPosition(layer_id id, const PositionChange& pc);
+ status_t setSize(layer_id id, const SizeChange& sc);
+ status_t setAlpha(layer_id id, const AlphaChange& ac);
+ status_t setLayer(layer_id id, const LayerChange& lc);
+ status_t setCrop(layer_id id, const CropChange& cc);
+ status_t setFinalCrop(layer_id id, const FinalCropChange& fcc);
+ status_t setMatrix(layer_id id, const MatrixChange& mc);
+ status_t setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc);
+ status_t setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trgc);
+ status_t setLayerStack(layer_id id, const LayerStackChange& lsc);
+ status_t setHiddenFlag(layer_id id, const HiddenFlagChange& hfc);
+ status_t setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc);
+ status_t setSecureFlag(layer_id id, const SecureFlagChange& sfc);
+ status_t setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc);
+
+ void setDisplaySurface(display_id id, const DispSurfaceChange& dsc);
+ void setDisplayLayerStack(display_id id, const LayerStackChange& lsc);
+ void setDisplaySize(display_id id, const SizeChange& sc);
+ void setDisplayProjection(display_id id, const ProjectionChange& pc);
+
+ void doDeleteSurfaceControls();
+ void waitUntilTimestamp(int64_t timestamp);
+ void waitUntilDeferredTransactionLayerExists(
+ const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
+ status_t loadSurfaceComposerClient();
+
+ Trace mTrace;
+ bool mLoaded = false;
+ int32_t mIncrementIndex = 0;
+ int64_t mCurrentTime = 0;
+ int32_t mNumThreads = DEFAULT_THREADS;
+
+ Increment mCurrentIncrement;
+
+ std::string mLastInput;
+
+ static atomic_bool sReplayingManually;
+ bool mWaitingForNextVSync;
+ bool mWaitForTimeStamps;
+ nsecs_t mStopTimeStamp;
+ bool mHasStopped;
+
+ std::mutex mLayerLock;
+ std::condition_variable mLayerCond;
+ std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
+ std::unordered_map<layer_id, HSV> mColors;
+
+ std::mutex mPendingLayersLock;
+ std::vector<layer_id> mLayersPendingRemoval;
+
+ std::mutex mBufferQueueSchedulerLock;
+ std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
+
+ std::mutex mDisplayLock;
+ std::condition_variable mDisplayCond;
+ std::unordered_map<display_id, sp<IBinder>> mDisplays;
+
+ sp<SurfaceComposerClient> mComposerClient;
+ std::queue<std::shared_ptr<Event>> mPendingIncrements;
+};
+
+} // namespace android
+#endif
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
new file mode 100644
index 0000000..a892e46
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -0,0 +1,294 @@
+#!/usr/bin/python
+from subprocess import call
+import os
+proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/"
+call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"])
+
+from trace_pb2 import *
+
+trace = Trace()
+
+def main():
+ global trace
+ while(1):
+ option = main_menu()
+
+ if option == 0:
+ break
+
+ increment = trace.increment.add()
+ increment.time_stamp = int(input("Time stamp of action: "))
+
+ if option == 1:
+ transaction(increment)
+ elif option == 2:
+ surface_create(increment)
+ elif option == 3:
+ surface_delete(increment)
+ elif option == 4:
+ display_create(increment)
+ elif option == 5:
+ display_delete(increment)
+ elif option == 6:
+ buffer_update(increment)
+ elif option == 7:
+ vsync_event(increment)
+ elif option == 8:
+ power_mode_update(increment)
+
+ seralizeTrace()
+
+def seralizeTrace():
+ with open("trace.dat", 'wb') as f:
+ f.write(trace.SerializeToString())
+
+
+def main_menu():
+ print ("")
+ print ("What would you like to do?")
+ print ("1. Add transaction")
+ print ("2. Add surface creation")
+ print ("3. Add surface deletion")
+ print ("4. Add display creation")
+ print ("5. Add display deletion")
+ print ("6. Add buffer update")
+ print ("7. Add VSync event")
+ print ("8. Add power mode update")
+ print ("0. Finish and serialize")
+ print ("")
+
+ return int(input("> "))
+
+def transaction_menu():
+ print ("")
+ print ("What kind of transaction?")
+ print ("1. Position Change")
+ print ("2. Size Change")
+ print ("3. Alpha Change")
+ print ("4. Layer Change")
+ print ("5. Crop Change")
+ print ("6. Final Crop Change")
+ print ("7. Matrix Change")
+ print ("8. Override Scaling Mode Change")
+ print ("9. Transparent Region Hint Change")
+ print ("10. Layer Stack Change")
+ print ("11. Hidden Flag Change")
+ print ("12. Opaque Flag Change")
+ print ("13. Secure Flag Change")
+ print ("14. Deferred Transaction Change")
+ print ("15. Display - Surface Change")
+ print ("16. Display - Layer Stack Change")
+ print ("17. Display - Size Change")
+ print ("18. Display - Projection Change")
+ print ("0. Finished adding Changes to this transaction")
+ print ("")
+
+ return int(input("> "))
+
+def transaction(increment):
+ global trace
+
+ increment.transaction.synchronous \
+ = bool(input("Is transaction synchronous (True/False): "))
+ increment.transaction.animation \
+ = bool(input("Is transaction animated (True/False): "))
+
+ while(1):
+ option = transaction_menu()
+
+ if option == 0:
+ break
+
+ change = None
+ if option <= 14:
+ change = increment.transaction.surface_change.add()
+ elif option >= 15 and option <= 18:
+ change = increment.transaction.display_change.add()
+
+ change.id = int(input("ID of layer/display to undergo a change: "))
+
+ if option == 1:
+ change.position.x, change.position.y = position()
+ elif option == 2:
+ change.size.w, change.size.h = size()
+ elif option == 3:
+ change.alpha.alpha = alpha()
+ elif option == 4:
+ change.layer.layer = layer()
+ elif option == 5:
+ change.crop.rectangle.left, change.crop.rectangle.top, \
+ change.crop.rectangle.right, change.crop.rectangle.bottom = crop()
+ elif option == 6:
+ change.final_crop.rectangle.left, \
+ change.final_crop.rectangle.top, \
+ change.final_crop.rectangle.right,\
+ change.final_crop.rectangle.bottom = final_crop()
+ elif option == 7:
+ change.matrix.dsdx,\
+ change.matrix.dtdx,\
+ change.matrix.dsdy,\
+ change.matrix.dtdy = layer()
+ elif option == 8:
+ change.override_scaling_mode.override_scaling_mode \
+ = override_scaling_mode()
+ elif option == 9:
+ for rect in transparent_region_hint():
+ new = increment.transparent_region_hint.region.add()
+ new.left = rect[0]
+ new.top = rect[1]
+ new.right = rect[2]
+ new.bottom = rect[3]
+ elif option == 10:
+ change.layer_stack.layer_stack = layer_stack()
+ elif option == 11:
+ change.hidden_flag.hidden_flag = hidden_flag()
+ elif option == 12:
+ change.opaque_flag.opaque_flag = opaque_flag()
+ elif option == 13:
+ change.secure_flag.secure_flag = secure_flag()
+ elif option == 14:
+ change.deferred_transaction.layer_id, \
+ change.deferred_transaction.frame_number = deferred_transaction()
+ elif option == 15:
+ change.surface.buffer_queue_id, \
+ change.surface.buffer_queue_name = surface()
+ elif option == 16:
+ change.layer_stack.layer_stack = layer_stack()
+ elif option == 17:
+ change.size.w, change.size.h = size()
+ elif option == 18:
+ projection(change)
+
+def surface_create(increment):
+ increment.surface_creation.id = int(input("Enter id: "))
+ n = str(raw_input("Enter name: "))
+ increment.surface_creation.name = n
+ increment.surface_creation.w = input("Enter w: ")
+ increment.surface_creation.h = input("Enter h: ")
+
+def surface_delete(increment):
+ increment.surface_deletion.id = int(input("Enter id: "))
+
+def display_create(increment):
+ increment.display_creation.id = int(input("Enter id: "))
+ increment.display_creation.name = str(raw_input("Enter name: "))
+ increment.display_creation.type = int(input("Enter type: "))
+ increment.display_creation.is_secure = bool(input("Enter if secure: "))
+
+def display_delete(increment):
+ increment.surface_deletion.id = int(input("Enter id: "))
+
+def buffer_update(increment):
+ increment.buffer_update.id = int(input("Enter id: "))
+ increment.buffer_update.w = int(input("Enter w: "))
+ increment.buffer_update.h = int(input("Enter h: "))
+ increment.buffer_update.frame_number = int(input("Enter frame_number: "))
+
+def vsync_event(increment):
+ increment.vsync_event.when = int(input("Enter when: "))
+
+def power_mode_update(increment):
+ increment.power_mode_update.id = int(input("Enter id: "))
+ increment.power_mode_update.mode = int(input("Enter mode: "))
+
+def position():
+ x = input("Enter x: ")
+ y = input("Enter y: ")
+
+ return float(x), float(y)
+
+def size():
+ w = input("Enter w: ")
+ h = input("Enter h: ")
+
+ return int(w), int(h)
+
+def alpha():
+ alpha = input("Enter alpha: ")
+
+ return float(alpha)
+
+def layer():
+ layer = input("Enter layer: ")
+
+ return int(layer)
+
+def crop():
+ return rectangle()
+
+def final_crop():
+ return rectangle()
+
+def matrix():
+ dsdx = input("Enter dsdx: ")
+ dtdx = input("Enter dtdx: ")
+ dsdy = input("Enter dsdy: ")
+ dtdy = input("Enter dtdy: ")
+
+ return float(dsdx)
+
+def override_scaling_mode():
+ mode = input("Enter override scaling mode: ")
+
+ return int(mode)
+
+def transparent_region_hint():
+ num = input("Enter number of rectangles in region: ")
+
+ return [rectangle() in range(x)]
+
+def layer_stack():
+ layer_stack = input("Enter layer stack: ")
+
+ return int(layer_stack)
+
+def hidden_flag():
+ flag = input("Enter hidden flag state (True/False): ")
+
+ return bool(flag)
+
+def opaque_flag():
+ flag = input("Enter opaque flag state (True/False): ")
+
+ return bool(flag)
+
+def secure_flag():
+ flag = input("Enter secure flag state (True/False): ")
+
+ return bool(flag)
+
+def deferred_transaction():
+ layer_id = input("Enter layer_id: ")
+ frame_number = input("Enter frame_number: ")
+
+ return int(layer_id), int(frame_number)
+
+def surface():
+ id = input("Enter id: ")
+ name = raw_input("Enter name: ")
+
+ return int(id), str(name)
+
+def projection(change):
+ change.projection.orientation = input("Enter orientation: ")
+ print("Enter rectangle for viewport")
+ change.projection.viewport.left, \
+ change.projection.viewport.top, \
+ change.projection.viewport.right,\
+ change.projection.viewport.bottom = rectangle()
+ print("Enter rectangle for frame")
+ change.projection.frame.left, \
+ change.projection.frame.top, \
+ change.projection.frame.right,\
+ change.projection.frame.bottom = rectangle()
+
+def rectangle():
+ left = input("Enter left: ")
+ top = input("Enter top: ")
+ right = input("Enter right: ")
+ bottom = input("Enter bottom: ")
+
+ return int(left), int(top), int(right), int(bottom)
+
+if __name__ == "__main__":
+ main()
diff --git a/cmds/vr/.clang-format b/cmds/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/cmds/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/data/etc/android.hardware.nfc.hce.xml b/data/etc/android.hardware.nfc.hce.xml
index 10b96b1..95da181 100644
--- a/data/etc/android.hardware.nfc.hce.xml
+++ b/data/etc/android.hardware.nfc.hce.xml
@@ -18,4 +18,5 @@
NFC card emulation -->
<permissions>
<feature name="android.hardware.nfc.hce" />
+ <feature name="android.hardware.nfc.any" />
</permissions>
diff --git a/data/etc/android.hardware.nfc.hcef.xml b/data/etc/android.hardware.nfc.hcef.xml
index 0d03023..b86890d 100644
--- a/data/etc/android.hardware.nfc.hcef.xml
+++ b/data/etc/android.hardware.nfc.hcef.xml
@@ -18,4 +18,5 @@
NFC-F card emulation -->
<permissions>
<feature name="android.hardware.nfc.hcef" />
+ <feature name="android.hardware.nfc.any" />
</permissions>
diff --git a/data/etc/android.hardware.nfc.xml b/data/etc/android.hardware.nfc.xml
index 81c4a84..5201fa2 100644
--- a/data/etc/android.hardware.nfc.xml
+++ b/data/etc/android.hardware.nfc.xml
@@ -18,4 +18,5 @@
using Near-Field Communications (NFC). -->
<permissions>
<feature name="android.hardware.nfc" />
+ <feature name="android.hardware.nfc.any" />
</permissions>
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/data/etc/android.hardware.vr.headtracking-0.xml b/data/etc/android.hardware.vr.headtracking-0.xml
new file mode 100644
index 0000000..1b53995
--- /dev/null
+++ b/data/etc/android.hardware.vr.headtracking-0.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<!-- This is the feature indicating that the device supports VR headtracking
+ level 0 -->
+<permissions>
+ <feature name="android.hardware.vr.headtracking" version="0" />
+</permissions>
diff --git a/data/etc/android.hardware.vr.headtracking-1.xml b/data/etc/android.hardware.vr.headtracking-1.xml
new file mode 100644
index 0000000..2ad8ccc
--- /dev/null
+++ b/data/etc/android.hardware.vr.headtracking-1.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<!-- This is the feature indicating that the device supports VR headtracking
+ level 1 -->
+<permissions>
+ <feature name="android.hardware.vr.headtracking" version="1" />
+</permissions>
diff --git a/data/etc/android.hardware.vulkan.compute-0.xml b/data/etc/android.hardware.vulkan.compute-0.xml
new file mode 100644
index 0000000..bac2fde
--- /dev/null
+++ b/data/etc/android.hardware.vulkan.compute-0.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device supports Vulkan
+ compute level 0. -->
+<permissions>
+ <feature name="android.hardware.vulkan.compute" version="0" />
+</permissions>
diff --git a/data/etc/android.hardware.wifi.aware.xml b/data/etc/android.hardware.wifi.aware.xml
new file mode 100644
index 0000000..ae6272e
--- /dev/null
+++ b/data/etc/android.hardware.wifi.aware.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device includes WiFi Aware. -->
+<permissions>
+ <feature name="android.hardware.wifi.aware" />
+</permissions>
diff --git a/data/etc/android.hardware.wifi.nan.xml b/data/etc/android.hardware.wifi.nan.xml
deleted file mode 100644
index e557610..0000000
--- a/data/etc/android.hardware.wifi.nan.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard feature indicating that the device includes WiFi NAN. -->
-<permissions>
- <feature name="android.hardware.wifi.nan" />
-</permissions>
diff --git a/data/etc/android.software.activities_on_secondary_displays.xml b/data/etc/android.software.activities_on_secondary_displays.xml
new file mode 100644
index 0000000..db1bdb5
--- /dev/null
+++ b/data/etc/android.software.activities_on_secondary_displays.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<permissions>
+ <feature name="android.software.activities_on_secondary_displays" />
+</permissions>
diff --git a/data/etc/android.software.autofill.xml b/data/etc/android.software.autofill.xml
new file mode 100644
index 0000000..c510d0c
--- /dev/null
+++ b/data/etc/android.software.autofill.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<permissions>
+ <feature name="android.software.autofill" />
+</permissions>
diff --git a/data/etc/android.software.companion_device_setup.xml b/data/etc/android.software.companion_device_setup.xml
new file mode 100644
index 0000000..e60ef88
--- /dev/null
+++ b/data/etc/android.software.companion_device_setup.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<permissions>
+ <feature name="android.software.companion_device_setup" />
+</permissions>
diff --git a/data/etc/android.software.cts.xml b/data/etc/android.software.cts.xml
new file mode 100644
index 0000000..0414c9a
--- /dev/null
+++ b/data/etc/android.software.cts.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.
+-->
+
+<permissions>
+ <!-- This is Android and fully CTS compatible. Basically this is for CTS tests to use. -->
+ <feature name="android.software.cts" />
+</permissions>
diff --git a/data/etc/android.software.preview_sdk.xml b/data/etc/android.software.preview_sdk.xml
new file mode 100644
index 0000000..928b4b3
--- /dev/null
+++ b/data/etc/android.software.preview_sdk.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.
+-->
+
+<permissions>
+ <!-- The device is running a preview (i.e. unofficial) API version. -->
+ <feature name="android.software.preview_sdk" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index ab89ef5..835504f 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -23,6 +23,9 @@
devices.
-->
<permissions>
+ <!-- This is Android and fully CTS compatible. Basically this is for CTS tests to use. -->
+ <feature name="android.software.cts" />
+
<feature name="android.hardware.audio.output" />
<feature name="android.hardware.location" />
<feature name="android.hardware.location.network" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index f9464e8..0d5d206 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -23,6 +23,9 @@
devices.
-->
<permissions>
+ <!-- This is Android and fully CTS compatible. Basically this is for CTS tests to use. -->
+ <feature name="android.software.cts" />
+
<feature name="android.hardware.audio.output" />
<feature name="android.hardware.camera" />
<feature name="android.hardware.location" />
@@ -42,7 +45,11 @@
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.input_methods" />
+ <feature name="android.software.picture_in_picture" />
+ <feature name="android.software.activities_on_secondary_displays" />
<feature name="android.software.print" />
+ <feature name="android.software.companion_device_setup" />
+ <feature name="android.software.autofill" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
@@ -55,6 +62,9 @@
<!-- Devices with all optimizations required to be a "VR Ready" device that
pass all CTS tests for this feature must include feature
android.hardware.vr.high_performance -->
+ <!-- Devices that support VR headtracking features and pass all CDD
+ requirements may include
+ android.hardware.vr.headtracking -->
<!-- devices with GPS must include android.hardware.location.gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 8128165..9b88648 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -23,6 +23,9 @@
devices.
-->
<permissions>
+ <!-- This is Android and fully CTS compatible. Basically this is for CTS tests to use. -->
+ <feature name="android.software.cts" />
+
<feature name="android.hardware.audio.output" />
<feature name="android.hardware.location" />
<feature name="android.hardware.location.network" />
@@ -42,7 +45,11 @@
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.input_methods" />
+ <feature name="android.software.picture_in_picture" />
+ <feature name="android.software.activities_on_secondary_displays" />
<feature name="android.software.print" />
+ <feature name="android.software.companion_device_setup" />
+ <feature name="android.software.autofill" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index 84230da..d7c3730 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -21,6 +21,9 @@
Wearable devices include watches, glasses, backpacks, and sweaters.
-->
<permissions>
+ <!-- This is Android and fully CTS compatible. Basically this is for CTS tests to use. -->
+ <feature name="android.software.cts" />
+
<feature name="android.hardware.location" />
<!-- devices supporting compass/magnitometer sensor must include
android.hardware.sensor.compass.xml -->
@@ -36,6 +39,9 @@
<!-- device administration -->
<feature name="android.software.device_admin" />
+ <!-- input management and third-party input method editors -->
+ <feature name="android.software.input_methods" />
+
<!-- devices with GPS must include device/google/clockwork/gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
android.hardware.camera.autofocus.xml or
diff --git a/docs/Doxyfile b/docs/Doxyfile
index 3ea453f..bb0ca32 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -14,90 +14,90 @@
# Project related configuration options
#---------------------------------------------------------------------------
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
# http://www.gnu.org/software/libiconv for the list of possible encodings.
DOXYFILE_ENCODING = UTF-8
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
# to put quotes around the project name if it contains spaces.
PROJECT_NAME = "NDK API"
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER =
+PROJECT_NUMBER =
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
# a quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = ""
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
# Doxygen will copy the logo to the output directory.
PROJECT_LOGO = logo.png
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
-OUTPUT_DIRECTORY =
+OUTPUT_DIRECTORY =
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
# otherwise cause performance problems for the file system.
CREATE_SUBDIRS = NO
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
OUTPUT_LANGUAGE = English
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
# "represents" "a" "an" "the"
ABBREVIATE_BRIEF = "The $name class" \
@@ -112,256 +112,256 @@
an \
the
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip. Note that you specify absolute paths here, but also
-# relative paths, which will be relative from the directory where doxygen is
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
# started.
-STRIP_FROM_PATH =
+STRIP_FROM_PATH =
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
# are normally passed to the compiler using the -I flag.
-STRIP_FROM_INC_PATH =
+STRIP_FROM_INC_PATH =
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
# (thus requiring an explicit @brief command for a brief description.)
JAVADOC_AUTOBRIEF = NO
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
# an explicit \brief command for a brief description.)
QT_AUTOBRIEF = NO
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
# description. Set this tag to YES if you prefer the old behaviour instead.
MULTILINE_CPP_IS_BRIEF = NO
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
# re-implements.
INHERIT_DOCS = YES
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
# be part of the file/class/namespace that contains it.
SEPARATE_MEMBER_PAGES = NO
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 4
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
-ALIASES =
+ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding
-# "class=itcl::class" will allow you to use the command class in the
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
# itcl::class meaning.
-TCL_SUBST =
+TCL_SUBST =
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = YES
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
# scopes will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
# Fortran.
OPTIMIZE_FOR_FORTRAN = NO
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
# VHDL.
OPTIMIZE_OUTPUT_VHDL = NO
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given
-# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension,
-# and language is one of the parsers supported by doxygen: IDL, Java,
-# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
-# C++. For instance to make doxygen treat .inc files as Fortran files (default
-# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
-# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
# files are not read by doxygen.
-EXTENSION_MAPPING =
+EXTENSION_MAPPING =
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
-# comments according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you
-# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
# Disable only in case of backward compatibilities issues.
MARKDOWN_SUPPORT = YES
-# When enabled doxygen tries to link words that correspond to documented classes,
-# or namespaces to their corresponding documentation. Such a link can be
-# prevented in individual cases by by putting a % sign in front of the word or
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
# globally by setting AUTOLINK_SUPPORT to NO.
AUTOLINK_SUPPORT = YES
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
BUILTIN_STL_SUPPORT = NO
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
CPP_CLI_SUPPORT = NO
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
# instead of private inheritance when no explicit protection keyword is present.
SIP_SUPPORT = NO
-# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES (the
-# default) will make doxygen replace the get and set methods by a property in
-# the documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
# methods anyway, you should set this option to NO.
IDL_PROPERTY_SUPPORT = YES
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
# the \nosubgrouping command.
SUBGROUPING = YES
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
# section (for LaTeX and RTF).
INLINE_GROUPED_CLASSES = NO
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields will be shown inline in the documentation
-# of the scope in which they are defined (i.e. file, namespace, or group
-# documentation), provided this scope is documented. If set to NO (the default),
-# structs, classes, and unions are shown on a separate page (for HTML and Man
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
# pages) or section (for LaTeX and RTF).
INLINE_SIMPLE_STRUCTS = NO
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
TYPEDEF_HIDES_STRUCT = NO
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penalty.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will roughly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
# corresponding to a cache size of 2^16 = 65536 symbols.
SYMBOL_CACHE_SIZE = 0
-# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
-# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
-# their name and scope. Since this can be an expensive process and often the
-# same symbol appear multiple times in the code, doxygen keeps a cache of
-# pre-resolved symbols. If the cache is too small doxygen will become slower.
-# If the cache is too large, memory is wasted. The cache size is given by this
-# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
# corresponding to a cache size of 2^16 = 65536 symbols.
LOOKUP_CACHE_SIZE = 0
@@ -370,329 +370,329 @@
# Build related configuration options
#---------------------------------------------------------------------------
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = YES
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
# scope will be included in the documentation.
EXTRACT_PACKAGE = NO
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
# If set to NO (the default) only methods in the interface are included.
EXTRACT_LOCAL_METHODS = NO
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
# anonymous namespaces are hidden.
EXTRACT_ANON_NSPACES = NO
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
# documentation.
HIDE_FRIEND_COMPOUNDS = NO
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
# function's detailed documentation block.
HIDE_IN_BODY_DOCS = NO
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
CASE_SENSE_NAMES = NO
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = YES
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
# rather than with sharp brackets.
FORCE_LOCAL_INCLUDES = NO
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = YES
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
# declaration order.
SORT_BRIEF_DOCS = NO
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
SORT_MEMBERS_CTORS_1ST = NO
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
# the group names will appear in their defined order.
SORT_GROUP_NAMES = NO
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
# alphabetical list.
SORT_BY_SCOPE_NAME = NO
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
# will still accept a match between prototype and implementation in such cases.
STRICT_PROTO_MATCHING = NO
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
# \deprecated commands in the documentation.
GENERATE_DEPRECATEDLIST= YES
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if section-label ... \endif
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if section-label ... \endif
# and \cond section-label ... \endcond blocks.
-ENABLED_SECTIONS =
+ENABLED_SECTIONS =
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 26
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
# Folder Tree View (if specified). The default is YES.
SHOW_FILES = YES
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page. This will remove the Namespaces entry from the Quick Index
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page. This will remove the Namespaces entry from the Quick Index
# and from the Folder Tree View (if specified). The default is YES.
SHOW_NAMESPACES = YES
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
# is used as the file version. See the manual for examples.
-FILE_VERSION_FILTER =
+FILE_VERSION_FILTER =
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
# DoxygenLayout.xml will be used as the name of the layout file.
-LAYOUT_FILE =
+LAYOUT_FILE =
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# requires the bibtex tool to be installed. See also
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
-# feature you need bibtex and perl available in the search path. Do not use
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
# file names with spaces, bibtex cannot handle them.
-CITE_BIB_FILES =
+CITE_BIB_FILES =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
-# The QUIET tag can be used to turn on/off the messages that are generated
+# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
# don't exist or using markup commands wrongly.
WARN_IF_DOC_ERROR = YES
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
# documentation.
WARN_NO_PARAMDOC = NO
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
# be obtained via FILE_VERSION_FILTER)
WARN_FORMAT = "$file:$line: $text"
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
# to stderr.
-WARN_LOGFILE =
+WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ../include/android ../../av/include/ndk ../../av/include/camera/ndk
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
# the list of possible encodings.
INPUT_ENCODING = UTF-8
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
# *.f90 *.f *.for *.vhd *.vhdl
FILE_PATTERNS = *.c \
@@ -730,159 +730,159 @@
*.vhd \
*.vhdl
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = YES
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-# Note that relative paths are relative to the directory from which doxygen is
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
# run.
-EXCLUDE =
+EXCLUDE =
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
EXCLUDE_SYMLINKS = NO
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
# for example use the pattern */test/*
-EXCLUDE_PATTERNS =
+EXCLUDE_PATTERNS =
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
-EXCLUDE_SYMBOLS =
+EXCLUDE_SYMBOLS =
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
# the \include command).
-EXAMPLE_PATH =
+EXAMPLE_PATH =
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS = *
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
# the \image command).
-IMAGE_PATH =
+IMAGE_PATH =
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
# ignored.
-INPUT_FILTER =
+INPUT_FILTER =
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
# non of the patterns match the file name, INPUT_FILTER is applied.
-FILTER_PATTERNS =
+FILTER_PATTERNS =
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
FILTER_SOURCE_FILES = NO
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
# FILTER_SOURCE_FILES is enabled.
-FILTER_SOURCE_PATTERNS =
+FILTER_SOURCE_PATTERNS =
-# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page (index.html).
-# This can be useful if you have a project on for instance GitHub and want reuse
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page (index.html).
+# This can be useful if you have a project on for instance GitHub and want reuse
# the introduction page also for the doxygen output.
-USE_MDFILE_AS_MAINPAGE =
+USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
# VERBATIM_HEADERS is set to NO.
SOURCE_BROWSER = NO
-# Setting the INLINE_SOURCES tag to YES will include the body
+# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C, C++ and Fortran comments will always remain visible.
STRIP_CODE_COMMENTS = NO
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = NO
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = NO
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
# link to the source code. Otherwise they will link to the documentation.
REFERENCES_LINK_SOURCE = YES
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
# will need version 4.8.6 or higher.
USE_HTAGS = NO
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = NO
@@ -891,170 +891,170 @@
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = NO
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
-IGNORE_PREFIX =
+IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = $(HTML_OUTPUT)
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-# for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is advised to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
# changing the value of configuration settings such as GENERATE_TREEVIEW!
HTML_HEADER = $(HTML_HEADER)
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER = $(HTML_FOOTER)
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If left blank doxygen will
-# generate a default style sheet. Note that it is recommended to use
-# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
# tag will in the future become obsolete.
-HTML_STYLESHEET =
+HTML_STYLESHEET =
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
-# user-defined cascading style sheet that is included after the standard
-# style sheets created by doxygen. Using this option one can overrule
-# certain style aspects. This is preferred over using HTML_STYLESHEET
-# since it does not replace the standard style sheet and is therefor more
-# robust against future updates. Doxygen will copy the style sheet file to
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
# the output directory.
-HTML_EXTRA_STYLESHEET =
+HTML_EXTRA_STYLESHEET =
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
# the files will be copied as-is; there are no commands or markers available.
-HTML_EXTRA_FILES =
+HTML_EXTRA_FILES =
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the style sheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
# The allowed range is 0 to 359.
HTML_COLORSTYLE_HUE = 220
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
# grayscales only. A value of 255 will produce the most vivid colors.
HTML_COLORSTYLE_SAT = 0
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
# and 100 does not change the gamma.
HTML_COLORSTYLE_GAMMA = 80
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
# this to NO can help when comparing the output of multiple runs.
HTML_TIMESTAMP = YES
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
# page has loaded.
HTML_DYNAMIC_SECTIONS = NO
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
-# entries shown in the various tree structured indices initially; the user
-# can expand and collapse entries dynamically later on. Doxygen will expand
-# the tree to such a level that at most the specified number of entries are
-# visible (unless a fully collapsed tree already exceeds this amount).
-# So setting the number of entries 1 will produce a full collapsed tree by
-# default. 0 is a special value representing an infinite number of entries
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
# and will result in a full expanded tree by default.
HTML_INDEX_NUM_ENTRIES = 100
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
GENERATE_DOCSET = NO
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
# can be grouped.
DOCSET_FEEDNAME = "Doxygen generated docs"
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
# will append .docset to the name.
DOCSET_BUNDLE_ID = org.doxygen.Project
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
-# identify the documentation publisher. This should be a reverse domain-name
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
# style string, e.g. com.mycompany.MyDocSet.documentation.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
@@ -1063,361 +1063,361 @@
DOCSET_PUBLISHER_NAME = Publisher
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
# written to the html output directory.
-CHM_FILE =
+CHM_FILE =
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
# the HTML help compiler on the generated index.hhp.
-HHC_LOCATION =
+HHC_LOCATION =
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
# content.
-CHM_INDEX_ENCODING =
+CHM_INDEX_ENCODING =
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the HTML help documentation and to the tree view.
TOC_EXPAND = NO
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
# Qt Compressed Help (.qch) of the generated HTML documentation.
GENERATE_QHP = NO
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
# The path specified is relative to the HTML output folder.
-QCH_FILE =
+QCH_FILE =
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
# http://doc.trolltech.com/qthelpproject.html#namespace
QHP_NAMESPACE = org.doxygen.Project
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
# http://doc.trolltech.com/qthelpproject.html#virtual-folders
QHP_VIRTUAL_FOLDER = doc
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
# http://doc.trolltech.com/qthelpproject.html#custom-filters
-QHP_CUST_FILTER_NAME =
+QHP_CUST_FILTER_NAME =
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
# Qt Help Project / Custom Filters</a>.
-QHP_CUST_FILTER_ATTRS =
+QHP_CUST_FILTER_ATTRS =
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
# Qt Help Project / Filter Attributes</a>.
-QHP_SECT_FILTER_ATTRS =
+QHP_SECT_FILTER_ATTRS =
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
# .qhp file.
-QHG_LOCATION =
+QHG_LOCATION =
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
# the help appears.
GENERATE_ECLIPSEHELP = NO
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
# this name.
ECLIPSE_DOC_ID = org.doxygen.Project
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
-# at top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it. Since the tabs have the same information as the
-# navigation tree you can set this option to NO if you already set
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
# GENERATE_TREEVIEW to YES.
DISABLE_INDEX = YES
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-# Since the tree basically has the same information as the tab index you
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
# could consider to set DISABLE_INDEX to NO when enabling this option.
GENERATE_TREEVIEW = NO
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
# values from appearing in the overview section.
ENUM_VALUES_PER_LINE = 4
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
# links to external symbols imported via tag files in a separate window.
EXT_LINKS_IN_WINDOW = NO
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
# to force them to be regenerated.
FORMULA_FONTSIZE = 10
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
# in the HTML output before the changes have effect.
FORMULA_TRANSPARENT = YES
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you may also need to install MathJax separately and
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
# configure the path to it using the MATHJAX_RELPATH option.
USE_MATHJAX = NO
-# When MathJax is enabled you can set the default output format to be used for
-# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
-# SVG. The default value is HTML-CSS, which is slower, but has the best
+# When MathJax is enabled you can set the default output format to be used for
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
# compatibility.
MATHJAX_FORMAT = HTML-CSS
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to
-# the MathJax Content Delivery Network so you can quickly see the result without
-# installing MathJax. However, it is strongly recommended to install a local
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax. However, it is strongly recommended to install a local
# copy of MathJax from http://www.mathjax.org before deployment.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
# names that should be enabled during MathJax rendering.
-MATHJAX_EXTENSIONS =
+MATHJAX_EXTENSIONS =
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
SEARCHENGINE = NO
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript.
-# There are two flavours of web server based search depending on the
-# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
-# searching and an index file used by the script. When EXTERNAL_SEARCH is
-# enabled the indexing and searching needs to be provided by external tools.
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
# See the manual for details.
SERVER_BASED_SEARCH = NO
-# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
-# script for searching. Instead the search results are written to an XML file
-# which needs to be processed by an external indexer. Doxygen will invoke an
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain
-# the search results. Doxygen ships with an example indexer (doxyindexer) and
-# search engine (doxysearch.cgi) which are based on the open source search engine
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search engine
# library Xapian. See the manual for configuration details.
EXTERNAL_SEARCH = NO
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server
-# which will returned the search results when EXTERNAL_SEARCH is enabled.
-# Doxygen ships with an example search engine (doxysearch) which is based on
-# the open source search engine library Xapian. See the manual for configuration
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
# details.
-SEARCHENGINE_URL =
+SEARCHENGINE_URL =
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
-# search data is written to a file for indexing by an external tool. With the
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
# SEARCHDATA_FILE tag the name of this file can be specified.
SEARCHDATA_FILE = searchdata.xml
-# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
# projects and redirect the results back to the right project.
-EXTERNAL_SEARCH_ID =
+EXTERNAL_SEARCH_ID =
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
-# projects other than the one defined by this configuration file, but that are
-# all added to the same external search index. Each project needs to have a
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
-# of to a relative location where the documentation can be found.
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
+# of to a relative location where the documentation can be found.
# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
-EXTRA_SEARCH_MAPPINGS =
+EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = NO
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
# Makefile that is written to the output directory.
LATEX_CMD_NAME = latex
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
-EXTRA_PACKAGES =
+EXTRA_PACKAGES =
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
-LATEX_HEADER =
+LATEX_HEADER =
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
# standard footer. Notice: only use this tag if you know what you are doing!
-LATEX_FOOTER =
+LATEX_FOOTER =
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = YES
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = YES
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
# in the output.
LATEX_HIDE_INDICES = NO
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
# such as SOURCE_BROWSER.
LATEX_SOURCE_CODE = NO
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
# http://en.wikipedia.org/wiki/BibTeX for more info.
LATEX_BIB_STYLE = plain
@@ -1426,68 +1426,68 @@
# configuration options related to the RTF output
#---------------------------------------------------------------------------
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
-# Load style sheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
-RTF_STYLESHEET_FILE =
+RTF_STYLESHEET_FILE =
-# Set optional variables used in the generation of an rtf document.
+# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
-RTF_EXTENSIONS_FILE =
+RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
-# The MAN_EXTENSION tag determines the extension that is added to
+# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
@@ -1496,33 +1496,33 @@
# configuration options related to the XML output
#---------------------------------------------------------------------------
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
# the code including all documentation.
GENERATE_XML = NO
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `xml' will be used as the default path.
XML_OUTPUT = xml
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
# syntax of the XML files.
-XML_SCHEMA =
+XML_SCHEMA =
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
# syntax of the XML files.
-XML_DTD =
+XML_DTD =
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
XML_PROGRAMLISTING = YES
@@ -1531,10 +1531,10 @@
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
@@ -1543,97 +1543,97 @@
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
# moment.
GENERATE_PERLMOD = NO
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
# to generate PDF and DVI output from the Perl module output.
PERLMOD_LATEX = NO
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader. This is useful
-# if you want to understand what is going on. On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
PERLMOD_PRETTY = YES
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
# Makefile don't overwrite each other's variables.
-PERLMOD_MAKEVAR_PREFIX =
+PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
# files.
-ENABLE_PREPROCESSING = YES
+ENABLE_PREPROCESSING = NO
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = NO
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_DEFINED tags.
EXPAND_ONLY_PREDEF = NO
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# pointed to by INCLUDE_PATH will be searched when a #include is found.
SEARCH_INCLUDES = YES
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
# the preprocessor.
-INCLUDE_PATH =
+INCLUDE_PATH =
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
-INCLUDE_FILE_PATTERNS =
+INCLUDE_FILE_PATTERNS =
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
-PREDEFINED =
+PREDEFINED =
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
# overrules the definition found in the source code.
-EXPAND_AS_DEFINED =
+EXPAND_AS_DEFINED =
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
# semicolon, because these will confuse the parser if not removed.
SKIP_FUNCTION_MACROS = YES
@@ -1642,37 +1642,37 @@
# Configuration::additions related to external references
#---------------------------------------------------------------------------
-# The TAGFILES option can be used to specify one or more tagfiles. For each
-# tag file the location of the external documentation should be added. The
-# format of a tag file without this location is as follows:
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths
-# or URLs. Note that each tag file must have a unique name (where the name does
-# NOT include the path). If a tag file is not located in the directory in which
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
# doxygen is run, you must also specify the path to the tagfile here.
-TAGFILES =
+TAGFILES =
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
-GENERATE_TAGFILE =
+GENERATE_TAGFILE =
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
-# The PERL_PATH should be the absolute path and name of the perl script
+# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
@@ -1681,222 +1681,222 @@
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
# install and use dot, since it yields more powerful graphs.
CLASS_DIAGRAMS = NO
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
-MSCGEN_PATH =
+MSCGEN_PATH =
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = NO
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
# between CPU load and processing speed.
DOT_NUM_THREADS = 0
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font.
DOT_FONTNAME = Helvetica
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
# The default size is 10pt.
DOT_FONTSIZE = 10
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
# set the path where dot can find it.
-DOT_FONTPATH =
+DOT_FONTPATH =
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
# CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for groups, showing the direct groups dependencies
GROUP_GRAPHS = YES
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
UML_LOOK = NO
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside
-# the class node. If there are many fields or methods and many nodes the
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
-# threshold limits the number of items for each type to make the size more
-# managable. Set this to 0 for no limit. Note that the threshold may be
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
# exceeded by 50% before the limit is enforced.
UML_LIMIT_NUM_FIELDS = 10
-# If set to YES, the inheritance and collaboration graphs will show the
+# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = NO
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
# for selected functions only using the \callgraph command.
CALL_GRAPH = NO
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
# graphs for selected functions only using the \callergraph command.
CALLER_GRAPH = NO
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will generate a graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
-# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
# relations between the files in the directories.
DIRECTORY_GRAPH = YES
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
# visible in IE 9+ (other browsers do not have this requirement).
DOT_IMAGE_FORMAT = png
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
# visible. Older versions of IE do not have SVG support.
INTERACTIVE_SVG = NO
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
-DOT_PATH =
+DOT_PATH =
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
# \dotfile command).
-DOTFILE_DIRS =
+DOTFILE_DIRS =
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
# \mscfile command).
-MSCFILE_DIRS =
+MSCFILE_DIRS =
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
DOT_GRAPH_MAX_NODES = 50
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
MAX_DOT_GRAPH_DEPTH = 0
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
# a graph (i.e. they become hard to read).
DOT_TRANSPARENT = NO
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
# support this, this feature is disabled by default.
DOT_MULTI_TARGETS = NO
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
diff --git a/include/android/asset_manager.h b/include/android/asset_manager.h
index d654839..7ef3ecb 100644
--- a/include/android/asset_manager.h
+++ b/include/android/asset_manager.h
@@ -26,6 +26,9 @@
#ifndef ANDROID_ASSET_MANAGER_H
#define ANDROID_ASSET_MANAGER_H
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -131,6 +134,7 @@
*/
off_t AAsset_seek(AAsset* asset, off_t offset, int whence);
+#if __ANDROID_API__ >= 13
/**
* Seek to the specified offset within the asset data. 'whence' uses the
* same constants as lseek()/fseek().
@@ -141,6 +145,7 @@
* Returns the new position on success, or (off64_t) -1 on error.
*/
off64_t AAsset_seek64(AAsset* asset, off64_t offset, int whence);
+#endif
/**
* Close the asset, freeing all associated resources.
@@ -159,23 +164,27 @@
*/
off_t AAsset_getLength(AAsset* asset);
+#if __ANDROID_API__ >= 13
/**
* Report the total size of the asset data. Reports the size using a 64-bit
* number insted of 32-bit as AAsset_getLength.
*/
off64_t AAsset_getLength64(AAsset* asset);
+#endif
/**
* Report the total amount of asset data that can be read from the current position.
*/
off_t AAsset_getRemainingLength(AAsset* asset);
+#if __ANDROID_API__ >= 13
/**
* Report the total amount of asset data that can be read from the current position.
*
* Uses a 64-bit number instead of a 32-bit number as AAsset_getRemainingLength does.
*/
off64_t AAsset_getRemainingLength64(AAsset* asset);
+#endif
/**
* Open a new file descriptor that can be used to read the asset data. If the
@@ -187,6 +196,7 @@
*/
int AAsset_openFileDescriptor(AAsset* asset, off_t* outStart, off_t* outLength);
+#if __ANDROID_API__ >= 13
/**
* Open a new file descriptor that can be used to read the asset data.
*
@@ -197,6 +207,7 @@
* compressed).
*/
int AAsset_openFileDescriptor64(AAsset* asset, off64_t* outStart, off64_t* outLength);
+#endif
/**
* Returns whether this asset's internal buffer is allocated in ordinary RAM (i.e. not
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 261e64f..2def64d 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -56,9 +56,9 @@
ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
/** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
ANDROID_BITMAP_FORMAT_RGB_565 = 4,
- /** Red: 4 bits, Green: 4 bits, Blue: 4 bits, Alpha: 4 bits. **/
+ /** Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead. **/
ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
- /** Deprecated. */
+ /** Alpha: 8 bits. */
ANDROID_BITMAP_FORMAT_A_8 = 8,
};
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 02c83dc..43346fe 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -30,6 +30,8 @@
__BEGIN_DECLS
+#if __ANDROID_API__ >= 24
+
struct AChoreographer;
typedef struct AChoreographer AChoreographer;
@@ -62,6 +64,9 @@
*/
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data, long delayMillis);
+
+#endif /* __ANDROID_API__ >= 24 */
+
__END_DECLS
#endif // ANDROID_CHOREOGRAPHER_H
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 81f71a9..6287332 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -26,6 +26,8 @@
#ifndef ANDROID_CONFIGURATION_H
#define ANDROID_CONFIGURATION_H
+#include <sys/cdefs.h>
+
#include <android/asset_manager.h>
#ifdef __cplusplus
@@ -265,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,
/**
@@ -298,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,
@@ -424,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_COLOR_MODE = 0x10000,
+ /**
* Constant used to to represent MNC (Mobile Network Code) zero.
* 0 cannot be used, since it is used to represent an undefined MNC.
*/
@@ -628,6 +671,7 @@
*/
void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight);
+#if __ANDROID_API__ >= 13
/**
* Return the current configuration screen width in dp units, or
* ACONFIGURATION_SCREEN_WIDTH_DP_ANY if not set.
@@ -660,7 +704,9 @@
* Set the configuration's smallest screen width in dp units.
*/
void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value);
+#endif /* __ANDROID_API__ >= 13 */
+#if __ANDROID_API__ >= 17
/**
* Return the configuration's layout direction, or
* ACONFIGURATION_LAYOUTDIR_ANY if not set.
@@ -671,6 +717,7 @@
* Set the configuration's layout direction.
*/
void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value);
+#endif /* __ANDROID_API__ >= 17 */
/**
* Perform a diff between two configurations. Returns a bit mask of
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/input.h b/include/android/input.h
index fd9fa98..0829989 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -26,6 +26,8 @@
#ifndef _ANDROID_INPUT_H
#define _ANDROID_INPUT_H
+#include <sys/cdefs.h>
+
/******************************************************************
*
* IMPORTANT NOTICE:
@@ -833,6 +835,8 @@
AINPUT_SOURCE_BLUETOOTH_STYLUS = 0x00008000 | AINPUT_SOURCE_STYLUS,
/** trackball */
AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
+ /** mouse relative */
+ AINPUT_SOURCE_MOUSE_RELATIVE = 0x00020000 | AINPUT_SOURCE_CLASS_NAVIGATION,
/** touchpad */
AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
/** navigation */
@@ -978,8 +982,10 @@
*/
int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event);
+#if __ANDROID_API__ >= 14
/** Get the button state of all buttons that are pressed. */
int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event);
+#endif
/**
* Get a bitfield indicating which edges, if any, were touched by this motion event.
@@ -1044,12 +1050,14 @@
*/
int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index);
+#if __ANDROID_API__ >= 14
/**
* Get the tool type of a pointer for the given pointer index.
* The tool type indicates the type of tool used to make contact such as a
* finger or stylus, if known.
*/
int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index);
+#endif
/**
* Get the original raw X coordinate of this event.
@@ -1139,9 +1147,11 @@
*/
float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index);
+#if __ANDROID_API__ >= 13
/** Get the value of the request axis for the given pointer index. */
float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
int32_t axis, size_t pointer_index);
+#endif
/**
* Get the number of historical points in this event. These are movements that
@@ -1272,12 +1282,14 @@
float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
+#if __ANDROID_API__ >= 13
/**
* Get the historical value of the request axis for the given pointer index
* that occurred between this event and the previous motion event.
*/
float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
int32_t axis, size_t pointer_index, size_t history_index);
+#endif
struct AInputQueue;
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 6c718c9..97892f8 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -51,16 +51,15 @@
* on failure with an appropriate errno value set.
*/
+#if __ANDROID_API__ >= 24
/**
* Set the network to be used by the given socket file descriptor.
*
- * To clear a previous socket binding invoke with NETWORK_UNSPECIFIED.
+ * To clear a previous socket binding, invoke with NETWORK_UNSPECIFIED.
*
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.Network#bindSocket()](https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket))
*
- * [ android.net.Network#bindSocket() ]
- * https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket)
*/
int android_setsocknetwork(net_handle_t network, int fd);
@@ -74,12 +73,10 @@
* resolutions will fail. This is by design so an application doesn't
* accidentally use sockets it thinks are still bound to a particular network.
*
- * To clear a previous process binding invoke with NETWORK_UNSPECIFIED.
+ * To clear a previous process binding, invoke with NETWORK_UNSPECIFIED.
*
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network))
*
- * [ android.net.ConnectivityManager#setProcessDefaultNetwork() ]
- * https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network)
*/
int android_setprocnetwork(net_handle_t network);
@@ -95,15 +92,15 @@
* - either |node| or |service| may be NULL, but not both
* - |res| must not be NULL
*
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.Network#getAllByName()](https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String))
*
- * [ android.net.Network#getAllByName() ]
- * https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String)
*/
int android_getaddrinfofornetwork(net_handle_t network,
const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res);
+#endif /* __ANDROID_API__ >= 24 */
+
__END_DECLS
#endif // ANDROID_MULTINETWORK_H
diff --git a/include/android/native_window.h b/include/android/native_window.h
deleted file mode 100644
index cf07f1a..0000000
--- a/include/android/native_window.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @addtogroup NativeActivity Native Activity
- * @{
- */
-
-/**
- * @file native_window.h
- */
-
-#ifndef ANDROID_NATIVE_WINDOW_H
-#define ANDROID_NATIVE_WINDOW_H
-
-#include <android/rect.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Pixel formats that a window can use.
- */
-enum {
- /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
- WINDOW_FORMAT_RGBA_8888 = 1,
- /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
- WINDOW_FORMAT_RGBX_8888 = 2,
- /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
- WINDOW_FORMAT_RGB_565 = 4,
-};
-
-struct ANativeWindow;
-/**
- * {@link ANativeWindow} is opaque type that provides access to a native window.
- *
- * A pointer can be obtained using ANativeWindow_fromSurface().
- */
-typedef struct ANativeWindow ANativeWindow;
-
-/**
- * {@link ANativeWindow} is a struct that represents a windows buffer.
- *
- * A pointer can be obtained using ANativeWindow_lock().
- */
-typedef struct ANativeWindow_Buffer {
- // The number of pixels that are show horizontally.
- int32_t width;
-
- // The number of pixels that are shown vertically.
- int32_t height;
-
- // The number of *pixels* that a line in the buffer takes in
- // memory. This may be >= width.
- int32_t stride;
-
- // The format of the buffer. One of WINDOW_FORMAT_*
- int32_t format;
-
- // The actual bits.
- void* bits;
-
- // Do not touch.
- uint32_t reserved[6];
-} ANativeWindow_Buffer;
-
-/**
- * Acquire a reference on the given ANativeWindow object. This prevents the object
- * from being deleted until the reference is removed.
- */
-void ANativeWindow_acquire(ANativeWindow* window);
-
-/**
- * Remove a reference that was previously acquired with ANativeWindow_acquire().
- */
-void ANativeWindow_release(ANativeWindow* window);
-
-/**
- * Return the current width in pixels of the window surface. Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getWidth(ANativeWindow* window);
-
-/**
- * Return the current height in pixels of the window surface. Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getHeight(ANativeWindow* window);
-
-/**
- * Return the current pixel format of the window surface. Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getFormat(ANativeWindow* window);
-
-/**
- * Change the format and size of the window buffers.
- *
- * The width and height control the number of pixels in the buffers, not the
- * dimensions of the window on screen. If these are different than the
- * window's physical size, then it buffer will be scaled to match that size
- * when compositing it to the screen.
- *
- * For all of these parameters, if 0 is supplied then the window's base
- * value will come back in force.
- *
- * width and height must be either both zero or both non-zero.
- *
- */
-int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
- int32_t width, int32_t height, int32_t format);
-
-/**
- * Lock the window's next drawing surface for writing.
- * inOutDirtyBounds is used as an in/out parameter, upon entering the
- * function, it contains the dirty region, that is, the region the caller
- * intends to redraw. When the function returns, inOutDirtyBounds is updated
- * with the actual area the caller needs to redraw -- this region is often
- * extended by ANativeWindow_lock.
- */
-int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
- ARect* inOutDirtyBounds);
-
-/**
- * Unlock the window's drawing surface after previously locking it,
- * posting the new buffer to the display.
- */
-int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
-
-#ifdef __cplusplus
-};
-#endif
-
-#endif // ANDROID_NATIVE_WINDOW_H
-
-/** @} */
diff --git a/include/android/native_window_jni.h b/include/android/native_window_jni.h
index 60a36c3..23b39aa 100644
--- a/include/android/native_window_jni.h
+++ b/include/android/native_window_jni.h
@@ -26,6 +26,8 @@
#ifndef ANDROID_NATIVE_WINDOW_JNI_H
#define ANDROID_NATIVE_WINDOW_JNI_H
+#include <sys/cdefs.h>
+
#include <android/native_window.h>
#include <jni.h>
@@ -42,6 +44,27 @@
*/
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);
+#if __ANDROID_API__ >= 13
+/**
+ * Return the ANativeWindow associated with a Java SurfaceTexture object,
+ * for interacting with it through native code. This acquires a reference
+ * on the ANativeWindow that is returned; be sure to use ANativeWindow_release()
+ * when done with it so that it doesn't leak.
+ */
+ANativeWindow* ANativeWindow_fromSurfaceTexture(JNIEnv* env, jobject surfaceTexture);
+#endif
+
+#if __ANDROID_API__ >= 26
+/**
+ * Return a Java Surface object derived from the ANativeWindow, for interacting
+ * with it through Java code. The returned Java object acquires a reference on
+ * the ANativeWindow; maintains it through general Java object's life cycle;
+ * and will automatically release the reference when the Java object gets garbage
+ * collected.
+ */
+jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window);
+#endif
+
#ifdef __cplusplus
};
#endif
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 5a61213..cdb3fff 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -48,14 +48,21 @@
*
*/
-#include <sys/types.h>
-
#include <android/looper.h>
+#include <sys/types.h>
+#include <math.h>
+#include <stdint.h>
+
#ifdef __cplusplus
extern "C" {
#endif
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+#define ASENSOR_RESOLUTION_INVALID (nanf(""))
+#define ASENSOR_FIFO_COUNT_INVALID (-1)
+#define ASENSOR_DELAY_INVALID INT32_MIN
/**
* Sensor types.
@@ -63,6 +70,10 @@
*/
enum {
/**
+ * Invalid sensor type. Returned by {@link ASensor_getType} as error value.
+ */
+ ASENSOR_TYPE_INVALID = -1,
+ /**
* {@link ASENSOR_TYPE_ACCELEROMETER}
* reporting-mode: continuous
*
@@ -134,6 +145,8 @@
* Sensor Reporting Modes.
*/
enum {
+ /** invalid reporting mode */
+ AREPORTING_MODE_INVALID = -1,
/** continuous reporting */
AREPORTING_MODE_CONTINUOUS = 0,
/** reporting on change */
@@ -144,6 +157,30 @@
AREPORTING_MODE_SPECIAL_TRIGGER = 3
};
+/**
+ * Sensor Direct Report Rates.
+ */
+enum {
+ /** stopped */
+ ASENSOR_DIRECT_RATE_STOP = 0,
+ /** nominal 50Hz */
+ ASENSOR_DIRECT_RATE_NORMAL = 1,
+ /** nominal 200Hz */
+ ASENSOR_DIRECT_RATE_FAST = 2,
+ /** nominal 800Hz */
+ ASENSOR_DIRECT_RATE_VERY_FAST = 3
+};
+
+/**
+ * Sensor Direct Channel Type.
+ */
+enum {
+ /** shared memory created by ASharedMemory_create */
+ ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY = 1,
+ /** AHardwareBuffer */
+ ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER = 2
+};
+
/*
* A few useful constants
*/
@@ -367,12 +404,13 @@
*/
ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type);
+#if __ANDROID_API__ >= 21
/**
* Returns the default sensor with the given type and wakeUp properties or NULL if no sensor
* of this type and wakeUp properties exists.
*/
-ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type,
- bool wakeUp);
+ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp);
+#endif
/**
* Creates a new sensor event queue and associate it with a looper.
@@ -389,15 +427,108 @@
*/
int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* queue);
+#if __ANDROID_API__ >= __ANDROID_API_O__
+/**
+ * Create direct channel based on shared memory
+ *
+ * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} to be used
+ * for configuring sensor direct report.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ * {@link ASensorManager_getInstanceForPackage}.
+ * \param fd file descriptor representing a shared memory created by
+ * {@link ASharedMemory_create}
+ * \param size size to be used, must be less or equal to size of shared memory.
+ *
+ * \return a positive integer as a channel id to be used in
+ * {@link ASensorManager_destroyDirectChannel} and
+ * {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures.
+ */
+int ASensorManager_createSharedMemoryDirectChannel(ASensorManager* manager, int fd, size_t size);
+
+/**
+ * Create direct channel based on AHardwareBuffer
+ *
+ * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER} type to be used
+ * for configuring sensor direct report.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ * {@link ASensorManager_getInstanceForPackage}.
+ * \param buffer {@link AHardwareBuffer} instance created by {@link AHardwareBuffer_allocate}.
+ * \param size the intended size to be used, must be less or equal to size of buffer.
+ *
+ * \return a positive integer as a channel id to be used in
+ * {@link ASensorManager_destroyDirectChannel} and
+ * {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures.
+ */
+int ASensorManager_createHardwareBufferDirectChannel(
+ ASensorManager* manager, AHardwareBuffer const * buffer, size_t size);
+
+/**
+ * Destroy a direct channel
+ *
+ * Destroy a direct channel previously created using {@link ASensorManager_createDirectChannel}.
+ * The buffer used for creating direct channel does not get destroyed with
+ * {@link ASensorManager_destroy} and has to be close or released separately.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ * {@link ASensorManager_getInstanceForPackage}.
+ * \param channelId channel id (a positive integer) returned from
+ * {@link ASensorManager_createSharedMemoryDirectChannel} or
+ * {@link ASensorManager_createHardwareBufferDirectChannel}.
+ */
+void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId);
+
+/**
+ * Configure direct report on channel
+ *
+ * Configure sensor direct report on a direct channel: set rate to value other than
+ * {@link ASENSOR_DIRECT_RATE_STOP} so that sensor event can be directly
+ * written into the shared memory region used for creating the buffer. It returns a positive token
+ * which can be used for identify sensor events from different sensors on success. Calling with rate
+ * {@link ASENSOR_DIRECT_RATE_STOP} will stop direct report of the sensor specified in the channel.
+ *
+ * To stop all active sensor direct report configured to a channel, set sensor to NULL and rate to
+ * {@link ASENSOR_DIRECT_RATE_STOP}.
+ *
+ * In order to successfully configure a direct report, the sensor has to support the specified rate
+ * and the channel type, which can be checked by {@link ASensor_getHighestDirectReportRateLevel} and
+ * {@link ASensor_isDirectChannelTypeSupported}, respectively.
+ *
+ * Example:
+ * \code{.cpp}
+ * ASensorManager *manager = ...;
+ * ASensor *sensor = ...;
+ * int channelId = ...;
+ *
+ * ASensorManager_configureDirectReport(
+ * manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
+ * \endcode
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ * {@link ASensorManager_getInstanceForPackage}.
+ * \param sensor a {@link ASensor} to denote which sensor to be operate. It can be NULL if rate
+ * is {@link ASENSOR_DIRECT_RATE_STOP}, denoting stopping of all active sensor
+ * direct report.
+ * \param channelId channel id (a positive integer) returned from
+ * {@link ASensorManager_createSharedMemoryDirectChannel} or
+ * {@link ASensorManager_createHardwareBufferDirectChannel}.
+ *
+ * \return positive token for success or negative error code.
+ */
+int ASensorManager_configureDirectReport(
+ ASensorManager* manager, ASensor const* sensor, int channelId, int rate);
+#endif
/*****************************************************************************/
/**
* Enable the selected sensor with a specified sampling period and max batch report latency.
* Returns a negative error code on failure.
+ * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before.
*/
int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
- int32_t samplingPeriodUs, int maxBatchReportLatencyUs);
+ int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs);
/**
* Enable the selected sensor. Returns a negative error code on failure.
@@ -438,8 +569,7 @@
* ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
*
*/
-ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue,
- ASensorEvent* events, size_t count);
+ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
/*****************************************************************************/
@@ -471,6 +601,7 @@
*/
int ASensor_getMinDelay(ASensor const* sensor);
+#if __ANDROID_API__ >= 21
/**
* Returns the maximum size of batches for this sensor. Batches will often be
* smaller, as the hardware fifo might be used for other sensors.
@@ -496,6 +627,30 @@
* Returns true if this is a wake up sensor, false otherwise.
*/
bool ASensor_isWakeUpSensor(ASensor const* sensor);
+#endif /* __ANDROID_API__ >= 21 */
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+/**
+ * Test if sensor supports a certain type of direct channel.
+ *
+ * \param sensor a {@link ASensor} to denote the sensor to be checked.
+ * \param channelType Channel type constant, either
+ * {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY}
+ * or {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER}.
+ * \returns true if sensor supports the specified direct channel type.
+ */
+bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType);
+/**
+ * Get the highest direct rate level that a sensor support.
+ *
+ * \param sensor a {@link ASensor} to denote the sensor to be checked.
+ *
+ * \return a ASENSOR_DIRECT_RATE_... enum denoting the highest rate level supported by the sensor.
+ * If return value is {@link ASENSOR_DIRECT_RATE_STOP}, it means the sensor
+ * does not support direct report.
+ */
+int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor);
+#endif
#ifdef __cplusplus
};
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
new file mode 100644
index 0000000..46d2f4b
--- /dev/null
+++ b/include/android/sharedmem.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup Memory
+ * @{
+ */
+
+/**
+ * @file sharedmem.h
+ */
+
+#ifndef ANDROID_SHARED_MEMORY_H
+#define ANDROID_SHARED_MEMORY_H
+
+#include <stddef.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit).
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/**
+ * Structures and functions for a shared memory buffer that can be shared across process.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+
+/**
+ * Create a shared memory region.
+ *
+ * Create shared memory region and returns an file descriptor. The resulting file descriptor can be
+ * mmap'ed to process memory space with PROT_READ | PROT_WRITE | PROT_EXEC. Access to shared memory
+ * region can be restricted with {@link ASharedMemory_setProt}.
+ *
+ * Use close() to release the shared memory region.
+ *
+ * \param name an optional name.
+ * \param size size of the shared memory region
+ * \return file descriptor that denotes the shared memory; error code on failure.
+ */
+int ASharedMemory_create(const char *name, size_t size);
+
+/**
+ * Get the size of the shared memory region.
+ *
+ * \param fd file descriptor of the shared memory region
+ * \return size in bytes; 0 if fd is not a valid shared memory file descriptor.
+ */
+size_t ASharedMemory_getSize(int fd);
+
+/**
+ * Restrict access of shared memory region.
+ *
+ * This function restricts access of a shared memory region. Access can only be removed. The effect
+ * applies globally to all file descriptors in all processes across the system that refer to this
+ * shared memory region. Existing memory mapped regions are not affected.
+ *
+ * It is a common use case to create a shared memory region, map it read/write locally to intialize
+ * content, and then send the shared memory to another process with read only access. Code example
+ * as below (error handling omited).
+ *
+ *
+ * int fd = ASharedMemory_create("memory", 128);
+ *
+ * // By default it has PROT_READ | PROT_WRITE | PROT_EXEC.
+ * char *buffer = (char *) mmap(NULL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ *
+ * strcpy(buffer, "This is an example."); // trivially initialize content
+ *
+ * // limit access to read only
+ * ASharedMemory_setProt(fd, PROT_READ);
+ *
+ * // share fd with another process here and the other process can only map with PROT_READ.
+ *
+ * \param fd file descriptor of the shared memory region.
+ * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting
+ * updated access. Note access can only be removed, but not added back.
+ * \return 0 for success, error code on failure.
+ */
+int ASharedMemory_setProt(int fd, int prot);
+
+#endif
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_SHARED_MEMORY_H
+
+/** @} */
diff --git a/include/android/trace.h b/include/android/trace.h
index e42e334..d3b1fb6 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -14,16 +14,26 @@
* limitations under the License.
*/
+/**
+ * @file trace.h
+ * @brief Writes trace events to the system trace buffer.
+ *
+ * These trace events can be collected and visualized using the Systrace tool.
+ * For information about using the Systrace tool, read <a href="https://developer.android.com/studio/profile/systrace.html">Analyzing UI Performance with Systrace</a>.
+ */
#ifndef ANDROID_NATIVE_TRACE_H
#define ANDROID_NATIVE_TRACE_H
#include <stdbool.h>
+#include <sys/cdefs.h>
#ifdef __cplusplus
extern "C" {
#endif
+#if __ANDROID_API__ >= 23
+
/**
* Returns true if tracing is enabled. Use this signal to avoid expensive computation only necessary
* when tracing is enabled.
@@ -48,6 +58,8 @@
*/
void ATrace_endSection();
+#endif /* __ANDROID_API__ >= 23 */
+
#ifdef __cplusplus
};
#endif
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
new file mode 100644
index 0000000..009dc52
--- /dev/null
+++ b/include/audiomanager/AudioManager.h
@@ -0,0 +1,45 @@
+/*
+ * 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_AUDIOMANAGER_H
+#define ANDROID_AUDIOMANAGER_H
+
+namespace android {
+
+// must be kept in sync with definitions in AudioPlaybackConfiguration.java
+
+#define PLAYER_PIID_INVALID -1
+
+typedef enum {
+ PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11,
+ PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12,
+ PLAYER_TYPE_AAUDIO = 13,
+ PLAYER_TYPE_HW_SOURCE = 14,
+ PLAYER_TYPE_EXTERNAL_PROXY = 15,
+} player_type_t;
+
+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
+
+#endif // ANDROID_AUDIOMANAGER_H
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
new file mode 100644
index 0000000..ce7804b
--- /dev/null
+++ b/include/audiomanager/IAudioManager.h
@@ -0,0 +1,132 @@
+/*
+ * 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_IAUDIOMANAGER_H
+#define ANDROID_IAUDIOMANAGER_H
+
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+#include <hardware/power.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioManager : public IInterface
+{
+public:
+ // These transaction IDs must be kept in sync with the method order from
+ // IAudioService.aidl.
+ enum {
+ // transaction IDs for the unsupported methods are commented out
+ /*
+ ADJUSTSUGGESTEDSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION,
+ ADJUSTSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 1,
+ SETSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 2,
+ ISSTREAMMUTE = IBinder::FIRST_CALL_TRANSACTION + 3,
+ FORCEREMOTESUBMIXFULLVOLUME = IBinder::FIRST_CALL_TRANSACTION + 4,
+ ISMASTERMUTE = IBinder::FIRST_CALL_TRANSACTION + 5,
+ SETMASTERMUTE = IBinder::FIRST_CALL_TRANSACTION + 6,
+ GETSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 7,
+ GETSTREAMMINVOLUME = IBinder::FIRST_CALL_TRANSACTION + 8,
+ GETSTREAMMAXVOLUME = IBinder::FIRST_CALL_TRANSACTION + 9,
+ GETLASTAUDIBLESTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 10,
+ SETMICROPHONEMUTE = IBinder::FIRST_CALL_TRANSACTION + 11,
+ SETRINGERMODEEXTERNAL = IBinder::FIRST_CALL_TRANSACTION + 12,
+ SETRINGERMODEINTERNAL = IBinder::FIRST_CALL_TRANSACTION + 13,
+ GETRINGERMODEEXTERNAL = IBinder::FIRST_CALL_TRANSACTION + 14,
+ GETRINGERMODEINTERNAL = IBinder::FIRST_CALL_TRANSACTION + 15,
+ ISVALIDRINGERMODE = IBinder::FIRST_CALL_TRANSACTION + 16,
+ SETVIBRATESETTING = IBinder::FIRST_CALL_TRANSACTION + 17,
+ GETVIBRATESETTING = IBinder::FIRST_CALL_TRANSACTION + 18,
+ SHOULDVIBRATE = IBinder::FIRST_CALL_TRANSACTION + 19,
+ SETMODE = IBinder::FIRST_CALL_TRANSACTION + 20,
+ GETMODE = IBinder::FIRST_CALL_TRANSACTION + 21,
+ PLAYSOUNDEFFECT = IBinder::FIRST_CALL_TRANSACTION + 22,
+ PLAYSOUNDEFFECTVOLUME = IBinder::FIRST_CALL_TRANSACTION + 23,
+ LOADSOUNDEFFECTS = IBinder::FIRST_CALL_TRANSACTION + 24,
+ UNLOADSOUNDEFFECTS = IBinder::FIRST_CALL_TRANSACTION + 25,
+ RELOADAUDIOSETTINGS = IBinder::FIRST_CALL_TRANSACTION + 26,
+ AVRCPSUPPORTSABSOLUTEVOLUME = IBinder::FIRST_CALL_TRANSACTION + 27,
+ SETSPEAKERPHONEON = IBinder::FIRST_CALL_TRANSACTION + 28,
+ ISSPEAKERPHONEON = IBinder::FIRST_CALL_TRANSACTION + 29,
+ SETBLUETOOTHSCOON = IBinder::FIRST_CALL_TRANSACTION + 30,
+ ISBLUETOOTHSCOON = IBinder::FIRST_CALL_TRANSACTION + 31,
+ SETBLUETOOTHA2DPON = IBinder::FIRST_CALL_TRANSACTION + 32,
+ ISBLUETOOTHA2DPON = IBinder::FIRST_CALL_TRANSACTION + 33,
+ REQUESTAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 34,
+ ABANDONAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 35,
+ UNREGISTERAUDIOFOCUSCLIENT = IBinder::FIRST_CALL_TRANSACTION + 36,
+ GETCURRENTAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 37,
+ STARTBLUETOOTHSCO = IBinder::FIRST_CALL_TRANSACTION + 38,
+ STARTBLUETOOTHSCOVIRTUALCALL = IBinder::FIRST_CALL_TRANSACTION + 39,
+ STOPBLUETOOTHSCO = IBinder::FIRST_CALL_TRANSACTION + 40,
+ FORCEVOLUMECONTROLSTREAM = IBinder::FIRST_CALL_TRANSACTION + 41,
+ SETRINGTONEPLAYER = IBinder::FIRST_CALL_TRANSACTION + 42,
+ GETRINGTONEPLAYER = IBinder::FIRST_CALL_TRANSACTION + 43,
+ GETUISOUNDSSTREAMTYPE = IBinder::FIRST_CALL_TRANSACTION + 44,
+ SETWIREDDEVICECONNECTIONSTATE = IBinder::FIRST_CALL_TRANSACTION + 45,
+ SETBLUETOOTHA2DPDEVICECONNECTIONSTATE = IBinder::FIRST_CALL_TRANSACTION + 46,
+ HANDLEBLUETOOTHA2DPDEVICECONFIGCHANGE = IBinder::FIRST_CALL_TRANSACTION + 47,
+ STARTWATCHINGROUTES = IBinder::FIRST_CALL_TRANSACTION + 48,
+ ISCAMERASOUNDFORCED = IBinder::FIRST_CALL_TRANSACTION + 49,
+ SETVOLUMECONTROLLER = IBinder::FIRST_CALL_TRANSACTION + 50,
+ NOTIFYVOLUMECONTROLLERVISIBLE = IBinder::FIRST_CALL_TRANSACTION + 51,
+ ISSTREAMAFFECTEDBYRINGERMODE = IBinder::FIRST_CALL_TRANSACTION + 52,
+ ISSTREAMAFFECTEDBYMUTE = IBinder::FIRST_CALL_TRANSACTION + 53,
+ DISABLESAFEMEDIAVOLUME = IBinder::FIRST_CALL_TRANSACTION + 54,
+ SETHDMISYSTEMAUDIOSUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 55,
+ ISHDMISYSTEMAUDIOSUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 56,
+ REGISTERAUDIOPOLICY = IBinder::FIRST_CALL_TRANSACTION + 57,
+ UNREGISTERAUDIOPOLICYASYNC = IBinder::FIRST_CALL_TRANSACTION + 58,
+ SETFOCUSPROPERTIESFORPOLICY = IBinder::FIRST_CALL_TRANSACTION + 59,
+ SETVOLUMEPOLICY = IBinder::FIRST_CALL_TRANSACTION + 60,
+ REGISTERRECORDINGCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 61,
+ UNREGISTERRECORDINGCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 62,
+ GETACTIVERECORDINGCONFIGURATIONS = IBinder::FIRST_CALL_TRANSACTION + 63,
+ REGISTERPLAYBACKCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 64,
+ UNREGISTERPLAYBACKCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 65,
+ GETACTIVEPLAYBACKCONFIGURATIONS = IBinder::FIRST_CALL_TRANSACTION + 66,
+ */
+
+ TRACK_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 67,
+ PLAYER_ATTRIBUTES = IBinder::FIRST_CALL_TRANSACTION + 68,
+ 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(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;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOMANAGER_H
diff --git a/include/audiomanager/IPlayer.h b/include/audiomanager/IPlayer.h
new file mode 100644
index 0000000..de5c1c7
--- /dev/null
+++ b/include/audiomanager/IPlayer.h
@@ -0,0 +1,69 @@
+/*
+ * 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 <media/VolumeShaper.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;
+
+ virtual void setPan(float pan) = 0;
+
+ virtual void setStartDelayMs(int delayMs) = 0;
+
+ virtual void applyVolumeShaper(
+ const sp<VolumeShaper::Configuration>& configuration,
+ const sp<VolumeShaper::Operation>& operation) = 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/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h
index b399905..80ab7f3 100644
--- a/include/batteryservice/BatteryService.h
+++ b/include/batteryservice/BatteryService.h
@@ -24,33 +24,17 @@
namespace android {
-// must be kept in sync with definitions in BatteryManager.java
-enum {
- BATTERY_STATUS_UNKNOWN = 1, // equals BatteryManager.BATTERY_STATUS_UNKNOWN constant
- BATTERY_STATUS_CHARGING = 2, // equals BatteryManager.BATTERY_STATUS_CHARGING constant
- BATTERY_STATUS_DISCHARGING = 3, // equals BatteryManager.BATTERY_STATUS_DISCHARGING constant
- BATTERY_STATUS_NOT_CHARGING = 4, // equals BatteryManager.BATTERY_STATUS_NOT_CHARGING constant
- BATTERY_STATUS_FULL = 5, // equals BatteryManager.BATTERY_STATUS_FULL constant
-};
+#include "BatteryServiceConstants.h"
-// must be kept in sync with definitions in BatteryManager.java
+// must be kept in sync with definitions in
+// frameworks/base/core/java/android/os/BatteryManager.java
enum {
- BATTERY_HEALTH_UNKNOWN = 1, // equals BatteryManager.BATTERY_HEALTH_UNKNOWN constant
- BATTERY_HEALTH_GOOD = 2, // equals BatteryManager.BATTERY_HEALTH_GOOD constant
- BATTERY_HEALTH_OVERHEAT = 3, // equals BatteryManager.BATTERY_HEALTH_OVERHEAT constant
- BATTERY_HEALTH_DEAD = 4, // equals BatteryManager.BATTERY_HEALTH_DEAD constant
- BATTERY_HEALTH_OVER_VOLTAGE = 5, // equals BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE constant
- BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6, // equals BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE constant
- BATTERY_HEALTH_COLD = 7, // equals BatteryManager.BATTERY_HEALTH_COLD constant
-};
-
-// must be kept in sync with definitions in BatteryProperty.java
-enum {
- BATTERY_PROP_CHARGE_COUNTER = 1, // equals BatteryProperty.CHARGE_COUNTER constant
- BATTERY_PROP_CURRENT_NOW = 2, // equals BatteryProperty.CURRENT_NOW constant
- BATTERY_PROP_CURRENT_AVG = 3, // equals BatteryProperty.CURRENT_AVG constant
- BATTERY_PROP_CAPACITY = 4, // equals BatteryProperty.CAPACITY constant
- BATTERY_PROP_ENERGY_COUNTER = 5, // equals BatteryProperty.ENERGY_COUNTER constant
+ BATTERY_PROP_CHARGE_COUNTER = 1, // equals BATTERY_PROPERTY_CHARGE_COUNTER
+ BATTERY_PROP_CURRENT_NOW = 2, // equals BATTERY_PROPERTY_CURRENT_NOW
+ BATTERY_PROP_CURRENT_AVG = 3, // equals BATTERY_PROPERTY_CURRENT_AVERAGE
+ BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY
+ BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER
+ BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS
};
struct BatteryProperties {
diff --git a/include/batteryservice/BatteryServiceConstants.h b/include/batteryservice/BatteryServiceConstants.h
new file mode 100644
index 0000000..8a90a12
--- /dev/null
+++ b/include/batteryservice/BatteryServiceConstants.h
@@ -0,0 +1,32 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ BATTERY_STATUS_UNKNOWN = 1,
+ BATTERY_STATUS_CHARGING = 2,
+ BATTERY_STATUS_DISCHARGING = 3,
+ BATTERY_STATUS_NOT_CHARGING = 4,
+ BATTERY_STATUS_FULL = 5,
+};
+
+enum {
+ BATTERY_HEALTH_UNKNOWN = 1,
+ BATTERY_HEALTH_GOOD = 2,
+ BATTERY_HEALTH_OVERHEAT = 3,
+ BATTERY_HEALTH_DEAD = 4,
+ BATTERY_HEALTH_OVER_VOLTAGE = 5,
+ BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6,
+ BATTERY_HEALTH_COLD = 7,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/include/batteryservice/IBatteryPropertiesListener.h b/include/batteryservice/IBatteryPropertiesListener.h
index b02d8e9..b226dd6 100644
--- a/include/batteryservice/IBatteryPropertiesListener.h
+++ b/include/batteryservice/IBatteryPropertiesListener.h
@@ -33,13 +33,19 @@
class IBatteryPropertiesListener : public IInterface {
public:
- DECLARE_META_INTERFACE(BatteryPropertiesListener);
+ DECLARE_META_INTERFACE(BatteryPropertiesListener)
virtual void batteryPropertiesChanged(struct BatteryProperties props) = 0;
};
// ----------------------------------------------------------------------------
+class BnBatteryPropertiesListener: public BnInterface<IBatteryPropertiesListener> {
+public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0);
+};
+
}; // namespace android
#endif // ANDROID_IBATTERYPROPERTIESLISTENER_H
diff --git a/include/batteryservice/IBatteryPropertiesRegistrar.h b/include/batteryservice/IBatteryPropertiesRegistrar.h
index eca075d..a7dbea6 100644
--- a/include/batteryservice/IBatteryPropertiesRegistrar.h
+++ b/include/batteryservice/IBatteryPropertiesRegistrar.h
@@ -27,15 +27,17 @@
REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
UNREGISTER_LISTENER,
GET_PROPERTY,
+ SCHEDULE_UPDATE,
};
class IBatteryPropertiesRegistrar : public IInterface {
public:
- DECLARE_META_INTERFACE(BatteryPropertiesRegistrar);
+ DECLARE_META_INTERFACE(BatteryPropertiesRegistrar)
virtual void registerListener(const sp<IBatteryPropertiesListener>& listener) = 0;
virtual void unregisterListener(const sp<IBatteryPropertiesListener>& listener) = 0;
virtual status_t getProperty(int id, struct BatteryProperty *val) = 0;
+ virtual void scheduleUpdate() = 0;
};
class BnBatteryPropertiesRegistrar : public BnInterface<IBatteryPropertiesRegistrar> {
diff --git a/include/binder b/include/binder
new file mode 120000
index 0000000..35a022a
--- /dev/null
+++ b/include/binder
@@ -0,0 +1 @@
+../libs/binder/include/binder/
\ No newline at end of file
diff --git a/include/binder/AppOpsManager.h b/include/binder/AppOpsManager.h
deleted file mode 100644
index 042927c..0000000
--- a/include/binder/AppOpsManager.h
+++ /dev/null
@@ -1,118 +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.
- */
-
-#ifndef ANDROID_APP_OPS_MANAGER_H
-#define ANDROID_APP_OPS_MANAGER_H
-
-#include <binder/IAppOpsService.h>
-
-#include <utils/threads.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class AppOpsManager
-{
-public:
- enum {
- MODE_ALLOWED = IAppOpsService::MODE_ALLOWED,
- MODE_IGNORED = IAppOpsService::MODE_IGNORED,
- MODE_ERRORED = IAppOpsService::MODE_ERRORED
- };
-
- enum {
- OP_NONE = -1,
- OP_COARSE_LOCATION = 0,
- OP_FINE_LOCATION = 1,
- OP_GPS = 2,
- OP_VIBRATE = 3,
- OP_READ_CONTACTS = 4,
- OP_WRITE_CONTACTS = 5,
- OP_READ_CALL_LOG = 6,
- OP_WRITE_CALL_LOG = 7,
- OP_READ_CALENDAR = 8,
- OP_WRITE_CALENDAR = 9,
- OP_WIFI_SCAN = 10,
- OP_POST_NOTIFICATION = 11,
- OP_NEIGHBORING_CELLS = 12,
- OP_CALL_PHONE = 13,
- OP_READ_SMS = 14,
- OP_WRITE_SMS = 15,
- OP_RECEIVE_SMS = 16,
- OP_RECEIVE_EMERGECY_SMS = 17,
- OP_RECEIVE_MMS = 18,
- OP_RECEIVE_WAP_PUSH = 19,
- OP_SEND_SMS = 20,
- OP_READ_ICC_SMS = 21,
- OP_WRITE_ICC_SMS = 22,
- OP_WRITE_SETTINGS = 23,
- OP_SYSTEM_ALERT_WINDOW = 24,
- OP_ACCESS_NOTIFICATIONS = 25,
- OP_CAMERA = 26,
- OP_RECORD_AUDIO = 27,
- OP_PLAY_AUDIO = 28,
- OP_READ_CLIPBOARD = 29,
- OP_WRITE_CLIPBOARD = 30,
- OP_TAKE_MEDIA_BUTTONS = 31,
- OP_TAKE_AUDIO_FOCUS = 32,
- OP_AUDIO_MASTER_VOLUME = 33,
- OP_AUDIO_VOICE_VOLUME = 34,
- OP_AUDIO_RING_VOLUME = 35,
- OP_AUDIO_MEDIA_VOLUME = 36,
- OP_AUDIO_ALARM_VOLUME = 37,
- OP_AUDIO_NOTIFICATION_VOLUME = 38,
- OP_AUDIO_BLUETOOTH_VOLUME = 39,
- OP_WAKE_LOCK = 40,
- OP_MONITOR_LOCATION = 41,
- OP_MONITOR_HIGH_POWER_LOCATION = 42,
- OP_GET_USAGE_STATS = 43,
- OP_MUTE_MICROPHONE = 44,
- OP_TOAST_WINDOW = 45,
- OP_PROJECT_MEDIA = 46,
- OP_ACTIVATE_VPN = 47,
- OP_WRITE_WALLPAPER = 48,
- OP_ASSIST_STRUCTURE = 49,
- OP_ASSIST_SCREENSHOT = 50,
- OP_READ_PHONE_STATE = 51,
- OP_ADD_VOICEMAIL = 52,
- OP_USE_SIP = 53,
- OP_PROCESS_OUTGOING_CALLS = 54,
- OP_USE_FINGERPRINT = 55,
- OP_BODY_SENSORS = 56
- };
-
- AppOpsManager();
-
- int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
- int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
- int32_t startOp(int32_t op, int32_t uid, const String16& callingPackage);
- void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
- void startWatchingMode(int32_t op, const String16& packageName,
- const sp<IAppOpsCallback>& callback);
- void stopWatchingMode(const sp<IAppOpsCallback>& callback);
- int32_t permissionToOpCode(const String16& permission);
-
-private:
- Mutex mLock;
- sp<IAppOpsService> mService;
-
- sp<IAppOpsService> getService();
-};
-
-
-}; // namespace android
-// ---------------------------------------------------------------------------
-#endif // ANDROID_APP_OPS_MANAGER_H
diff --git a/include/binder/Binder.h b/include/binder/Binder.h
deleted file mode 100644
index f849fd4..0000000
--- a/include/binder/Binder.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2008 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_BINDER_H
-#define ANDROID_BINDER_H
-
-#include <atomic>
-#include <stdint.h>
-#include <binder/IBinder.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class BBinder : public IBinder
-{
-public:
- BBinder();
-
- virtual const String16& getInterfaceDescriptor() const;
- virtual bool isBinderAlive() const;
- virtual status_t pingBinder();
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- virtual status_t transact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-
- virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0);
-
- virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0,
- wp<DeathRecipient>* outRecipient = NULL);
-
- virtual void attachObject( const void* objectID,
- void* object,
- void* cleanupCookie,
- object_cleanup_func func);
- virtual void* findObject(const void* objectID) const;
- virtual void detachObject(const void* objectID);
-
- virtual BBinder* localBinder();
-
-protected:
- virtual ~BBinder();
-
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-
-private:
- BBinder(const BBinder& o);
- BBinder& operator=(const BBinder& o);
-
- class Extras;
-
- std::atomic<Extras*> mExtras;
- void* mReserved0;
-};
-
-// ---------------------------------------------------------------------------
-
-class BpRefBase : public virtual RefBase
-{
-protected:
- BpRefBase(const sp<IBinder>& o);
- virtual ~BpRefBase();
- virtual void onFirstRef();
- virtual void onLastStrongRef(const void* id);
- virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
-
- inline IBinder* remote() { return mRemote; }
- inline IBinder* remote() const { return mRemote; }
-
-private:
- BpRefBase(const BpRefBase& o);
- BpRefBase& operator=(const BpRefBase& o);
-
- IBinder* const mRemote;
- RefBase::weakref_type* mRefs;
- std::atomic<int32_t> mState;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_BINDER_H
diff --git a/include/binder/IAppOpsCallback.h b/include/binder/IAppOpsCallback.h
deleted file mode 100644
index 7f8eb01..0000000
--- a/include/binder/IAppOpsCallback.h
+++ /dev/null
@@ -1,55 +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.
- */
-
-//
-#ifndef ANDROID_IAPP_OPS_CALLBACK_H
-#define ANDROID_IAPP_OPS_CALLBACK_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IAppOpsCallback : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(AppOpsCallback);
-
- virtual void opChanged(int32_t op, const String16& packageName) = 0;
-
- enum {
- OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnAppOpsCallback : public BnInterface<IAppOpsCallback>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAPP_OPS_CALLBACK_H
-
diff --git a/include/binder/IAppOpsService.h b/include/binder/IAppOpsService.h
deleted file mode 100644
index cd81efa..0000000
--- a/include/binder/IAppOpsService.h
+++ /dev/null
@@ -1,78 +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.
- */
-
-//
-#ifndef ANDROID_IAPP_OPS_SERVICE_H
-#define ANDROID_IAPP_OPS_SERVICE_H
-
-#include <binder/IAppOpsCallback.h>
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IAppOpsService : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(AppOpsService);
-
- virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
- virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
- virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName) = 0;
- virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName) = 0;
- virtual void startWatchingMode(int32_t op, const String16& packageName,
- const sp<IAppOpsCallback>& callback) = 0;
- virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
- virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
- virtual int32_t permissionToOpCode(const String16& permission) = 0;
-
- enum {
- CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1,
- START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2,
- FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
- START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
- STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
- GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
- PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
- };
-
- enum {
- MODE_ALLOWED = 0,
- MODE_IGNORED = 1,
- MODE_ERRORED = 2
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnAppOpsService : public BnInterface<IAppOpsService>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/include/binder/IBatteryStats.h b/include/binder/IBatteryStats.h
deleted file mode 100644
index 5f38186..0000000
--- a/include/binder/IBatteryStats.h
+++ /dev/null
@@ -1,79 +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.
- */
-
-#ifndef ANDROID_IBATTERYSTATS_H
-#define ANDROID_IBATTERYSTATS_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IBatteryStats : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(BatteryStats);
-
- virtual void noteStartSensor(int uid, int sensor) = 0;
- virtual void noteStopSensor(int uid, int sensor) = 0;
- virtual void noteStartVideo(int uid) = 0;
- virtual void noteStopVideo(int uid) = 0;
- virtual void noteStartAudio(int uid) = 0;
- virtual void noteStopAudio(int uid) = 0;
- virtual void noteResetVideo() = 0;
- virtual void noteResetAudio() = 0;
- virtual void noteFlashlightOn(int uid) = 0;
- virtual void noteFlashlightOff(int uid) = 0;
- virtual void noteStartCamera(int uid) = 0;
- virtual void noteStopCamera(int uid) = 0;
- virtual void noteResetCamera() = 0;
- virtual void noteResetFlashlight() = 0;
-
- enum {
- NOTE_START_SENSOR_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- NOTE_STOP_SENSOR_TRANSACTION,
- NOTE_START_VIDEO_TRANSACTION,
- NOTE_STOP_VIDEO_TRANSACTION,
- NOTE_START_AUDIO_TRANSACTION,
- NOTE_STOP_AUDIO_TRANSACTION,
- NOTE_RESET_VIDEO_TRANSACTION,
- NOTE_RESET_AUDIO_TRANSACTION,
- NOTE_FLASHLIGHT_ON_TRANSACTION,
- NOTE_FLASHLIGHT_OFF_TRANSACTION,
- NOTE_START_CAMERA_TRANSACTION,
- NOTE_STOP_CAMERA_TRANSACTION,
- NOTE_RESET_CAMERA_TRANSACTION,
- NOTE_RESET_FLASHLIGHT_TRANSACTION
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnBatteryStats : public BnInterface<IBatteryStats>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IBATTERYSTATS_H
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
deleted file mode 100644
index 5f1e87c..0000000
--- a/include/binder/IBinder.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2008 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_IBINDER_H
-#define ANDROID_IBINDER_H
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
-
-// linux/binder.h already defines this, but we can't just include it from there
-// because there are host builds that include this file.
-#ifndef B_PACK_CHARS
-#define B_PACK_CHARS(c1, c2, c3, c4) \
- ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
-#endif // B_PACK_CHARS
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class BBinder;
-class BpBinder;
-class IInterface;
-class Parcel;
-class IResultReceiver;
-
-/**
- * Base class and low-level protocol for a remotable object.
- * You can derive from this class to create an object for which other
- * processes can hold references to it. Communication between processes
- * (method calls, property get and set) is down through a low-level
- * protocol implemented on top of the transact() API.
- */
-class IBinder : public virtual RefBase
-{
-public:
- enum {
- FIRST_CALL_TRANSACTION = 0x00000001,
- LAST_CALL_TRANSACTION = 0x00ffffff,
-
- PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'),
- DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'),
- SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
- INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
- SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
-
- // Corresponds to TF_ONE_WAY -- an asynchronous call.
- FLAG_ONEWAY = 0x00000001
- };
-
- IBinder();
-
- /**
- * Check if this IBinder implements the interface named by
- * @a descriptor. If it does, the base pointer to it is returned,
- * which you can safely static_cast<> to the concrete C++ interface.
- */
- virtual sp<IInterface> queryLocalInterface(const String16& descriptor);
-
- /**
- * Return the canonical name of the interface provided by this IBinder
- * object.
- */
- virtual const String16& getInterfaceDescriptor() const = 0;
-
- virtual bool isBinderAlive() const = 0;
- virtual status_t pingBinder() = 0;
- virtual status_t dump(int fd, const Vector<String16>& args) = 0;
- static status_t shellCommand(const sp<IBinder>& target, int in, int out, int err,
- Vector<String16>& args,
- const sp<IResultReceiver>& resultReceiver);
-
- virtual status_t transact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0) = 0;
-
- class DeathRecipient : public virtual RefBase
- {
- public:
- virtual void binderDied(const wp<IBinder>& who) = 0;
- };
-
- /**
- * Register the @a recipient for a notification if this binder
- * goes away. If this binder object unexpectedly goes away
- * (typically because its hosting process has been killed),
- * then DeathRecipient::binderDied() will be called with a reference
- * to this.
- *
- * The @a cookie is optional -- if non-NULL, it should be a
- * memory address that you own (that is, you know it is unique).
- *
- * @note You will only receive death notifications for remote binders,
- * as local binders by definition can't die without you dying as well.
- * Trying to use this function on a local binder will result in an
- * INVALID_OPERATION code being returned and nothing happening.
- *
- * @note This link always holds a weak reference to its recipient.
- *
- * @note You will only receive a weak reference to the dead
- * binder. You should not try to promote this to a strong reference.
- * (Nor should you need to, as there is nothing useful you can
- * directly do with it now that it has passed on.)
- */
- virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0) = 0;
-
- /**
- * Remove a previously registered death notification.
- * The @a recipient will no longer be called if this object
- * dies. The @a cookie is optional. If non-NULL, you can
- * supply a NULL @a recipient, and the recipient previously
- * added with that cookie will be unlinked.
- */
- virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0,
- wp<DeathRecipient>* outRecipient = NULL) = 0;
-
- virtual bool checkSubclass(const void* subclassID) const;
-
- typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
-
- virtual void attachObject( const void* objectID,
- void* object,
- void* cleanupCookie,
- object_cleanup_func func) = 0;
- virtual void* findObject(const void* objectID) const = 0;
- virtual void detachObject(const void* objectID) = 0;
-
- virtual BBinder* localBinder();
- virtual BpBinder* remoteBinder();
-
-protected:
- virtual ~IBinder();
-
-private:
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_IBINDER_H
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
deleted file mode 100644
index 4ce3613..0000000
--- a/include/binder/IInterface.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2005 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_IINTERFACE_H
-#define ANDROID_IINTERFACE_H
-
-#include <binder/Binder.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IInterface : public virtual RefBase
-{
-public:
- IInterface();
- static sp<IBinder> asBinder(const IInterface*);
- static sp<IBinder> asBinder(const sp<IInterface>&);
-
-protected:
- virtual ~IInterface();
- virtual IBinder* onAsBinder() = 0;
-};
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
-{
- return INTERFACE::asInterface(obj);
-}
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-class BnInterface : public INTERFACE, public BBinder
-{
-public:
- virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
- virtual const String16& getInterfaceDescriptor() const;
-
-protected:
- virtual IBinder* onAsBinder();
-};
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-class BpInterface : public INTERFACE, public BpRefBase
-{
-public:
- BpInterface(const sp<IBinder>& remote);
-
-protected:
- virtual IBinder* onAsBinder();
-};
-
-// ----------------------------------------------------------------------
-
-#define DECLARE_META_INTERFACE(INTERFACE) \
- static const android::String16 descriptor; \
- static android::sp<I##INTERFACE> asInterface( \
- const android::sp<android::IBinder>& obj); \
- virtual const android::String16& getInterfaceDescriptor() const; \
- I##INTERFACE(); \
- virtual ~I##INTERFACE(); \
-
-
-#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
- const android::String16 I##INTERFACE::descriptor(NAME); \
- const android::String16& \
- I##INTERFACE::getInterfaceDescriptor() const { \
- return I##INTERFACE::descriptor; \
- } \
- android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
- const android::sp<android::IBinder>& obj) \
- { \
- android::sp<I##INTERFACE> intr; \
- if (obj != NULL) { \
- intr = static_cast<I##INTERFACE*>( \
- obj->queryLocalInterface( \
- I##INTERFACE::descriptor).get()); \
- if (intr == NULL) { \
- intr = new Bp##INTERFACE(obj); \
- } \
- } \
- return intr; \
- } \
- I##INTERFACE::I##INTERFACE() { } \
- I##INTERFACE::~I##INTERFACE() { } \
-
-
-#define CHECK_INTERFACE(interface, data, reply) \
- if (!data.checkInterface(this)) { return PERMISSION_DENIED; } \
-
-
-// ----------------------------------------------------------------------
-// No user-serviceable parts after this...
-
-template<typename INTERFACE>
-inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
- const String16& _descriptor)
-{
- if (_descriptor == INTERFACE::descriptor) return this;
- return NULL;
-}
-
-template<typename INTERFACE>
-inline const String16& BnInterface<INTERFACE>::getInterfaceDescriptor() const
-{
- return INTERFACE::getInterfaceDescriptor();
-}
-
-template<typename INTERFACE>
-IBinder* BnInterface<INTERFACE>::onAsBinder()
-{
- return this;
-}
-
-template<typename INTERFACE>
-inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
- : BpRefBase(remote)
-{
-}
-
-template<typename INTERFACE>
-inline IBinder* BpInterface<INTERFACE>::onAsBinder()
-{
- return remote();
-}
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IINTERFACE_H
diff --git a/include/binder/IMediaResourceMonitor.h b/include/binder/IMediaResourceMonitor.h
deleted file mode 100644
index c671f7a..0000000
--- a/include/binder/IMediaResourceMonitor.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_I_MEDIA_RESOURCE_MONITOR_H
-#define ANDROID_I_MEDIA_RESOURCE_MONITOR_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IMediaResourceMonitor : public IInterface {
-public:
- DECLARE_META_INTERFACE(MediaResourceMonitor);
-
- // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX.
- enum {
- TYPE_VIDEO_CODEC = 0,
- TYPE_AUDIO_CODEC = 1,
- };
-
- virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0;
-
- enum {
- NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION,
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
-public:
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_I_MEDIA_RESOURCE_MONITOR_H
diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h
deleted file mode 100644
index 2d0db00..0000000
--- a/include/binder/IMemory.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2007 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_IMEMORY_H
-#define ANDROID_IMEMORY_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IMemoryHeap : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(MemoryHeap);
-
- // flags returned by getFlags()
- enum {
- READ_ONLY = 0x00000001
- };
-
- virtual int getHeapID() const = 0;
- virtual void* getBase() const = 0;
- virtual size_t getSize() const = 0;
- virtual uint32_t getFlags() const = 0;
- virtual uint32_t getOffset() const = 0;
-
- // these are there just for backward source compatibility
- int32_t heapID() const { return getHeapID(); }
- void* base() const { return getBase(); }
- size_t virtualSize() const { return getSize(); }
-};
-
-class BnMemoryHeap : public BnInterface<IMemoryHeap>
-{
-public:
- virtual status_t onTransact(
- uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-
- BnMemoryHeap();
-protected:
- virtual ~BnMemoryHeap();
-};
-
-// ----------------------------------------------------------------------------
-
-class IMemory : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(Memory);
-
- virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
-
- // helpers
- void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
- void* pointer() const;
- size_t size() const;
- ssize_t offset() const;
-};
-
-class BnMemory : public BnInterface<IMemory>
-{
-public:
- virtual status_t onTransact(
- uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-
- BnMemory();
-protected:
- virtual ~BnMemory();
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IMEMORY_H
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
deleted file mode 100644
index 7b826d6..0000000
--- a/include/binder/IPCThreadState.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2005 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_IPC_THREAD_STATE_H
-#define ANDROID_IPC_THREAD_STATE_H
-
-#include <utils/Errors.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-#include <utils/Vector.h>
-
-#if defined(_WIN32)
-typedef int uid_t;
-#endif
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class IPCThreadState
-{
-public:
- static IPCThreadState* self();
- static IPCThreadState* selfOrNull(); // self(), but won't instantiate
-
- sp<ProcessState> process();
-
- status_t clearLastError();
-
- pid_t getCallingPid() const;
- uid_t getCallingUid() const;
-
- void setStrictModePolicy(int32_t policy);
- int32_t getStrictModePolicy() const;
-
- void setLastTransactionBinderFlags(int32_t flags);
- int32_t getLastTransactionBinderFlags() const;
-
- int64_t clearCallingIdentity();
- void restoreCallingIdentity(int64_t token);
-
- int setupPolling(int* fd);
- status_t handlePolledCommands();
- void flushCommands();
-
- void joinThreadPool(bool isMain = true);
-
- // Stop the local process.
- void stopProcess(bool immediate = true);
-
- status_t transact(int32_t handle,
- uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
-
- void incStrongHandle(int32_t handle);
- void decStrongHandle(int32_t handle);
- void incWeakHandle(int32_t handle);
- void decWeakHandle(int32_t handle);
- status_t attemptIncStrongHandle(int32_t handle);
- static void expungeHandle(int32_t handle, IBinder* binder);
- status_t requestDeathNotification( int32_t handle,
- BpBinder* proxy);
- status_t clearDeathNotification( int32_t handle,
- BpBinder* proxy);
-
- static void shutdown();
-
- // Call this to disable switching threads to background scheduling when
- // receiving incoming IPC calls. This is specifically here for the
- // Android system process, since it expects to have background apps calling
- // in to it but doesn't want to acquire locks in its services while in
- // the background.
- static void disableBackgroundScheduling(bool disable);
-
- // Call blocks until the number of executing binder threads is less than
- // the maximum number of binder threads threads allowed for this process.
- void blockUntilThreadAvailable();
-
-private:
- IPCThreadState();
- ~IPCThreadState();
-
- status_t sendReply(const Parcel& reply, uint32_t flags);
- status_t waitForResponse(Parcel *reply,
- status_t *acquireResult=NULL);
- status_t talkWithDriver(bool doReceive=true);
- status_t writeTransactionData(int32_t cmd,
- uint32_t binderFlags,
- int32_t handle,
- uint32_t code,
- const Parcel& data,
- status_t* statusBuffer);
- status_t getAndExecuteCommand();
- status_t executeCommand(int32_t command);
- void processPendingDerefs();
-
- void clearCaller();
-
- static void threadDestructor(void *st);
- static void freeBuffer(Parcel* parcel,
- const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsSize,
- void* cookie);
-
- const sp<ProcessState> mProcess;
- Vector<BBinder*> mPendingStrongDerefs;
- Vector<RefBase::weakref_type*> mPendingWeakDerefs;
-
- Parcel mIn;
- Parcel mOut;
- status_t mLastError;
- pid_t mCallingPid;
- uid_t mCallingUid;
- int32_t mStrictModePolicy;
- int32_t mLastTransactionBinderFlags;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/include/binder/IPermissionController.h b/include/binder/IPermissionController.h
deleted file mode 100644
index 4e5fb34..0000000
--- a/include/binder/IPermissionController.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2005 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_IPERMISSION_CONTROLLER_H
-#define ANDROID_IPERMISSION_CONTROLLER_H
-
-#include <binder/IInterface.h>
-#include <stdlib.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IPermissionController : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(PermissionController);
-
- virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) = 0;
-
- virtual void getPackagesForUid(const uid_t uid, Vector<String16> &packages) = 0;
-
- virtual bool isRuntimePermission(const String16& permission) = 0;
-
- enum {
- CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- GET_PACKAGES_FOR_UID_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1,
- IS_RUNTIME_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 2
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnPermissionController : public BnInterface<IPermissionController>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPERMISSION_CONTROLLER_H
-
diff --git a/include/binder/IProcessInfoService.h b/include/binder/IProcessInfoService.h
deleted file mode 100644
index 69dc9a7..0000000
--- a/include/binder/IProcessInfoService.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2015 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_I_PROCESS_INFO_SERVICE_H
-#define ANDROID_I_PROCESS_INFO_SERVICE_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IProcessInfoService : public IInterface {
-public:
- DECLARE_META_INTERFACE(ProcessInfoService);
-
- virtual status_t getProcessStatesFromPids( size_t length,
- /*in*/ int32_t* pids,
- /*out*/ int32_t* states) = 0;
-
- virtual status_t getProcessStatesAndOomScoresFromPids( size_t length,
- /*in*/ int32_t* pids,
- /*out*/ int32_t* states,
- /*out*/ int32_t* scores) = 0;
-
- enum {
- GET_PROCESS_STATES_FROM_PIDS = IBinder::FIRST_CALL_TRANSACTION,
- GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS,
- };
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/include/binder/IResultReceiver.h b/include/binder/IResultReceiver.h
deleted file mode 100644
index 02dc6a6..0000000
--- a/include/binder/IResultReceiver.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 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_IRESULT_RECEIVER_H
-#define ANDROID_IRESULT_RECEIVER_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IResultReceiver : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(ResultReceiver);
-
- virtual void send(int32_t resultCode) = 0;
-
- enum {
- OP_SEND = IBinder::FIRST_CALL_TRANSACTION
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnResultReceiver : public BnInterface<IResultReceiver>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IRESULT_RECEIVER_H
-
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
deleted file mode 100644
index 7ccd9fe..0000000
--- a/include/binder/IServiceManager.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2005 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_ISERVICE_MANAGER_H
-#define ANDROID_ISERVICE_MANAGER_H
-
-#include <binder/IInterface.h>
-#include <binder/IPermissionController.h>
-#include <utils/Vector.h>
-#include <utils/String16.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IServiceManager : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(ServiceManager);
-
- /**
- * Retrieve an existing service, blocking for a few seconds
- * if it doesn't yet exist.
- */
- virtual sp<IBinder> getService( const String16& name) const = 0;
-
- /**
- * Retrieve an existing service, non-blocking.
- */
- virtual sp<IBinder> checkService( const String16& name) const = 0;
-
- /**
- * Register a service.
- */
- virtual status_t addService( const String16& name,
- const sp<IBinder>& service,
- bool allowIsolated = false) = 0;
-
- /**
- * Return list of all existing services.
- */
- virtual Vector<String16> listServices() = 0;
-
- enum {
- GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- CHECK_SERVICE_TRANSACTION,
- ADD_SERVICE_TRANSACTION,
- LIST_SERVICES_TRANSACTION,
- };
-};
-
-sp<IServiceManager> defaultServiceManager();
-
-template<typename INTERFACE>
-status_t getService(const String16& name, sp<INTERFACE>* outService)
-{
- const sp<IServiceManager> sm = defaultServiceManager();
- if (sm != NULL) {
- *outService = interface_cast<INTERFACE>(sm->getService(name));
- if ((*outService) != NULL) return NO_ERROR;
- }
- return NAME_NOT_FOUND;
-}
-
-bool checkCallingPermission(const String16& permission);
-bool checkCallingPermission(const String16& permission,
- int32_t* outPid, int32_t* outUid);
-bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
-
-}; // namespace android
-
-#endif // ANDROID_ISERVICE_MANAGER_H
-
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
deleted file mode 100644
index 2490b82..0000000
--- a/include/binder/Parcel.h
+++ /dev/null
@@ -1,798 +0,0 @@
-/*
- * Copyright (C) 2005 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_PARCEL_H
-#define ANDROID_PARCEL_H
-
-#include <string>
-#include <vector>
-
-#include <cutils/native_handle.h>
-#include <nativehelper/ScopedFd.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-#include <utils/Flattenable.h>
-#include <linux/binder.h>
-
-#include <binder/IInterface.h>
-#include <binder/Parcelable.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-template <typename T> class Flattenable;
-template <typename T> class LightFlattenable;
-class IBinder;
-class IPCThreadState;
-class ProcessState;
-class String8;
-class TextOutput;
-
-class Parcel {
- friend class IPCThreadState;
-public:
- class ReadableBlob;
- class WritableBlob;
-
- Parcel();
- ~Parcel();
-
- const uint8_t* data() const;
- size_t dataSize() const;
- size_t dataAvail() const;
- size_t dataPosition() const;
- size_t dataCapacity() const;
-
- status_t setDataSize(size_t size);
- void setDataPosition(size_t pos) const;
- status_t setDataCapacity(size_t size);
-
- status_t setData(const uint8_t* buffer, size_t len);
-
- status_t appendFrom(const Parcel *parcel,
- size_t start, size_t len);
-
- bool allowFds() const;
- bool pushAllowFds(bool allowFds);
- void restoreAllowFds(bool lastValue);
-
- bool hasFileDescriptors() const;
-
- // Writes the RPC header.
- status_t writeInterfaceToken(const String16& interface);
-
- // Parses the RPC header, returning true if the interface name
- // in the header matches the expected interface from the caller.
- //
- // Additionally, enforceInterface does part of the work of
- // propagating the StrictMode policy mask, populating the current
- // IPCThreadState, which as an optimization may optionally be
- // passed in.
- bool enforceInterface(const String16& interface,
- IPCThreadState* threadState = NULL) const;
- bool checkInterface(IBinder*) const;
-
- void freeData();
-
-private:
- const binder_size_t* objects() const;
-
-public:
- size_t objectsCount() const;
-
- status_t errorCheck() const;
- void setError(status_t err);
-
- status_t write(const void* data, size_t len);
- void* writeInplace(size_t len);
- status_t writeUnpadded(const void* data, size_t len);
- status_t writeInt32(int32_t val);
- status_t writeUint32(uint32_t val);
- status_t writeInt64(int64_t val);
- status_t writeUint64(uint64_t val);
- status_t writeFloat(float val);
- status_t writeDouble(double val);
- status_t writeCString(const char* str);
- status_t writeString8(const String8& str);
- status_t writeString16(const String16& str);
- status_t writeString16(const std::unique_ptr<String16>& str);
- status_t writeString16(const char16_t* str, size_t len);
- status_t writeStrongBinder(const sp<IBinder>& val);
- status_t writeWeakBinder(const wp<IBinder>& val);
- status_t writeInt32Array(size_t len, const int32_t *val);
- status_t writeByteArray(size_t len, const uint8_t *val);
- status_t writeBool(bool val);
- status_t writeChar(char16_t val);
- status_t writeByte(int8_t val);
-
- // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
- status_t writeUtf8AsUtf16(const std::string& str);
- status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
-
- status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
- status_t writeByteVector(const std::vector<int8_t>& val);
- status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
- status_t writeByteVector(const std::vector<uint8_t>& val);
- status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
- status_t writeInt32Vector(const std::vector<int32_t>& val);
- status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
- status_t writeInt64Vector(const std::vector<int64_t>& val);
- status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
- status_t writeFloatVector(const std::vector<float>& val);
- status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
- status_t writeDoubleVector(const std::vector<double>& val);
- status_t writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
- status_t writeBoolVector(const std::vector<bool>& val);
- status_t writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
- status_t writeCharVector(const std::vector<char16_t>& val);
- status_t writeString16Vector(
- const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
- status_t writeString16Vector(const std::vector<String16>& val);
- status_t writeUtf8VectorAsUtf16Vector(
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
- status_t writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
-
- status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
- status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
-
- template<typename T>
- status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
- template<typename T>
- status_t writeParcelableVector(const std::vector<T>& val);
-
- template<typename T>
- status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable);
-
- status_t writeParcelable(const Parcelable& parcelable);
-
- template<typename T>
- status_t write(const Flattenable<T>& val);
-
- template<typename T>
- status_t write(const LightFlattenable<T>& val);
-
-
- // Place a native_handle into the parcel (the native_handle's file-
- // descriptors are dup'ed, so it is safe to delete the native_handle
- // when this function returns).
- // Doesn't take ownership of the native_handle.
- status_t writeNativeHandle(const native_handle* handle);
-
- // Place a file descriptor into the parcel. The given fd must remain
- // valid for the lifetime of the parcel.
- // The Parcel does not take ownership of the given fd unless you ask it to.
- status_t writeFileDescriptor(int fd, bool takeOwnership = false);
-
- // Place a file descriptor into the parcel. A dup of the fd is made, which
- // will be closed once the parcel is destroyed.
- status_t writeDupFileDescriptor(int fd);
-
- // Place a file descriptor into the parcel. This will not affect the
- // semantics of the smart file descriptor. A new descriptor will be
- // created, and will be closed when the parcel is destroyed.
- status_t writeUniqueFileDescriptor(
- const ScopedFd& fd);
-
- // Place a vector of file desciptors into the parcel. Each descriptor is
- // dup'd as in writeDupFileDescriptor
- status_t writeUniqueFileDescriptorVector(
- const std::unique_ptr<std::vector<ScopedFd>>& val);
- status_t writeUniqueFileDescriptorVector(
- const std::vector<ScopedFd>& val);
-
- // Writes a blob to the parcel.
- // If the blob is small, then it is stored in-place, otherwise it is
- // transferred by way of an anonymous shared memory region. Prefer sending
- // immutable blobs if possible since they may be subsequently transferred between
- // processes without further copying whereas mutable blobs always need to be copied.
- // The caller should call release() on the blob after writing its contents.
- status_t writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob);
-
- // Write an existing immutable blob file descriptor to the parcel.
- // This allows the client to send the same blob to multiple processes
- // as long as it keeps a dup of the blob file descriptor handy for later.
- status_t writeDupImmutableBlobFileDescriptor(int fd);
-
- status_t writeObject(const flat_binder_object& val, bool nullMetaData);
-
- // Like Parcel.java's writeNoException(). Just writes a zero int32.
- // Currently the native implementation doesn't do any of the StrictMode
- // stack gathering and serialization that the Java implementation does.
- status_t writeNoException();
-
- void remove(size_t start, size_t amt);
-
- status_t read(void* outData, size_t len) const;
- const void* readInplace(size_t len) const;
- int32_t readInt32() const;
- status_t readInt32(int32_t *pArg) const;
- uint32_t readUint32() const;
- status_t readUint32(uint32_t *pArg) const;
- int64_t readInt64() const;
- status_t readInt64(int64_t *pArg) const;
- uint64_t readUint64() const;
- status_t readUint64(uint64_t *pArg) const;
- float readFloat() const;
- status_t readFloat(float *pArg) const;
- double readDouble() const;
- status_t readDouble(double *pArg) const;
- intptr_t readIntPtr() const;
- status_t readIntPtr(intptr_t *pArg) const;
- bool readBool() const;
- status_t readBool(bool *pArg) const;
- char16_t readChar() const;
- status_t readChar(char16_t *pArg) const;
- int8_t readByte() const;
- status_t readByte(int8_t *pArg) const;
-
- // Read a UTF16 encoded string, convert to UTF8
- status_t readUtf8FromUtf16(std::string* str) const;
- status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
-
- const char* readCString() const;
- String8 readString8() const;
- String16 readString16() const;
- status_t readString16(String16* pArg) const;
- status_t readString16(std::unique_ptr<String16>* pArg) const;
- const char16_t* readString16Inplace(size_t* outLen) const;
- sp<IBinder> readStrongBinder() const;
- status_t readStrongBinder(sp<IBinder>* val) const;
- wp<IBinder> readWeakBinder() const;
-
- template<typename T>
- status_t readParcelableVector(
- std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
- template<typename T>
- status_t readParcelableVector(std::vector<T>* val) const;
-
- status_t readParcelable(Parcelable* parcelable) const;
-
- template<typename T>
- status_t readParcelable(std::unique_ptr<T>* parcelable) const;
-
- template<typename T>
- status_t readStrongBinder(sp<T>* val) const;
-
- status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
- status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
-
- status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
- status_t readByteVector(std::vector<int8_t>* val) const;
- status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
- status_t readByteVector(std::vector<uint8_t>* val) const;
- status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
- status_t readInt32Vector(std::vector<int32_t>* val) const;
- status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
- status_t readInt64Vector(std::vector<int64_t>* val) const;
- status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
- status_t readFloatVector(std::vector<float>* val) const;
- status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
- status_t readDoubleVector(std::vector<double>* val) const;
- status_t readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
- status_t readBoolVector(std::vector<bool>* val) const;
- status_t readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
- status_t readCharVector(std::vector<char16_t>* val) const;
- status_t readString16Vector(
- std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
- status_t readString16Vector(std::vector<String16>* val) const;
- status_t readUtf8VectorFromUtf16Vector(
- std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
- status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
-
- template<typename T>
- status_t read(Flattenable<T>& val) const;
-
- template<typename T>
- status_t read(LightFlattenable<T>& val) const;
-
- // Like Parcel.java's readExceptionCode(). Reads the first int32
- // off of a Parcel's header, returning 0 or the negative error
- // code on exceptions, but also deals with skipping over rich
- // response headers. Callers should use this to read & parse the
- // response headers rather than doing it by hand.
- int32_t readExceptionCode() const;
-
- // Retrieve native_handle from the parcel. This returns a copy of the
- // parcel's native_handle (the caller takes ownership). The caller
- // must free the native_handle with native_handle_close() and
- // native_handle_delete().
- native_handle* readNativeHandle() const;
-
-
- // Retrieve a file descriptor from the parcel. This returns the raw fd
- // in the parcel, which you do not own -- use dup() to get your own copy.
- int readFileDescriptor() const;
-
- // Retrieve a smart file descriptor from the parcel.
- status_t readUniqueFileDescriptor(
- ScopedFd* val) const;
-
-
- // Retrieve a vector of smart file descriptors from the parcel.
- status_t readUniqueFileDescriptorVector(
- std::unique_ptr<std::vector<ScopedFd>>* val) const;
- status_t readUniqueFileDescriptorVector(
- std::vector<ScopedFd>* val) const;
-
- // Reads a blob from the parcel.
- // The caller should call release() on the blob after reading its contents.
- status_t readBlob(size_t len, ReadableBlob* outBlob) const;
-
- const flat_binder_object* readObject(bool nullMetaData) const;
-
- // Explicitly close all file descriptors in the parcel.
- void closeFileDescriptors();
-
- // Debugging: get metrics on current allocations.
- static size_t getGlobalAllocSize();
- static size_t getGlobalAllocCount();
-
-private:
- typedef void (*release_func)(Parcel* parcel,
- const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsSize,
- void* cookie);
-
- uintptr_t ipcData() const;
- size_t ipcDataSize() const;
- uintptr_t ipcObjects() const;
- size_t ipcObjectsCount() const;
- void ipcSetDataReference(const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsCount,
- release_func relFunc, void* relCookie);
-
-public:
- void print(TextOutput& to, uint32_t flags = 0) const;
-
-private:
- Parcel(const Parcel& o);
- Parcel& operator=(const Parcel& o);
-
- status_t finishWrite(size_t len);
- void releaseObjects();
- void acquireObjects();
- status_t growData(size_t len);
- status_t restartWrite(size_t desired);
- status_t continueWrite(size_t desired);
- status_t writePointer(uintptr_t val);
- status_t readPointer(uintptr_t *pArg) const;
- uintptr_t readPointer() const;
- void freeDataNoInit();
- void initState();
- void scanForFds() const;
-
- template<class T>
- status_t readAligned(T *pArg) const;
-
- template<class T> T readAligned() const;
-
- template<class T>
- status_t writeAligned(T val);
-
- status_t writeRawNullableParcelable(const Parcelable*
- parcelable);
-
- template<typename T, typename U>
- status_t unsafeReadTypedVector(std::vector<T>* val,
- status_t(Parcel::*read_func)(U*) const) const;
- template<typename T>
- status_t readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
- status_t(Parcel::*read_func)(T*) const) const;
- template<typename T>
- status_t readTypedVector(std::vector<T>* val,
- status_t(Parcel::*read_func)(T*) const) const;
- template<typename T, typename U>
- status_t unsafeWriteTypedVector(const std::vector<T>& val,
- status_t(Parcel::*write_func)(U));
- template<typename T>
- status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
- status_t(Parcel::*write_func)(const T&));
- template<typename T>
- status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
- status_t(Parcel::*write_func)(T));
- template<typename T>
- status_t writeTypedVector(const std::vector<T>& val,
- status_t(Parcel::*write_func)(const T&));
- template<typename T>
- status_t writeTypedVector(const std::vector<T>& val,
- status_t(Parcel::*write_func)(T));
-
- status_t mError;
- uint8_t* mData;
- size_t mDataSize;
- size_t mDataCapacity;
- mutable size_t mDataPos;
- binder_size_t* mObjects;
- size_t mObjectsSize;
- size_t mObjectsCapacity;
- mutable size_t mNextObjectHint;
-
- mutable bool mFdsKnown;
- mutable bool mHasFds;
- bool mAllowFds;
-
- release_func mOwner;
- void* mOwnerCookie;
-
- class Blob {
- public:
- Blob();
- ~Blob();
-
- void clear();
- void release();
- inline size_t size() const { return mSize; }
- inline int fd() const { return mFd; };
- inline bool isMutable() const { return mMutable; }
-
- protected:
- void init(int fd, void* data, size_t size, bool isMutable);
-
- int mFd; // owned by parcel so not closed when released
- void* mData;
- size_t mSize;
- bool mMutable;
- };
-
- class FlattenableHelperInterface {
- protected:
- ~FlattenableHelperInterface() { }
- public:
- virtual size_t getFlattenedSize() const = 0;
- virtual size_t getFdCount() const = 0;
- virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const = 0;
- virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) = 0;
- };
-
- template<typename T>
- class FlattenableHelper : public FlattenableHelperInterface {
- friend class Parcel;
- const Flattenable<T>& val;
- explicit FlattenableHelper(const Flattenable<T>& val) : val(val) { }
-
- public:
- virtual size_t getFlattenedSize() const {
- return val.getFlattenedSize();
- }
- virtual size_t getFdCount() const {
- return val.getFdCount();
- }
- virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const {
- return val.flatten(buffer, size, fds, count);
- }
- virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) {
- return const_cast<Flattenable<T>&>(val).unflatten(buffer, size, fds, count);
- }
- };
- status_t write(const FlattenableHelperInterface& val);
- status_t read(FlattenableHelperInterface& val) const;
-
-public:
- class ReadableBlob : public Blob {
- friend class Parcel;
- public:
- inline const void* data() const { return mData; }
- inline void* mutableData() { return isMutable() ? mData : NULL; }
- };
-
- class WritableBlob : public Blob {
- friend class Parcel;
- public:
- inline void* data() { return mData; }
- };
-
-private:
- size_t mOpenAshmemSize;
-
-public:
- // TODO: Remove once ABI can be changed.
- size_t getBlobAshmemSize() const;
- size_t getOpenAshmemSize() const;
-};
-
-// ---------------------------------------------------------------------------
-
-template<typename T>
-status_t Parcel::write(const Flattenable<T>& val) {
- const FlattenableHelper<T> helper(val);
- return write(helper);
-}
-
-template<typename T>
-status_t Parcel::write(const LightFlattenable<T>& val) {
- size_t size(val.getFlattenedSize());
- if (!val.isFixedSize()) {
- status_t err = writeInt32(size);
- if (err != NO_ERROR) {
- return err;
- }
- }
- if (size) {
- void* buffer = writeInplace(size);
- if (buffer == NULL)
- return NO_MEMORY;
- return val.flatten(buffer, size);
- }
- return NO_ERROR;
-}
-
-template<typename T>
-status_t Parcel::read(Flattenable<T>& val) const {
- FlattenableHelper<T> helper(val);
- return read(helper);
-}
-
-template<typename T>
-status_t Parcel::read(LightFlattenable<T>& val) const {
- size_t size;
- if (val.isFixedSize()) {
- size = val.getFlattenedSize();
- } else {
- int32_t s;
- status_t err = readInt32(&s);
- if (err != NO_ERROR) {
- return err;
- }
- size = s;
- }
- if (size) {
- void const* buffer = readInplace(size);
- return buffer == NULL ? NO_MEMORY :
- val.unflatten(buffer, size);
- }
- return NO_ERROR;
-}
-
-template<typename T>
-status_t Parcel::readStrongBinder(sp<T>* val) const {
- sp<IBinder> tmp;
- status_t ret = readStrongBinder(&tmp);
-
- if (ret == OK) {
- *val = interface_cast<T>(tmp);
-
- if (val->get() == nullptr) {
- return UNKNOWN_ERROR;
- }
- }
-
- return ret;
-}
-
-template<typename T, typename U>
-status_t Parcel::unsafeReadTypedVector(
- std::vector<T>* val,
- status_t(Parcel::*read_func)(U*) const) const {
- int32_t size;
- status_t status = this->readInt32(&size);
-
- if (status != OK) {
- return status;
- }
-
- if (size < 0) {
- return UNEXPECTED_NULL;
- }
-
- if (val->max_size() < size) {
- return NO_MEMORY;
- }
-
- val->resize(size);
-
- if (val->size() < size) {
- return NO_MEMORY;
- }
-
- for (auto& v: *val) {
- status = (this->*read_func)(&v);
-
- if (status != OK) {
- return status;
- }
- }
-
- return OK;
-}
-
-template<typename T>
-status_t Parcel::readTypedVector(std::vector<T>* val,
- status_t(Parcel::*read_func)(T*) const) const {
- return unsafeReadTypedVector(val, read_func);
-}
-
-template<typename T>
-status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
- status_t(Parcel::*read_func)(T*) const) const {
- const int32_t start = dataPosition();
- int32_t size;
- status_t status = readInt32(&size);
- val->reset();
-
- if (status != OK || size < 0) {
- return status;
- }
-
- setDataPosition(start);
- val->reset(new std::vector<T>());
-
- status = unsafeReadTypedVector(val->get(), read_func);
-
- if (status != OK) {
- val->reset();
- }
-
- return status;
-}
-
-template<typename T, typename U>
-status_t Parcel::unsafeWriteTypedVector(const std::vector<T>& val,
- status_t(Parcel::*write_func)(U)) {
- if (val.size() > std::numeric_limits<int32_t>::max()) {
- return BAD_VALUE;
- }
-
- status_t status = this->writeInt32(val.size());
-
- if (status != OK) {
- return status;
- }
-
- for (const auto& item : val) {
- status = (this->*write_func)(item);
-
- if (status != OK) {
- return status;
- }
- }
-
- return OK;
-}
-
-template<typename T>
-status_t Parcel::writeTypedVector(const std::vector<T>& val,
- status_t(Parcel::*write_func)(const T&)) {
- return unsafeWriteTypedVector(val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeTypedVector(const std::vector<T>& val,
- status_t(Parcel::*write_func)(T)) {
- return unsafeWriteTypedVector(val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
- status_t(Parcel::*write_func)(const T&)) {
- if (val.get() == nullptr) {
- return this->writeInt32(-1);
- }
-
- return unsafeWriteTypedVector(*val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
- status_t(Parcel::*write_func)(T)) {
- if (val.get() == nullptr) {
- return this->writeInt32(-1);
- }
-
- return unsafeWriteTypedVector(*val, write_func);
-}
-
-template<typename T>
-status_t Parcel::readParcelableVector(std::vector<T>* val) const {
- return unsafeReadTypedVector<T, Parcelable>(val, &Parcel::readParcelable);
-}
-
-template<typename T>
-status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
- const int32_t start = dataPosition();
- int32_t size;
- status_t status = readInt32(&size);
- val->reset();
-
- if (status != OK || size < 0) {
- return status;
- }
-
- setDataPosition(start);
- val->reset(new std::vector<std::unique_ptr<T>>());
-
- status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
-
- if (status != OK) {
- val->reset();
- }
-
- return status;
-}
-
-template<typename T>
-status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const {
- const int32_t start = dataPosition();
- int32_t present;
- status_t status = readInt32(&present);
- parcelable->reset();
-
- if (status != OK || !present) {
- return status;
- }
-
- setDataPosition(start);
- parcelable->reset(new T());
-
- status = readParcelable(parcelable->get());
-
- if (status != OK) {
- parcelable->reset();
- }
-
- return status;
-}
-
-template<typename T>
-status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
- return writeRawNullableParcelable(parcelable.get());
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::vector<T>& val) {
- return unsafeWriteTypedVector<T,const Parcelable&>(val, &Parcel::writeParcelable);
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) {
- if (val.get() == nullptr) {
- return this->writeInt32(-1);
- }
-
- return unsafeWriteTypedVector(*val, &Parcel::writeParcelable);
-}
-
-// ---------------------------------------------------------------------------
-
-inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
-{
- parcel.print(to);
- return to;
-}
-
-// ---------------------------------------------------------------------------
-
-// Generic acquire and release of objects.
-void acquire_object(const sp<ProcessState>& proc,
- const flat_binder_object& obj, const void* who);
-void release_object(const sp<ProcessState>& proc,
- const flat_binder_object& obj, const void* who);
-
-void flatten_binder(const sp<ProcessState>& proc,
- const sp<IBinder>& binder, flat_binder_object* out);
-void flatten_binder(const sp<ProcessState>& proc,
- const wp<IBinder>& binder, flat_binder_object* out);
-status_t unflatten_binder(const sp<ProcessState>& proc,
- const flat_binder_object& flat, sp<IBinder>* out);
-status_t unflatten_binder(const sp<ProcessState>& proc,
- const flat_binder_object& flat, wp<IBinder>* out);
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PARCEL_H
diff --git a/include/binder/Parcelable.h b/include/binder/Parcelable.h
deleted file mode 100644
index faf0d34..0000000
--- a/include/binder/Parcelable.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 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_PARCELABLE_H
-#define ANDROID_PARCELABLE_H
-
-#include <vector>
-
-#include <utils/Errors.h>
-#include <utils/String16.h>
-
-namespace android {
-
-class Parcel;
-
-// Abstract interface of all parcelables.
-class Parcelable {
-public:
- virtual ~Parcelable() = default;
-
- // Write |this| parcelable to the given |parcel|. Keep in mind that
- // implementations of writeToParcel must be manually kept in sync
- // with readFromParcel and the Java equivalent versions of these methods.
- //
- // Returns android::OK on success and an appropriate error otherwise.
- virtual status_t writeToParcel(Parcel* parcel) const = 0;
-
- // Read data from the given |parcel| into |this|. After readFromParcel
- // completes, |this| should have equivalent state to the object that
- // wrote itself to the parcel.
- //
- // Returns android::OK on success and an appropriate error otherwise.
- virtual status_t readFromParcel(const Parcel* parcel) = 0;
-}; // class Parcelable
-
-} // namespace android
-
-#endif // ANDROID_PARCELABLE_H
diff --git a/include/binder/PersistableBundle.h b/include/binder/PersistableBundle.h
deleted file mode 100644
index fe5619f..0000000
--- a/include/binder/PersistableBundle.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2015 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_PERSISTABLE_BUNDLE_H
-#define ANDROID_PERSISTABLE_BUNDLE_H
-
-#include <map>
-#include <vector>
-
-#include <binder/Parcelable.h>
-#include <utils/String16.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-namespace os {
-
-/*
- * C++ implementation of PersistableBundle, a mapping from String values to
- * various types that can be saved to persistent and later restored.
- */
-class PersistableBundle : public Parcelable {
-public:
- PersistableBundle() = default;
- virtual ~PersistableBundle() = default;
- PersistableBundle(const PersistableBundle& bundle) = default;
-
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- bool empty() const;
- size_t size() const;
- size_t erase(const String16& key);
-
- /*
- * Setters for PersistableBundle. Adds a a key-value pair instantiated with
- * |key| and |value| into the member map appropriate for the type of |value|.
- * If there is already an existing value for |key|, |value| will replace it.
- */
- void putBoolean(const String16& key, bool value);
- void putInt(const String16& key, int32_t value);
- void putLong(const String16& key, int64_t value);
- void putDouble(const String16& key, double value);
- void putString(const String16& key, const String16& value);
- void putBooleanVector(const String16& key, const std::vector<bool>& value);
- void putIntVector(const String16& key, const std::vector<int32_t>& value);
- void putLongVector(const String16& key, const std::vector<int64_t>& value);
- void putDoubleVector(const String16& key, const std::vector<double>& value);
- void putStringVector(const String16& key, const std::vector<String16>& value);
- void putPersistableBundle(const String16& key, const PersistableBundle& value);
-
- /*
- * Getters for PersistableBundle. If |key| exists, these methods write the
- * value associated with |key| into |out|, and return true. Otherwise, these
- * methods return false.
- */
- bool getBoolean(const String16& key, bool* out) const;
- bool getInt(const String16& key, int32_t* out) const;
- bool getLong(const String16& key, int64_t* out) const;
- bool getDouble(const String16& key, double* out) const;
- bool getString(const String16& key, String16* out) const;
- bool getBooleanVector(const String16& key, std::vector<bool>* out) const;
- bool getIntVector(const String16& key, std::vector<int32_t>* out) const;
- bool getLongVector(const String16& key, std::vector<int64_t>* out) const;
- bool getDoubleVector(const String16& key, std::vector<double>* out) const;
- bool getStringVector(const String16& key, std::vector<String16>* out) const;
- bool getPersistableBundle(const String16& key, PersistableBundle* out) const;
-
- friend bool operator==(const PersistableBundle& lhs, const PersistableBundle& rhs) {
- return (lhs.mBoolMap == rhs.mBoolMap && lhs.mIntMap == rhs.mIntMap &&
- lhs.mLongMap == rhs.mLongMap && lhs.mDoubleMap == rhs.mDoubleMap &&
- lhs.mStringMap == rhs.mStringMap && lhs.mBoolVectorMap == rhs.mBoolVectorMap &&
- lhs.mIntVectorMap == rhs.mIntVectorMap &&
- lhs.mLongVectorMap == rhs.mLongVectorMap &&
- lhs.mDoubleVectorMap == rhs.mDoubleVectorMap &&
- lhs.mStringVectorMap == rhs.mStringVectorMap &&
- lhs.mPersistableBundleMap == rhs.mPersistableBundleMap);
- }
-
- friend bool operator!=(const PersistableBundle& lhs, const PersistableBundle& rhs) {
- return !(lhs == rhs);
- }
-
-private:
- status_t writeToParcelInner(Parcel* parcel) const;
- status_t readFromParcelInner(const Parcel* parcel, size_t length);
-
- std::map<String16, bool> mBoolMap;
- std::map<String16, int32_t> mIntMap;
- std::map<String16, int64_t> mLongMap;
- std::map<String16, double> mDoubleMap;
- std::map<String16, String16> mStringMap;
- std::map<String16, std::vector<bool>> mBoolVectorMap;
- std::map<String16, std::vector<int32_t>> mIntVectorMap;
- std::map<String16, std::vector<int64_t>> mLongVectorMap;
- std::map<String16, std::vector<double>> mDoubleVectorMap;
- std::map<String16, std::vector<String16>> mStringVectorMap;
- std::map<String16, PersistableBundle> mPersistableBundleMap;
-};
-
-} // namespace os
-
-} // namespace android
-
-#endif // ANDROID_PERSISTABLE_BUNDLE_H
diff --git a/include/binder/ProcessInfoService.h b/include/binder/ProcessInfoService.h
deleted file mode 100644
index c5ead20..0000000
--- a/include/binder/ProcessInfoService.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2015 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_PROCESS_INFO_SERVICE_H
-#define ANDROID_PROCESS_INFO_SERVICE_H
-
-#include <binder/IProcessInfoService.h>
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
-#include <sys/types.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class ProcessInfoService : public Singleton<ProcessInfoService> {
-
- friend class Singleton<ProcessInfoService>;
- sp<IProcessInfoService> mProcessInfoService;
- Mutex mProcessInfoLock;
-
- ProcessInfoService();
-
- status_t getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states);
- void updateBinderLocked();
-
- static const int BINDER_ATTEMPT_LIMIT = 5;
-
-public:
-
- /**
- * For each PID in the given "pids" input array, write the current process state
- * for that process into the "states" output array, or
- * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID
- * exists.
- *
- * Returns NO_ERROR if this operation was successful, or a negative error code otherwise.
- */
- static status_t getProcessStatesFromPids(size_t length, /*in*/ int32_t* pids,
- /*out*/ int32_t* states) {
- return ProcessInfoService::getInstance().getProcessStatesImpl(length, /*in*/ pids,
- /*out*/ states);
- }
-
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_PROCESS_INFO_SERVICE_H
-
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
deleted file mode 100644
index 64cf72e..0000000
--- a/include/binder/ProcessState.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2005 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_PROCESS_STATE_H
-#define ANDROID_PROCESS_STATE_H
-
-#include <binder/IBinder.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-
-#include <utils/threads.h>
-
-#include <pthread.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class IPCThreadState;
-
-class ProcessState : public virtual RefBase
-{
-public:
- static sp<ProcessState> self();
-
- void setContextObject(const sp<IBinder>& object);
- sp<IBinder> getContextObject(const sp<IBinder>& caller);
-
- void setContextObject(const sp<IBinder>& object,
- const String16& name);
- sp<IBinder> getContextObject(const String16& name,
- const sp<IBinder>& caller);
-
- void startThreadPool();
-
- typedef bool (*context_check_func)(const String16& name,
- const sp<IBinder>& caller,
- void* userData);
-
- bool isContextManager(void) const;
- bool becomeContextManager(
- context_check_func checkFunc,
- void* userData);
-
- sp<IBinder> getStrongProxyForHandle(int32_t handle);
- wp<IBinder> getWeakProxyForHandle(int32_t handle);
- void expungeHandle(int32_t handle, IBinder* binder);
-
- void spawnPooledThread(bool isMain);
-
- status_t setThreadPoolMaxThreadCount(size_t maxThreads);
- void giveThreadPoolName();
-
-private:
- friend class IPCThreadState;
-
- ProcessState();
- ~ProcessState();
-
- ProcessState(const ProcessState& o);
- ProcessState& operator=(const ProcessState& o);
- String8 makeBinderThreadName();
-
- struct handle_entry {
- IBinder* binder;
- RefBase::weakref_type* refs;
- };
-
- handle_entry* lookupHandleLocked(int32_t handle);
-
- int mDriverFD;
- void* mVMStart;
-
- // Protects thread count variable below.
- pthread_mutex_t mThreadCountLock;
- pthread_cond_t mThreadCountDecrement;
- // Number of binder threads current executing a command.
- size_t mExecutingThreadsCount;
- // Maximum number for binder threads allowed for this process.
- size_t mMaxThreads;
- // Time when thread pool was emptied
- int64_t mStarvationStartTimeMs;
-
- mutable Mutex mLock; // protects everything below.
-
- Vector<handle_entry>mHandleToObject;
-
- bool mManagesContexts;
- context_check_func mBinderContextCheckFunc;
- void* mBinderContextUserData;
-
- KeyedVector<String16, sp<IBinder> >
- mContexts;
-
-
- String8 mRootDir;
- bool mThreadPoolStarted;
- volatile int32_t mThreadPoolSeq;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PROCESS_STATE_H
diff --git a/include/binder/Status.h b/include/binder/Status.h
deleted file mode 100644
index ce947fa..0000000
--- a/include/binder/Status.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2015 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_BINDER_STATUS_H
-#define ANDROID_BINDER_STATUS_H
-
-#include <cstdint>
-
-#include <binder/Parcel.h>
-#include <utils/String8.h>
-
-namespace android {
-namespace binder {
-
-// An object similar in function to a status_t except that it understands
-// how exceptions are encoded in the prefix of a Parcel. Used like:
-//
-// Parcel data;
-// Parcel reply;
-// status_t status;
-// binder::Status remote_exception;
-// if ((status = data.writeInterfaceToken(interface_descriptor)) != OK ||
-// (status = data.writeInt32(function_input)) != OK) {
-// // We failed to write into the memory of our local parcel?
-// }
-// if ((status = remote()->transact(transaction, data, &reply)) != OK) {
-// // Something has gone wrong in the binder driver or libbinder.
-// }
-// if ((status = remote_exception.readFromParcel(reply)) != OK) {
-// // The remote didn't correctly write the exception header to the
-// // reply.
-// }
-// if (!remote_exception.isOk()) {
-// // The transaction went through correctly, but the remote reported an
-// // exception during handling.
-// }
-//
-class Status final {
-public:
- // Keep the exception codes in sync with android/os/Parcel.java.
- enum Exception {
- EX_NONE = 0,
- EX_SECURITY = -1,
- EX_BAD_PARCELABLE = -2,
- EX_ILLEGAL_ARGUMENT = -3,
- EX_NULL_POINTER = -4,
- EX_ILLEGAL_STATE = -5,
- EX_NETWORK_MAIN_THREAD = -6,
- EX_UNSUPPORTED_OPERATION = -7,
- EX_SERVICE_SPECIFIC = -8,
-
- // This is special and Java specific; see Parcel.java.
- EX_HAS_REPLY_HEADER = -128,
- // This is special, and indicates to C++ binder proxies that the
- // transaction has failed at a low level.
- EX_TRANSACTION_FAILED = -129,
- };
-
- // A more readable alias for the default constructor.
- static Status ok();
- // Authors should explicitly pick whether their integer is:
- // - an exception code (EX_* above)
- // - service specific error code
- // - status_t
- //
- // Prefer a generic exception code when possible, then a service specific
- // code, and finally a status_t for low level failures or legacy support.
- // Exception codes and service specific errors map to nicer exceptions for
- // Java clients.
- static Status fromExceptionCode(int32_t exceptionCode);
- static Status fromExceptionCode(int32_t exceptionCode,
- const String8& message);
- static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode);
- static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode,
- const String8& message);
- static Status fromStatusT(status_t status);
-
- Status() = default;
- ~Status() = default;
-
- // Status objects are copyable and contain just simple data.
- Status(const Status& status) = default;
- Status(Status&& status) = default;
- Status& operator=(const Status& status) = default;
-
- // Bear in mind that if the client or service is a Java endpoint, this
- // is not the logic which will provide/interpret the data here.
- status_t readFromParcel(const Parcel& parcel);
- status_t writeToParcel(Parcel* parcel) const;
-
- // Set one of the pre-defined exception types defined above.
- void setException(int32_t ex, const String8& message);
- // Set a service specific exception with error code.
- void setServiceSpecificError(int32_t errorCode, const String8& message);
- // Setting a |status| != OK causes generated code to return |status|
- // from Binder transactions, rather than writing an exception into the
- // reply Parcel. This is the least preferable way of reporting errors.
- void setFromStatusT(status_t status);
-
- // Get information about an exception.
- int32_t exceptionCode() const { return mException; }
- const String8& exceptionMessage() const { return mMessage; }
- status_t transactionError() const {
- return mException == EX_TRANSACTION_FAILED ? mErrorCode : OK;
- }
- int32_t serviceSpecificErrorCode() const {
- return mException == EX_SERVICE_SPECIFIC ? mErrorCode : 0;
- }
-
- bool isOk() const { return mException == EX_NONE; }
-
- // For logging.
- String8 toString8() const;
-
-private:
- Status(int32_t exceptionCode, int32_t errorCode);
- Status(int32_t exceptionCode, int32_t errorCode, const String8& message);
-
- // If |mException| == EX_TRANSACTION_FAILED, generated code will return
- // |mErrorCode| as the result of the transaction rather than write an
- // exception to the reply parcel.
- //
- // Otherwise, we always write |mException| to the parcel.
- // If |mException| != EX_NONE, we write |mMessage| as well.
- // If |mException| == EX_SERVICE_SPECIFIC we write |mErrorCode| as well.
- int32_t mException = EX_NONE;
- int32_t mErrorCode = 0;
- String8 mMessage;
-}; // class Status
-
-// For gtest output logging
-template<typename T>
-T& operator<< (T& stream, const Status& s) {
- stream << s.toString8().string();
- return stream;
-}
-
-} // namespace binder
-} // namespace android
-
-#endif // ANDROID_BINDER_STATUS_H
diff --git a/include/binder/TextOutput.h b/include/binder/TextOutput.h
deleted file mode 100644
index 974a194..0000000
--- a/include/binder/TextOutput.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2006 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_TEXTOUTPUT_H
-#define ANDROID_TEXTOUTPUT_H
-
-#include <utils/Errors.h>
-
-#include <stdint.h>
-#include <string.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class String8;
-class String16;
-
-class TextOutput
-{
-public:
- TextOutput();
- virtual ~TextOutput();
-
- virtual status_t print(const char* txt, size_t len) = 0;
- virtual void moveIndent(int delta) = 0;
-
- class Bundle {
- public:
- inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); }
- inline ~Bundle() { mTO.popBundle(); }
- private:
- TextOutput& mTO;
- };
-
- virtual void pushBundle() = 0;
- virtual void popBundle() = 0;
-};
-
-// ---------------------------------------------------------------------------
-
-// Text output stream for printing to the log (via utils/Log.h).
-extern TextOutput& alog;
-
-// Text output stream for printing to stdout.
-extern TextOutput& aout;
-
-// Text output stream for printing to stderr.
-extern TextOutput& aerr;
-
-typedef TextOutput& (*TextOutputManipFunc)(TextOutput&);
-
-TextOutput& endl(TextOutput& to);
-TextOutput& indent(TextOutput& to);
-TextOutput& dedent(TextOutput& to);
-
-TextOutput& operator<<(TextOutput& to, const char* str);
-TextOutput& operator<<(TextOutput& to, char); // writes raw character
-TextOutput& operator<<(TextOutput& to, bool);
-TextOutput& operator<<(TextOutput& to, int);
-TextOutput& operator<<(TextOutput& to, long);
-TextOutput& operator<<(TextOutput& to, unsigned int);
-TextOutput& operator<<(TextOutput& to, unsigned long);
-TextOutput& operator<<(TextOutput& to, long long);
-TextOutput& operator<<(TextOutput& to, unsigned long long);
-TextOutput& operator<<(TextOutput& to, float);
-TextOutput& operator<<(TextOutput& to, double);
-TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
-TextOutput& operator<<(TextOutput& to, const void*);
-TextOutput& operator<<(TextOutput& to, const String8& val);
-TextOutput& operator<<(TextOutput& to, const String16& val);
-
-class TypeCode
-{
-public:
- inline TypeCode(uint32_t code);
- inline ~TypeCode();
-
- inline uint32_t typeCode() const;
-
-private:
- uint32_t mCode;
-};
-
-TextOutput& operator<<(TextOutput& to, const TypeCode& val);
-
-class HexDump
-{
-public:
- HexDump(const void *buf, size_t size, size_t bytesPerLine=16);
- inline ~HexDump();
-
- inline HexDump& setBytesPerLine(size_t bytesPerLine);
- inline HexDump& setSingleLineCutoff(int32_t bytes);
- inline HexDump& setAlignment(size_t alignment);
- inline HexDump& setCArrayStyle(bool enabled);
-
- inline const void* buffer() const;
- inline size_t size() const;
- inline size_t bytesPerLine() const;
- inline int32_t singleLineCutoff() const;
- inline size_t alignment() const;
- inline bool carrayStyle() const;
-
-private:
- const void* mBuffer;
- size_t mSize;
- size_t mBytesPerLine;
- int32_t mSingleLineCutoff;
- size_t mAlignment;
- bool mCArrayStyle;
-};
-
-TextOutput& operator<<(TextOutput& to, const HexDump& val);
-
-// ---------------------------------------------------------------------------
-// No user servicable parts below.
-
-inline TextOutput& endl(TextOutput& to)
-{
- to.print("\n", 1);
- return to;
-}
-
-inline TextOutput& indent(TextOutput& to)
-{
- to.moveIndent(1);
- return to;
-}
-
-inline TextOutput& dedent(TextOutput& to)
-{
- to.moveIndent(-1);
- return to;
-}
-
-inline TextOutput& operator<<(TextOutput& to, const char* str)
-{
- to.print(str, strlen(str));
- return to;
-}
-
-inline TextOutput& operator<<(TextOutput& to, char c)
-{
- to.print(&c, 1);
- return to;
-}
-
-inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func)
-{
- return (*func)(to);
-}
-
-inline TypeCode::TypeCode(uint32_t code) : mCode(code) { }
-inline TypeCode::~TypeCode() { }
-inline uint32_t TypeCode::typeCode() const { return mCode; }
-
-inline HexDump::~HexDump() { }
-
-inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) {
- mBytesPerLine = bytesPerLine; return *this;
-}
-inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) {
- mSingleLineCutoff = bytes; return *this;
-}
-inline HexDump& HexDump::setAlignment(size_t alignment) {
- mAlignment = alignment; return *this;
-}
-inline HexDump& HexDump::setCArrayStyle(bool enabled) {
- mCArrayStyle = enabled; return *this;
-}
-
-inline const void* HexDump::buffer() const { return mBuffer; }
-inline size_t HexDump::size() const { return mSize; }
-inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; }
-inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; }
-inline size_t HexDump::alignment() const { return mAlignment; }
-inline bool HexDump::carrayStyle() const { return mCArrayStyle; }
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_TEXTOUTPUT_H
diff --git a/include/gfx/.clang-format b/include/gfx/.clang-format
new file mode 100644
index 0000000..86763a0
--- /dev/null
+++ b/include/gfx/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
+BinPackParameters: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 2
+ContinuationIndentWidth: 8
+IndentWidth: 4
diff --git a/include/gui/BitTube.h b/include/gui/BitTube.h
deleted file mode 100644
index 3ecac52..0000000
--- a/include/gui/BitTube.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_SENSOR_CHANNEL_H
-#define ANDROID_GUI_SENSOR_CHANNEL_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <cutils/log.h>
-
-
-namespace android {
-// ----------------------------------------------------------------------------
-class Parcel;
-
-class BitTube : public RefBase
-{
-public:
-
- // creates a BitTube with a default (4KB) send buffer
- BitTube();
-
- // creates a BitTube with a a specified send and receive buffer size
- explicit BitTube(size_t bufsize);
-
- explicit BitTube(const Parcel& data);
- virtual ~BitTube();
-
- // check state after construction
- status_t initCheck() const;
-
- // get receive file-descriptor
- int getFd() const;
-
- // get the send file-descriptor.
- int getSendFd() const;
-
- // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
- template <typename T>
- static ssize_t sendObjects(const sp<BitTube>& tube,
- T const* events, size_t count) {
- return sendObjects(tube, events, count, sizeof(T));
- }
-
- // receive objects (sized blobs). If the receiving buffer isn't large enough,
- // excess messages are silently discarded.
- template <typename T>
- static ssize_t recvObjects(const sp<BitTube>& tube,
- T* events, size_t count) {
- return recvObjects(tube, events, count, sizeof(T));
- }
-
- // parcels this BitTube
- status_t writeToParcel(Parcel* reply) const;
-
-private:
- void init(size_t rcvbuf, size_t sndbuf);
-
- // send a message. The write is guaranteed to send the whole message or fail.
- ssize_t write(void const* vaddr, size_t size);
-
- // receive a message. the passed buffer must be at least as large as the
- // write call used to send the message, excess data is silently discarded.
- ssize_t read(void* vaddr, size_t size);
-
- int mSendFd;
- mutable int mReceiveFd;
-
- static ssize_t sendObjects(const sp<BitTube>& tube,
- void const* events, size_t count, size_t objSize);
-
- static ssize_t recvObjects(const sp<BitTube>& tube,
- void* events, size_t count, size_t objSize);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SENSOR_CHANNEL_H
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index f45d852..55637a9 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -17,9 +17,7 @@
#ifndef ANDROID_GUI_BUFFERITEM_H
#define ANDROID_GUI_BUFFERITEM_H
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
+#include <ui/FenceTime.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -46,6 +44,8 @@
enum { INVALID_BUFFER_SLOT = -1 };
BufferItem();
~BufferItem();
+ BufferItem(const BufferItem&) = default;
+ BufferItem& operator=(const BufferItem&) = default;
static const char* scalingModeName(uint32_t scalingMode);
@@ -57,6 +57,9 @@
// mFence is a fence that will signal when the buffer is idle.
sp<Fence> mFence;
+ // The std::shared_ptr<FenceTime> wrapper around mFence.
+ std::shared_ptr<FenceTime> mFenceTime{FenceTime::NO_FENCE};
+
// mCrop is the current crop rectangle for this buffer slot.
Rect mCrop;
@@ -72,13 +75,7 @@
// to set by queueBuffer each time this slot is queued. This value
// is guaranteed to be monotonically increasing for each newly
// acquired buffer.
- union {
- int64_t mTimestamp;
- struct {
- uint32_t mTimestampLo;
- uint32_t mTimestampHi;
- };
- };
+ int64_t mTimestamp;
// mIsAutoTimestamp indicates whether mTimestamp was generated
// automatically when the buffer was queued.
@@ -90,13 +87,7 @@
android_dataspace mDataSpace;
// mFrameNumber is the number of the queued frame for this slot.
- union {
- uint64_t mFrameNumber;
- struct {
- uint32_t mFrameNumberLo;
- uint32_t mFrameNumberHi;
- };
- };
+ uint64_t mFrameNumber;
// mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
int mSlot;
diff --git a/include/gui/BufferItemConsumer.h b/include/gui/BufferItemConsumer.h
index 56c7a3f..db7e944 100644
--- a/include/gui/BufferItemConsumer.h
+++ b/include/gui/BufferItemConsumer.h
@@ -18,18 +18,13 @@
#define ANDROID_GUI_BUFFERITEMCONSUMER_H
#include <gui/ConsumerBase.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <utils/String8.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
+#include <gui/BufferQueue.h>
#define ANDROID_GRAPHICS_BUFFERITEMCONSUMER_JNI_ID "mBufferItemConsumer"
namespace android {
-class BufferQueue;
+class String8;
/**
* BufferItemConsumer is a BufferQueue consumer endpoint that allows clients
@@ -42,6 +37,10 @@
public:
typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
+ struct BufferFreedListener : public virtual RefBase {
+ virtual void onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) = 0;
+ };
+
enum { DEFAULT_MAX_BUFFERS = -1 };
enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT };
enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE };
@@ -62,6 +61,10 @@
// log messages.
void setName(const String8& name);
+ // setBufferFreedListener sets the listener object that will be notified
+ // when an old buffer is being freed.
+ void setBufferFreedListener(const wp<BufferFreedListener>& listener);
+
// Gets the next graphics buffer from the producer, filling out the
// passed-in BufferItem structure. Returns NO_BUFFER_AVAILABLE if the queue
// of buffers is empty, and INVALID_OPERATION if the maximum number of
@@ -86,6 +89,13 @@
status_t releaseBuffer(const BufferItem &item,
const sp<Fence>& releaseFence = Fence::NO_FENCE);
+ private:
+ void freeBufferLocked(int slotIndex) override;
+
+ // mBufferFreedListener is the listener object that will be called when
+ // an old buffer is being freed. If it is not NULL it will be called from
+ // freeBufferLocked.
+ wp<BufferFreedListener> mBufferFreedListener;
};
} // namespace android
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index fe4b1fa..bd62d85 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -23,10 +23,6 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/IConsumerListener.h>
-// These are only required to keep other parts of the framework with incomplete
-// dependencies building successfully
-#include <gui/IGraphicBufferAlloc.h>
-
namespace android {
class BufferQueue {
@@ -60,14 +56,16 @@
// weak references.
class ProxyConsumerListener : public BnConsumerListener {
public:
- ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
+ explicit ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
virtual ~ProxyConsumerListener();
- virtual void onFrameAvailable(const BufferItem& item) override;
- virtual void onFrameReplaced(const BufferItem& item) override;
- virtual void onBuffersReleased() override;
- virtual void onSidebandStreamChanged() override;
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const override;
+ void onDisconnect() override;
+ void onFrameAvailable(const BufferItem& item) override;
+ void onFrameReplaced(const BufferItem& item) override;
+ void onBuffersReleased() override;
+ void onSidebandStreamChanged() override;
+ void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override;
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
@@ -79,10 +77,9 @@
// needed gralloc buffers.
static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
- const sp<IGraphicBufferAlloc>& allocator = NULL);
+ bool consumerIsSurfaceFlinger = false);
-private:
- BufferQueue(); // Create through createBufferQueue
+ BufferQueue() = delete; // Create through createBufferQueue
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index 8ec0546..b383056 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 {
@@ -109,7 +110,7 @@
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
// setConsumerName sets the name used in logging
- virtual void setConsumerName(const String8& name);
+ status_t setConsumerName(const String8& name) override;
// setDefaultBufferFormat allows the BufferQueue to create
// GraphicBuffers of a defaultFormat if no format is specified
@@ -128,13 +129,19 @@
// enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
virtual status_t setConsumerUsageBits(uint32_t usage);
+ // setConsumerIsProtected will turn on an internal bit that indicates whether
+ // the consumer can handle protected gralloc buffers (i.e. with
+ // GRALLOC_USAGE_PROTECTED set). IGraphicBufferProducer can query this
+ // capability using NATIVE_WINDOW_CONSUMER_IS_PROTECTED.
+ virtual status_t setConsumerIsProtected(bool isProtected);
+
// setTransformHint bakes in rotation to buffers so overlays can be used.
// The values are enumerated in window.h, e.g.
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
virtual status_t setTransformHint(uint32_t hint);
// Retrieve the sideband buffer stream, if any.
- virtual sp<NativeHandle> getSidebandStream() const;
+ status_t getSidebandStream(sp<NativeHandle>* outStream) const override;
// See IGraphicBufferConsumer::getOccupancyHistory
virtual status_t getOccupancyHistory(bool forceFlush,
@@ -144,7 +151,7 @@
virtual status_t discardFreeBuffers() override;
// dump our state in a String
- virtual void dump(String8& result, const char* prefix) const;
+ status_t dumpState(const String8& prefix, String8* outResult) const override;
// Functions required for backwards compatibility.
// These will be modified/renamed in IGraphicBufferConsumer and will be
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 1226feb..dd8b992 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -51,7 +51,6 @@
namespace android {
class IConsumerListener;
-class IGraphicBufferAlloc;
class IProducerListener;
class BufferQueueCore : public virtual RefBase {
@@ -79,14 +78,13 @@
typedef Vector<BufferItem> Fifo;
// BufferQueueCore manages a pool of gralloc memory slots to be used by
- // producers and consumers. allocator is used to allocate all the needed
- // gralloc buffers.
- BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator = NULL);
+ // producers and consumers.
+ BufferQueueCore();
virtual ~BufferQueueCore();
private:
// Dump our state in a string
- void dump(String8& result, const char* prefix) const;
+ void dumpState(const String8& prefix, String8* outResult) const;
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
// that must remain in a state other than DEQUEUED. The async parameter
@@ -143,10 +141,6 @@
void validateConsistencyLocked() const;
#endif
- // mAllocator is the connection to SurfaceFlinger that is used to allocate
- // new GraphicBuffer objects.
- sp<IGraphicBufferAlloc> mAllocator;
-
// mMutex is the mutex used to prevent concurrent access to the member
// variables of BufferQueueCore objects. It must be locked whenever any
// member variable is accessed.
@@ -178,6 +172,10 @@
// GraphicBuffers.
uint32_t mConsumerUsageBits;
+ // mConsumerIsProtected indicates the consumer is ready to handle protected
+ // buffer.
+ bool mConsumerIsProtected;
+
// mConnectedApi indicates the producer API that is currently connected
// to this BufferQueue. It defaults to NO_CONNECTED_API, and gets updated
// by the connect and disconnect methods.
@@ -317,13 +315,13 @@
// Cached data about the shared buffer in shared buffer mode
struct SharedBufferCache {
- SharedBufferCache(Rect _crop, uint32_t _transform, int _scalingMode,
- android_dataspace _dataspace)
+ SharedBufferCache(Rect _crop, uint32_t _transform,
+ uint32_t _scalingMode, android_dataspace _dataspace)
: crop(_crop),
transform(_transform),
scalingMode(_scalingMode),
dataspace(_dataspace) {
- };
+ }
Rect crop;
uint32_t transform;
diff --git a/include/gui/BufferQueueDefs.h b/include/gui/BufferQueueDefs.h
index 83e9580..ffafb49 100644
--- a/include/gui/BufferQueueDefs.h
+++ b/include/gui/BufferQueueDefs.h
@@ -18,16 +18,12 @@
#define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
#include <gui/BufferSlot.h>
+#include <ui/BufferQueueDefs.h>
namespace android {
class BufferQueueCore;
namespace BufferQueueDefs {
- // BufferQueue will keep track of at most this value of buffers.
- // Attempts at runtime to increase the number of buffers past this
- // will fail.
- enum { NUM_BUFFER_SLOTS = 64 };
-
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
} // namespace BufferQueueDefs
} // namespace android
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 8f613ee..5541468 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -22,14 +22,14 @@
namespace android {
-class BufferSlot;
+struct BufferSlot;
class BufferQueueProducer : public BnGraphicBufferProducer,
private IBinder::DeathRecipient {
public:
friend class BufferQueue; // Needed to access binderDied
- BufferQueueProducer(const sp<BufferQueueCore>& core);
+ BufferQueueProducer(const sp<BufferQueueCore>& core, bool consumerIsSurfaceFlinger = false);
virtual ~BufferQueueProducer();
// requestBuffer returns the GraphicBuffer for slot N.
@@ -80,9 +80,9 @@
//
// In both cases, the producer will need to call requestBuffer to get a
// GraphicBuffer handle for the returned slot.
- virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence,
+ status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
- uint32_t usage);
+ uint32_t usage, FrameEventHistoryDelta* outTimestamps) override;
// See IGraphicBufferProducer::detachBuffer
virtual status_t detachBuffer(int slot);
@@ -177,8 +177,7 @@
sp<Fence>* outFence, float outTransformMatrix[16]) override;
// See IGraphicBufferProducer::getFrameTimestamps
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const override;
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
// See IGraphicBufferProducer::getUniqueId
virtual status_t getUniqueId(uint64_t* outId) const override;
@@ -195,6 +194,9 @@
// BufferQueueCore::INVALID_BUFFER_SLOT otherwise
int getFreeSlotLocked() const;
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta);
+
// waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
// block if there are no available slots and we are not in non-blocking
// mode (producer and consumer controlled by the application). If it blocks,
@@ -218,6 +220,10 @@
uint32_t mStickyTransform;
+ // This controls whether the GraphicBuffer pointer in the BufferItem is
+ // cleared after being queued
+ bool mConsumerIsSurfaceFlinger;
+
// This saves the fence from the last queueBuffer, such that the
// next queueBuffer call can throttle buffer production. The prior
// queueBuffer's fence is not nessessarily available elsewhere,
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 0490c3c..891290b 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -17,19 +17,23 @@
#ifndef ANDROID_GUI_CONSUMERBASE_H
#define ANDROID_GUI_CONSUMERBASE_H
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/OccupancyTracker.h>
-#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/threads.h>
-#include <gui/IConsumerListener.h>
+
namespace android {
// ----------------------------------------------------------------------------
class String8;
+class GraphicBuffer;
// ConsumerBase is a base class for BufferQueue consumer end-points. It
// handles common tasks like management of the connection to the BufferQueue
@@ -63,11 +67,11 @@
// log messages.
void setName(const String8& name);
- // dump writes the current state to a string. Child classes should add
+ // dumpState writes the current state to a string. Child classes should add
// their state to the dump by overriding the dumpLocked method, which is
// called by these methods after locking the mutex.
- void dump(String8& result) const;
- void dump(String8& result, const char* prefix) const;
+ void dumpState(String8& result) const;
+ void dumpState(String8& result, const char* prefix) const;
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
@@ -101,7 +105,7 @@
// buffers from the given IGraphicBufferConsumer.
// The controlledByApp flag indicates that this consumer is under the application's
// control.
- ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+ explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
// onLastStrongRef gets called by RefBase just before the dtor of the most
// derived class. It is used to clean up the buffers so that ConsumerBase
@@ -180,7 +184,7 @@
// Derived classes should override this method to perform any cleanup that
// must take place when a buffer is released back to the BufferQueue. If
// it is overridden the derived class's implementation must call
- // ConsumerBase::releaseBufferLocked.e
+ // ConsumerBase::releaseBufferLocked.
virtual status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer,
EGLDisplay display, EGLSyncKHR eglFence);
@@ -222,7 +226,7 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
- Slot mSlots[BufferQueue::NUM_BUFFER_SLOTS];
+ Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
// mAbandoned indicates that the BufferQueue will no longer be used to
// consume images buffers pushed to it using the IGraphicBufferProducer
@@ -237,13 +241,19 @@
// mFrameAvailableListener is the listener object that will be called when a
// new frame becomes available. If it is not NULL it will be called from
- // queueBuffer.
+ // queueBuffer. The listener object is protected by mFrameAvailableMutex
+ // (not mMutex).
+ Mutex mFrameAvailableMutex;
wp<FrameAvailableListener> mFrameAvailableListener;
// The ConsumerBase has-a BufferQueue and is responsible for creating this object
// if none is supplied
sp<IGraphicBufferConsumer> mConsumer;
+ // The final release fence of the most recent buffer released by
+ // releaseBufferLocked.
+ sp<Fence> mPrevFinalReleaseFence;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables of ConsumerBase objects. It must be locked whenever the
// member variables are accessed or when any of the *Locked methods are
diff --git a/include/gui/CpuConsumer.h b/include/gui/CpuConsumer.h
index b7aa463..58602bf 100644
--- a/include/gui/CpuConsumer.h
+++ b/include/gui/CpuConsumer.h
@@ -17,18 +17,19 @@
#ifndef ANDROID_GUI_CPUCONSUMER_H
#define ANDROID_GUI_CPUCONSUMER_H
+#include <system/window.h>
+
#include <gui/ConsumerBase.h>
+#include <gui/BufferQueue.h>
-#include <ui/GraphicBuffer.h>
-
-#include <utils/String8.h>
#include <utils/Vector.h>
-#include <utils/threads.h>
namespace android {
class BufferQueue;
+class GraphicBuffer;
+class String8;
/**
* CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU
diff --git a/include/gui/DisplayEventReceiver.h b/include/gui/DisplayEventReceiver.h
index a4718b9..32ce59a 100644
--- a/include/gui/DisplayEventReceiver.h
+++ b/include/gui/DisplayEventReceiver.h
@@ -25,6 +25,7 @@
#include <utils/Timers.h>
#include <binder/IInterface.h>
+#include <gui/ISurfaceComposer.h>
// ----------------------------------------------------------------------------
@@ -32,16 +33,25 @@
// ----------------------------------------------------------------------------
-class BitTube;
class IDisplayEventConnection;
-// ----------------------------------------------------------------------------
+namespace gui {
+class BitTube;
+} // namespace gui
+static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) {
+ return static_cast<uint32_t>(c1) << 24 |
+ static_cast<uint32_t>(c2) << 16 |
+ static_cast<uint32_t>(c3) << 8 |
+ static_cast<uint32_t>(c4);
+}
+
+// ----------------------------------------------------------------------------
class DisplayEventReceiver {
public:
enum {
- DISPLAY_EVENT_VSYNC = 'vsyn',
- DISPLAY_EVENT_HOTPLUG = 'plug'
+ DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
+ DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
};
struct Event {
@@ -74,7 +84,8 @@
* or requestNextVsync to receive them.
* Other events start being delivered immediately.
*/
- DisplayEventReceiver();
+ DisplayEventReceiver(
+ ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp);
/*
* ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
@@ -102,15 +113,13 @@
* should be destroyed and getEvents() shouldn't be called again.
*/
ssize_t getEvents(Event* events, size_t count);
- static ssize_t getEvents(const sp<BitTube>& dataChannel,
- Event* events, size_t count);
+ static ssize_t getEvents(gui::BitTube* dataChannel, Event* events, size_t count);
/*
* sendEvents write events to the queue and returns how many events were
* written.
*/
- static ssize_t sendEvents(const sp<BitTube>& dataChannel,
- Event const* events, size_t count);
+ static ssize_t sendEvents(gui::BitTube* dataChannel, Event const* events, size_t count);
/*
* setVsyncRate() sets the Event::VSync delivery rate. A value of
@@ -128,7 +137,7 @@
private:
sp<IDisplayEventConnection> mEventConnection;
- sp<BitTube> mDataChannel;
+ std::unique_ptr<gui::BitTube> mDataChannel;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
index 4dc7467..9716be4 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -17,29 +17,321 @@
#ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
#define ANDROID_GUI_FRAMETIMESTAMPS_H
-#include <utils/Timers.h>
+#include <ui/FenceTime.h>
#include <utils/Flattenable.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <array>
+#include <bitset>
+#include <vector>
namespace android {
-struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
- FrameTimestamps() :
- frameNumber(0),
- postedTime(0),
- acquireTime(0),
- refreshStartTime(0),
- glCompositionDoneTime(0),
- displayRetireTime(0),
- releaseTime(0) {}
+struct FrameEvents;
+class FrameEventHistoryDelta;
+class String8;
- uint64_t frameNumber;
- nsecs_t postedTime;
- nsecs_t acquireTime;
- nsecs_t refreshStartTime;
- nsecs_t glCompositionDoneTime;
- nsecs_t displayRetireTime;
- nsecs_t releaseTime;
+
+// Identifiers for all the events that may be recorded or reported.
+enum class FrameEvent {
+ POSTED,
+ REQUESTED_PRESENT,
+ LATCH,
+ ACQUIRE,
+ FIRST_REFRESH_START,
+ LAST_REFRESH_START,
+ GPU_COMPOSITION_DONE,
+ DISPLAY_PRESENT,
+ DEQUEUE_READY,
+ RELEASE,
+ EVENT_COUNT, // Not an actual event.
};
+
+// A collection of timestamps corresponding to a single frame.
+struct FrameEvents {
+ static constexpr auto EVENT_COUNT =
+ static_cast<size_t>(FrameEvent::EVENT_COUNT);
+ static_assert(EVENT_COUNT <= 32, "Event count sanity check failed.");
+ static constexpr nsecs_t TIMESTAMP_PENDING = -2;
+
+ static inline bool isValidTimestamp(nsecs_t time) {
+ return time != TIMESTAMP_PENDING;
+ }
+
+ bool hasPostedInfo() const;
+ bool hasRequestedPresentInfo() const;
+ bool hasLatchInfo() const;
+ bool hasFirstRefreshStartInfo() const;
+ bool hasLastRefreshStartInfo() const;
+ bool hasAcquireInfo() const;
+ bool hasGpuCompositionDoneInfo() const;
+ bool hasDisplayPresentInfo() const;
+ bool hasReleaseInfo() const;
+ bool hasDequeueReadyInfo() const;
+
+ void checkFencesForCompletion();
+ void dump(String8& outString) const;
+
+ bool valid{false};
+ int connectId{0};
+ uint64_t frameNumber{0};
+
+ // Whether or not certain points in the frame's life cycle have been
+ // encountered help us determine if timestamps aren't available because
+ // a) we'll just never get them or b) they're not ready yet.
+ bool addPostCompositeCalled{false};
+ bool addReleaseCalled{false};
+
+ nsecs_t postedTime{TIMESTAMP_PENDING};
+ nsecs_t requestedPresentTime{TIMESTAMP_PENDING};
+ nsecs_t latchTime{TIMESTAMP_PENDING};
+ nsecs_t firstRefreshStartTime{TIMESTAMP_PENDING};
+ nsecs_t lastRefreshStartTime{TIMESTAMP_PENDING};
+ nsecs_t dequeueReadyTime{TIMESTAMP_PENDING};
+
+ std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> displayPresentFence{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE};
+};
+
+struct CompositorTiming {
+ nsecs_t deadline{0};
+ nsecs_t interval{16666667};
+ nsecs_t presentLatency{16666667};
+};
+
+// A short history of frames that are synchronized between the consumer and
+// producer via deltas.
+class FrameEventHistory {
+public:
+ virtual ~FrameEventHistory();
+
+ FrameEvents* getFrame(uint64_t frameNumber);
+ FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint);
+ void checkFencesForCompletion();
+ void dump(String8& outString) const;
+
+ static constexpr size_t MAX_FRAME_HISTORY = 8;
+
+protected:
+ std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames;
+
+ CompositorTiming mCompositorTiming;
+};
+
+
+// The producer's interface to FrameEventHistory
+class ProducerFrameEventHistory : public FrameEventHistory {
+public:
+ ~ProducerFrameEventHistory() override;
+
+ // Public for testing.
+ static nsecs_t snapToNextTick(
+ nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval);
+
+ nsecs_t getNextCompositeDeadline(const nsecs_t now) const;
+ nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; }
+ nsecs_t getCompositeToPresentLatency() const {
+ return mCompositorTiming.presentLatency;
+ }
+
+ // virtual for testing.
+ virtual void updateAcquireFence(
+ uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire);
+ void applyDelta(const FrameEventHistoryDelta& delta);
+
+ void updateSignalTimes();
+
+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
+ // they can coordinate shared timelines better. The producer doesn't have
+ // shared timelines though, so just let it own and update all of them.
+ FenceTimeline mAcquireTimeline;
+ FenceTimeline mGpuCompositionDoneTimeline;
+ FenceTimeline mPresentTimeline;
+ FenceTimeline mReleaseTimeline;
+};
+
+
+// Used by the consumer to create a new frame event record that is
+// partially complete.
+struct NewFrameEventsEntry {
+ uint64_t frameNumber{0};
+ nsecs_t postedTime{0};
+ nsecs_t requestedPresentTime{0};
+ std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
+};
+
+
+// Used by the consumer to keep track of which fields it already sent to
+// the producer.
+class FrameEventDirtyFields {
+public:
+ inline void reset() { mBitset.reset(); }
+ inline bool anyDirty() const { return mBitset.any(); }
+
+ template <FrameEvent event>
+ inline void setDirty() {
+ constexpr size_t eventIndex = static_cast<size_t>(event);
+ static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index.");
+ mBitset.set(eventIndex);
+ }
+
+ template <FrameEvent event>
+ inline bool isDirty() const {
+ constexpr size_t eventIndex = static_cast<size_t>(event);
+ static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index.");
+ return mBitset[eventIndex];
+ }
+
+private:
+ std::bitset<FrameEvents::EVENT_COUNT> mBitset;
+};
+
+
+// The consumer's interface to FrameEventHistory
+class ConsumerFrameEventHistory : public FrameEventHistory {
+public:
+ ~ConsumerFrameEventHistory() override;
+
+ void onDisconnect();
+
+ void initializeCompositorTiming(const CompositorTiming& compositorTiming);
+
+ void addQueue(const NewFrameEventsEntry& newEntry);
+ void addLatch(uint64_t frameNumber, nsecs_t latchTime);
+ void addPreComposition(uint64_t frameNumber, nsecs_t refreshStartTime);
+ void addPostComposition(uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& gpuCompositionDone,
+ const std::shared_ptr<FenceTime>& displayPresent,
+ const CompositorTiming& compositorTiming);
+ void addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime,
+ std::shared_ptr<FenceTime>&& release);
+
+ void getAndResetDelta(FrameEventHistoryDelta* delta);
+
+private:
+ void getFrameDelta(FrameEventHistoryDelta* delta,
+ const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame);
+
+ std::array<FrameEventDirtyFields, MAX_FRAME_HISTORY> mFramesDirty;
+
+ size_t mQueueOffset{0};
+ size_t mCompositionOffset{0};
+ size_t mReleaseOffset{0};
+
+ int mCurrentConnectId{0};
+ bool mProducerWantsEvents{false};
+};
+
+
+// A single frame update from the consumer to producer that can be sent
+// through Binder.
+// Although this may be sent multiple times for the same frame as new
+// timestamps are set, Fences only need to be sent once.
+class FrameEventsDelta : public Flattenable<FrameEventsDelta> {
+friend class ProducerFrameEventHistory;
+public:
+ FrameEventsDelta() = default;
+ FrameEventsDelta(size_t index,
+ const FrameEvents& frameTimestamps,
+ const FrameEventDirtyFields& dirtyFields);
+
+ // Movable.
+ FrameEventsDelta(FrameEventsDelta&& src) = default;
+ FrameEventsDelta& operator=(FrameEventsDelta&& src) = default;
+ // Not copyable.
+ FrameEventsDelta(const FrameEventsDelta& src) = delete;
+ FrameEventsDelta& operator=(const FrameEventsDelta& src) = delete;
+
+ // Flattenable implementation
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+private:
+ static constexpr size_t minFlattenedSize();
+
+ size_t mIndex{0};
+ uint64_t mFrameNumber{0};
+
+ bool mAddPostCompositeCalled{0};
+ bool mAddReleaseCalled{0};
+
+ nsecs_t mPostedTime{FrameEvents::TIMESTAMP_PENDING};
+ nsecs_t mRequestedPresentTime{FrameEvents::TIMESTAMP_PENDING};
+ nsecs_t mLatchTime{FrameEvents::TIMESTAMP_PENDING};
+ nsecs_t mFirstRefreshStartTime{FrameEvents::TIMESTAMP_PENDING};
+ nsecs_t mLastRefreshStartTime{FrameEvents::TIMESTAMP_PENDING};
+ nsecs_t mDequeueReadyTime{FrameEvents::TIMESTAMP_PENDING};
+
+ FenceTime::Snapshot mGpuCompositionDoneFence;
+ FenceTime::Snapshot mDisplayPresentFence;
+ FenceTime::Snapshot mReleaseFence;
+
+ // This is a static method with an auto return value so we can call
+ // it without needing const and non-const versions.
+ template <typename ThisT>
+ static inline auto allFences(ThisT fed) ->
+ std::array<decltype(&fed->mReleaseFence), 3> {
+ return {{
+ &fed->mGpuCompositionDoneFence, &fed->mDisplayPresentFence,
+ &fed->mReleaseFence
+ }};
+ }
+};
+
+
+// A collection of updates from consumer to producer that can be sent
+// through Binder.
+class FrameEventHistoryDelta
+ : public Flattenable<FrameEventHistoryDelta> {
+
+friend class ConsumerFrameEventHistory;
+friend class ProducerFrameEventHistory;
+
+public:
+ FrameEventHistoryDelta() = default;
+
+ // Movable.
+ FrameEventHistoryDelta(FrameEventHistoryDelta&& src) = default;
+ FrameEventHistoryDelta& operator=(FrameEventHistoryDelta&& src);
+ // Not copyable.
+ FrameEventHistoryDelta(const FrameEventHistoryDelta& src) = delete;
+ FrameEventHistoryDelta& operator=(
+ const FrameEventHistoryDelta& src) = delete;
+
+ // Flattenable implementation.
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+private:
+ static constexpr size_t minFlattenedSize();
+
+ std::vector<FrameEventsDelta> mDeltas;
+ CompositorTiming mCompositorTiming;
+};
+
+
} // namespace android
#endif
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 6267625..2cf6162 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -20,10 +20,10 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
+#include <ui/FenceTime.h>
#include <ui/GraphicBuffer.h>
#include <utils/String8.h>
@@ -146,6 +146,10 @@
// documented by the source.
int64_t getTimestamp();
+ // getDataSpace retrieves the DataSpace associated with the texture image
+ // set by the most recent call to updateTexImage.
+ android_dataspace getCurrentDataSpace();
+
// getFrameNumber retrieves the frame number associated with the texture
// image set by the most recent call to updateTexImage.
//
@@ -168,7 +172,9 @@
void setFilteringEnabled(bool enabled);
// getCurrentBuffer returns the buffer associated with the current image.
- sp<GraphicBuffer> getCurrentBuffer() const;
+ // When outSlot is not nullptr, the current buffer slot index is also
+ // returned.
+ sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
// getCurrentTextureTarget returns the texture target of the current
// texture as returned by updateTexImage().
@@ -187,6 +193,10 @@
// ready to be read from.
sp<Fence> getCurrentFence() const;
+ // getCurrentFence returns the FenceTime indicating when the current
+ // buffer is ready to be read from.
+ std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
// doGLFenceWait inserts a wait command into the OpenGL ES command stream
// to ensure that it is safe for future OpenGL ES commands to access the
// current texture buffer.
@@ -250,7 +260,7 @@
// mEglSlots array in addition to the ConsumerBase.
virtual status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer,
- EGLDisplay display, EGLSyncKHR eglFence);
+ EGLDisplay display, EGLSyncKHR eglFence) override;
status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) {
@@ -398,6 +408,9 @@
// mCurrentFence is the fence received from BufferQueue in updateTexImage.
sp<Fence> mCurrentFence;
+ // The FenceTime wrapper around mCurrentFence.
+ std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
// mCurrentTransformMatrix is the transform matrix for the current texture.
// It gets computed by computeTransformMatrix each time updateTexImage is
// called.
@@ -407,6 +420,10 @@
// gets set each time updateTexImage is called.
int64_t mCurrentTimestamp;
+ // mCurrentDataSpace is the dataspace for the current texture. It
+ // gets set each time updateTexImage is called.
+ android_dataspace mCurrentDataSpace;
+
// mCurrentFrameNumber is the frame counter for the current texture.
// It gets set each time updateTexImage is called.
uint64_t mCurrentFrameNumber;
@@ -472,7 +489,7 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
- EglSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
+ EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
diff --git a/include/gui/GraphicBufferAlloc.h b/include/gui/GraphicBufferAlloc.h
deleted file mode 100644
index 62e3877..0000000
--- a/include/gui/GraphicBufferAlloc.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
-#define ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <gui/IGraphicBufferAlloc.h>
-#include <ui/PixelFormat.h>
-#include <utils/Errors.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class GraphicBuffer;
-
-class GraphicBufferAlloc : public BnGraphicBufferAlloc {
-public:
- GraphicBufferAlloc();
- virtual ~GraphicBufferAlloc();
- virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage,
- std::string requestorName, status_t* error) override;
-};
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
diff --git a/include/gui/GraphicsEnv.h b/include/gui/GraphicsEnv.h
deleted file mode 100644
index 4c7366f..0000000
--- a/include/gui/GraphicsEnv.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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/IConsumerListener.h b/include/gui/IConsumerListener.h
index 1efcf3c..c082882 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -14,98 +14,84 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_ICONSUMERLISTENER_H
-#define ANDROID_GUI_ICONSUMERLISTENER_H
+#pragma once
-#include <stdint.h>
-#include <sys/types.h>
+#include <binder/IInterface.h>
+#include <binder/SafeInterface.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-
-#include <gui/FrameTimestamps.h>
+#include <cstdint>
namespace android {
-// ----------------------------------------------------------------------------
class BufferItem;
+class FrameEventHistoryDelta;
+struct NewFrameEventsEntry;
-// ConsumerListener is the interface through which the BufferQueue notifies
-// the consumer of events that the consumer may wish to react to. Because
-// the consumer will generally have a mutex that is locked during calls from
-// the consumer to the BufferQueue, these calls from the BufferQueue to the
+// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
+// that the consumer may wish to react to. Because the consumer will generally have a mutex that is
+// locked during calls from the consumer to the BufferQueue, these calls from the BufferQueue to the
// consumer *MUST* be called only when the BufferQueue mutex is NOT locked.
class ConsumerListener : public virtual RefBase {
public:
- ConsumerListener() { }
- virtual ~ConsumerListener() { }
+ ConsumerListener() {}
+ virtual ~ConsumerListener();
- // onFrameAvailable is called from queueBuffer each time an additional
- // frame becomes available for consumption. This means that frames that
- // are queued while in asynchronous mode only trigger the callback if no
- // previous frames are pending. Frames queued while in synchronous mode
- // always trigger the callback. The item passed to the callback will contain
- // all of the information about the queued frame except for its
- // GraphicBuffer pointer, which will always be null.
+ // onDisconnect is called when a producer disconnects from the BufferQueue.
+ virtual void onDisconnect() {} /* Asynchronous */
+
+ // onFrameAvailable is called from queueBuffer each time an additional frame becomes available
+ // for consumption. This means that frames that are queued while in asynchronous mode only
+ // trigger the callback if no previous frames are pending. Frames queued while in synchronous
+ // mode always trigger the callback. The item passed to the callback will contain all of the
+ // information about the queued frame except for its GraphicBuffer pointer, which will always be
+ // null (except if the consumer is SurfaceFlinger).
//
- // This is called without any lock held and can be called concurrently
- // by multiple threads.
+ // This is called without any lock held and can be called concurrently by multiple threads.
virtual void onFrameAvailable(const BufferItem& item) = 0; /* Asynchronous */
- // onFrameReplaced is called from queueBuffer if the frame being queued is
- // replacing an existing slot in the queue. Any call to queueBuffer that
- // doesn't call onFrameAvailable will call this callback instead. The item
- // passed to the callback will contain all of the information about the
- // queued frame except for its GraphicBuffer pointer, which will always be
- // null.
+ // onFrameReplaced is called from queueBuffer if the frame being queued is replacing an existing
+ // slot in the queue. Any call to queueBuffer that doesn't call onFrameAvailable will call this
+ // callback instead. The item passed to the callback will contain all of the information about
+ // the queued frame except for its GraphicBuffer pointer, which will always be null.
//
- // This is called without any lock held and can be called concurrently
- // by multiple threads.
+ // This is called without any lock held and can be called concurrently by multiple threads.
virtual void onFrameReplaced(const BufferItem& /* item */) {} /* Asynchronous */
- // onBuffersReleased is called to notify the buffer consumer that the
- // BufferQueue has released its references to one or more GraphicBuffers
- // contained in its slots. The buffer consumer should then call
- // BufferQueue::getReleasedBuffers to retrieve the list of buffers
+ // onBuffersReleased is called to notify the buffer consumer that the BufferQueue has released
+ // its references to one or more GraphicBuffers contained in its slots. The buffer consumer
+ // should then call BufferQueue::getReleasedBuffers to retrieve the list of buffers.
//
- // This is called without any lock held and can be called concurrently
- // by multiple threads.
+ // This is called without any lock held and can be called concurrently by multiple threads.
virtual void onBuffersReleased() = 0; /* Asynchronous */
- // onSidebandStreamChanged is called to notify the buffer consumer that the
- // BufferQueue's sideband buffer stream has changed. This is called when a
- // stream is first attached and when it is either detached or replaced by a
- // different stream.
+ // onSidebandStreamChanged is called to notify the buffer consumer that the BufferQueue's
+ // sideband buffer stream has changed. This is called when a stream is first attached and when
+ // it is either detached or replaced by a different stream.
virtual void onSidebandStreamChanged() = 0; /* Asynchronous */
- // See IGraphicBufferProducer::getFrameTimestamps
- // This queries the consumer for the timestamps
- virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
- FrameTimestamps* /*outTimestamps*/) const { return false; }
+ // Notifies the consumer of any new producer-side timestamps and returns the combined frame
+ // history that hasn't already been retrieved.
+ //
+ // WARNING: This method can only be called when the BufferQueue is in the consumer's process.
+ virtual void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/,
+ FrameEventHistoryDelta* /*outDelta*/) {}
};
-
-class IConsumerListener : public ConsumerListener, public IInterface
-{
+class IConsumerListener : public ConsumerListener, public IInterface {
public:
- DECLARE_META_INTERFACE(ConsumerListener);
+ DECLARE_META_INTERFACE(ConsumerListener)
};
-// ----------------------------------------------------------------------------
-
-class BnConsumerListener : public BnInterface<IConsumerListener>
-{
+class BnConsumerListener : public SafeBnInterface<IConsumerListener> {
public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
+ BnConsumerListener() : SafeBnInterface<IConsumerListener>("BnConsumerListener") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
};
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_ICONSUMERLISTENER_H
+} // namespace android
diff --git a/include/gui/IDisplayEventConnection.h b/include/gui/IDisplayEventConnection.h
index 86247de..d783f74 100644
--- a/include/gui/IDisplayEventConnection.h
+++ b/include/gui/IDisplayEventConnection.h
@@ -14,60 +14,52 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
-#define ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
+#pragma once
#include <binder/IInterface.h>
+#include <binder/SafeInterface.h>
+
+#include <utils/Errors.h>
+
+#include <cstdint>
namespace android {
-// ----------------------------------------------------------------------------
+namespace gui {
class BitTube;
+} // namespace gui
-class IDisplayEventConnection : public IInterface
-{
+class IDisplayEventConnection : public IInterface {
public:
-
- DECLARE_META_INTERFACE(DisplayEventConnection);
+ DECLARE_META_INTERFACE(DisplayEventConnection)
/*
- * getDataChannel() returns a BitTube where to receive the events from
+ * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
+ * descriptor of outChannel will be initialized, and this effectively "steals" the receive
+ * channel from the remote end (such that the remote end can only use its send channel).
*/
- virtual sp<BitTube> getDataChannel() const = 0;
+ virtual status_t stealReceiveChannel(gui::BitTube* outChannel) = 0;
/*
- * setVsyncRate() sets the vsync event delivery rate. A value of
- * 1 returns every vsync events. A value of 2 returns every other events,
- * etc... a value of 0 returns no event unless requestNextVsync() has
- * been called.
+ * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
+ * A value of 2 returns every other event, etc. A value of 0 returns no event unless
+ * requestNextVsync() has been called.
*/
- virtual void setVsyncRate(uint32_t count) = 0;
+ virtual status_t setVsyncRate(uint32_t count) = 0;
/*
- * requestNextVsync() schedules the next vsync event. It has no effect
- * if the vsync rate is > 0.
+ * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
*/
- virtual void requestNextVsync() = 0; // asynchronous
+ virtual void requestNextVsync() = 0; // Asynchronous
};
-// ----------------------------------------------------------------------------
-
-class BnDisplayEventConnection : public BnInterface<IDisplayEventConnection>
-{
+class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
+ BnDisplayEventConnection()
+ : SafeBnInterface<IDisplayEventConnection>("BnDisplayEventConnection") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
};
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
+} // namespace android
diff --git a/include/gui/IGraphicBufferAlloc.h b/include/gui/IGraphicBufferAlloc.h
deleted file mode 100644
index 600cf27..0000000
--- a/include/gui/IGraphicBufferAlloc.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_IGRAPHIC_BUFFER_ALLOC_H
-#define ANDROID_GUI_IGRAPHIC_BUFFER_ALLOC_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/PixelFormat.h>
-#include <utils/RefBase.h>
-
-#include <string>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class IGraphicBufferAlloc : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(GraphicBufferAlloc);
-
- /* Create a new GraphicBuffer for the client to use.
- */
- virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage, std::string requestorName,
- status_t* error) = 0;
-
- sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage, status_t* error) {
- return createGraphicBuffer(w, h, format, usage, "<Unknown>", error);
- }
-};
-
-// ----------------------------------------------------------------------------
-
-class BnGraphicBufferAlloc : public BnInterface<IGraphicBufferAlloc>
-{
-public:
- virtual status_t onTransact(uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_IGRAPHIC_BUFFER_ALLOC_H
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index 3b10d78..3d069df 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -14,26 +14,21 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
-#define ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
+#pragma once
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
+#include <gui/OccupancyTracker.h>
#include <binder/IInterface.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
-#include <gui/OccupancyTracker.h>
+#include <binder/SafeInterface.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <ui/PixelFormat.h>
+
+#include <utils/Errors.h>
+
namespace android {
-// ----------------------------------------------------------------------------
class BufferItem;
class Fence;
@@ -42,11 +37,12 @@
class NativeHandle;
class IGraphicBufferConsumer : public IInterface {
-
public:
+ DECLARE_META_INTERFACE(GraphicBufferConsumer)
+
enum {
- // Returned by releaseBuffer, after which the consumer must
- // free any references to the just-released buffer that it might have.
+ // Returned by releaseBuffer, after which the consumer must free any references to the
+ // just-released buffer that it might have.
STALE_BUFFER_SLOT = 1,
// Returned by dequeueBuffer if there are no pending buffers available.
NO_BUFFER_AVAILABLE,
@@ -54,88 +50,79 @@
PRESENT_LATER,
};
- // acquireBuffer attempts to acquire ownership of the next pending buffer in
- // the BufferQueue. If no buffer is pending then it returns
- // NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the
- // information about the buffer is returned in BufferItem.
+ // acquireBuffer attempts to acquire ownership of the next pending buffer in the BufferQueue.
+ // If no buffer is pending then it returns NO_BUFFER_AVAILABLE. If a buffer is successfully
+ // acquired, the information about the buffer is returned in BufferItem.
//
- // If the buffer returned had previously been
- // acquired then the BufferItem::mGraphicBuffer field of buffer is set to
- // NULL and it is assumed that the consumer still holds a reference to the
+ // If the buffer returned had previously been acquired then the BufferItem::mGraphicBuffer field
+ // of buffer is set to NULL and it is assumed that the consumer still holds a reference to the
// buffer.
//
- // If presentWhen is non-zero, it indicates the time when the buffer will
- // be displayed on screen. If the buffer's timestamp is farther in the
- // future, the buffer won't be acquired, and PRESENT_LATER will be
- // returned. The presentation time is in nanoseconds, and the time base
+ // If presentWhen is non-zero, it indicates the time when the buffer will be displayed on
+ // screen. If the buffer's timestamp is farther in the future, the buffer won't be acquired, and
+ // PRESENT_LATER will be returned. The presentation time is in nanoseconds, and the time base
// is CLOCK_MONOTONIC.
//
- // If maxFrameNumber is non-zero, it indicates that acquireBuffer should
- // only return a buffer with a frame number less than or equal to
- // maxFrameNumber. If no such frame is available (such as when a buffer has
- // been replaced but the consumer has not received the onFrameReplaced
- // callback), then PRESENT_LATER will be returned.
+ // If maxFrameNumber is non-zero, it indicates that acquireBuffer should only return a buffer
+ // with a frame number less than or equal to maxFrameNumber. If no such frame is available
+ // (such as when a buffer has been replaced but the consumer has not received the
+ // onFrameReplaced callback), then PRESENT_LATER will be returned.
//
// Return of NO_ERROR means the operation completed as normal.
//
- // Return of a positive value means the operation could not be completed
- // at this time, but the user should try again later:
+ // Return of a positive value means the operation could not be completed at this time, but the
+ // user should try again later:
// * NO_BUFFER_AVAILABLE - no buffer is pending (nothing queued by producer)
// * PRESENT_LATER - the buffer's timestamp is farther in the future
//
// Return of a negative value means an error has occurred:
// * INVALID_OPERATION - too many buffers have been acquired
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
- uint64_t maxFrameNumber = 0) = 0;
+ uint64_t maxFrameNumber = 0) = 0;
- // detachBuffer attempts to remove all ownership of the buffer in the given
- // slot from the buffer queue. If this call succeeds, the slot will be
- // freed, and there will be no way to obtain the buffer from this interface.
- // The freed slot will remain unallocated until either it is selected to
- // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached
- // to the slot. The buffer must have already been acquired.
+ // detachBuffer attempts to remove all ownership of the buffer in the given slot from the buffer
+ // queue. If this call succeeds, the slot will be freed, and there will be no way to obtain the
+ // buffer from this interface. The freed slot will remain unallocated until either it is
+ // selected to hold a freshly allocated buffer in dequeueBuffer or a buffer is attached to the
+ // slot. The buffer must have already been acquired.
//
// Return of a value other than NO_ERROR means an error has occurred:
- // * BAD_VALUE - the given slot number is invalid, either because it is
- // out of the range [0, NUM_BUFFER_SLOTS) or because the slot
- // it refers to is not currently acquired.
+ // * BAD_VALUE - the given slot number is invalid, either because it is out of the range
+ // [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not
+ // currently acquired.
virtual status_t detachBuffer(int slot) = 0;
- // attachBuffer attempts to transfer ownership of a buffer to the buffer
- // queue. If this call succeeds, it will be as if this buffer was acquired
- // from the returned slot number. As such, this call will fail if attaching
- // this buffer would cause too many buffers to be simultaneously acquired.
+ // attachBuffer attempts to transfer ownership of a buffer to the BufferQueue. If this call
+ // succeeds, it will be as if this buffer was acquired from the returned slot number. As such,
+ // this call will fail if attaching this buffer would cause too many buffers to be
+ // simultaneously acquired.
//
- // If the buffer is successfully attached, its frameNumber is initialized
- // to 0. This must be passed into the releaseBuffer call or else the buffer
- // will be deallocated as stale.
+ // If the buffer is successfully attached, its frameNumber is initialized to 0. This must be
+ // passed into the releaseBuffer call or else the buffer will be deallocated as stale.
//
// Return of a value other than NO_ERROR means an error has occurred:
- // * BAD_VALUE - outSlot or buffer were NULL, or the generation number of
- // the buffer did not match the buffer queue.
- // * INVALID_OPERATION - cannot attach the buffer because it would cause too
- // many buffers to be acquired.
+ // * BAD_VALUE - outSlot or buffer were NULL, or the generation number of the buffer did not
+ // match the BufferQueue.
+ // * INVALID_OPERATION - cannot attach the buffer because it would cause too many buffers
+ // to be acquired.
// * NO_MEMORY - no free slots available
- virtual status_t attachBuffer(int *outSlot,
- const sp<GraphicBuffer>& buffer) = 0;
+ virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) = 0;
- // releaseBuffer releases a buffer slot from the consumer back to the
- // BufferQueue. This may be done while the buffer's contents are still
- // being accessed. The fence will signal when the buffer is no longer
- // in use. frameNumber is used to indentify the exact buffer returned.
+ // releaseBuffer releases a buffer slot from the consumer back to the BufferQueue. This may be
+ // done while the buffer's contents are still being accessed. The fence will signal when the
+ // buffer is no longer in use. frameNumber is used to identify the exact buffer returned.
//
- // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
- // any references to the just-released buffer that it might have, as if it
- // had received a onBuffersReleased() call with a mask set for the released
- // buffer.
+ // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free any references to the
+ // just-released buffer that it might have, as if it had received a onBuffersReleased() call
+ // with a mask set for the released buffer.
//
- // Note that the dependencies on EGL will be removed once we switch to using
- // the Android HW Sync HAL.
+ // Note that the dependencies on EGL will be removed once we switch to using the Android HW
+ // Sync HAL.
//
// Return of NO_ERROR means the operation completed as normal.
//
- // Return of a positive value means the operation could not be completed
- // at this time, but the user should try again later:
+ // Return of a positive value means the operation could not be completed at this time, but the
+ // user should try again later:
// * STALE_BUFFER_SLOT - see above (second paragraph)
//
// Return of a negative value means an error has occurred:
@@ -143,159 +130,165 @@
// * the buffer slot was invalid
// * the fence was NULL
// * the buffer slot specified is not in the acquired state
- virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
- EGLDisplay display, EGLSyncKHR fence,
- const sp<Fence>& releaseFence) = 0;
+ virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display,
+ EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0;
- // consumerConnect connects a consumer to the BufferQueue. Only one
- // consumer may be connected, and when that consumer disconnects the
- // BufferQueue is placed into the "abandoned" state, causing most
- // interactions with the BufferQueue by the producer to fail.
- // controlledByApp indicates whether the consumer is controlled by
- // the application.
+ status_t releaseHelper(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) {
+ return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
+ }
+ // This is explicitly *not* the actual signature of IGBC::releaseBuffer, but:
+ // 1) We have no easy way to send the EGL objects across Binder
+ // 2) This has always been broken, probably because
+ // 3) IGBC is rarely remoted
+ // For now, we will choose to bury our heads in the sand and ignore this problem until such time
+ // as we can finally finish converting away from EGL sync to native Android sync
+ using ReleaseBuffer = decltype(&IGraphicBufferConsumer::releaseHelper);
+
+ // consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected,
+ // and when that consumer disconnects the BufferQueue is placed into the "abandoned" state,
+ // causing most interactions with the BufferQueue by the producer to fail. controlledByApp
+ // indicates whether the consumer is controlled by the application.
//
// consumer may not be NULL.
//
// Return of a value other than NO_ERROR means an error has occurred:
- // * NO_INIT - the buffer queue has been abandoned
+ // * NO_INIT - the BufferQueue has been abandoned
// * BAD_VALUE - a NULL consumer was provided
- virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0;
+ virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
+ bool controlledByApp) = 0;
- // consumerDisconnect disconnects a consumer from the BufferQueue. All
- // buffers will be freed and the BufferQueue is placed in the "abandoned"
- // state, causing most interactions with the BufferQueue by the producer to
- // fail.
+ // consumerDisconnect disconnects a consumer from the BufferQueue. All buffers will be freed and
+ // the BufferQueue is placed in the "abandoned" state, causing most interactions with the
+ // BufferQueue by the producer to fail.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - no consumer is currently connected
virtual status_t consumerDisconnect() = 0;
- // getReleasedBuffers sets the value pointed to by slotMask to a bit set.
- // Each bit index with a 1 corresponds to a released buffer slot with that
- // index value. In particular, a released buffer is one that has
- // been released by the BufferQueue but have not yet been released by the consumer.
+ // getReleasedBuffers sets the value pointed to by slotMask to a bit set. Each bit index with a
+ // 1 corresponds to a released buffer slot with that index value. In particular, a released
+ // buffer is one that has been released by the BufferQueue but has not yet been released by
+ // the consumer.
//
// This should be called from the onBuffersReleased() callback.
//
// Return of a value other than NO_ERROR means an error has occurred:
- // * NO_INIT - the buffer queue has been abandoned.
+ // * NO_INIT - the BufferQueue has been abandoned.
virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
- // setDefaultBufferSize is used to set the size of buffers returned by
- // dequeueBuffer when a width and height of zero is requested. Default
- // is 1x1.
+ // setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a
+ // width and height of zero is requested. Default is 1x1.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - either w or h was zero
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
- // setMaxBufferCount sets the maximum value for the number of buffers used
- // in the buffer queue (the initial default is NUM_BUFFER_SLOTS). If a call
- // to setMaxAcquiredBufferCount (by the consumer), or a call to setAsyncMode
- // or setMaxDequeuedBufferCount (by the producer), would cause this value to
- // be exceeded then that call will fail. This call will fail if a producer
+ // setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue
+ // (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the
+ // consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would
+ // cause this value to be exceeded then that call will fail. This call will fail if a producer
// is connected to the BufferQueue.
//
- // The count must be between 1 and NUM_BUFFER_SLOTS, inclusive. The count
- // cannot be less than maxAcquiredBufferCount.
+ // The count must be between 1 and NUM_BUFFER_SLOTS, inclusive. The count cannot be less than
+ // maxAcquiredBufferCount.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - one of the below conditions occurred:
- // * bufferCount was out of range (see above).
- // * failure to adjust the number of available slots.
+ // * bufferCount was out of range (see above).
+ // * failure to adjust the number of available slots.
// * INVALID_OPERATION - attempting to call this after a producer connected.
virtual status_t setMaxBufferCount(int bufferCount) = 0;
- // setMaxAcquiredBufferCount sets the maximum number of buffers that can
- // be acquired by the consumer at one time (default 1). If this method
- // succeeds, any new buffer slots will be both unallocated and owned by the
- // BufferQueue object (i.e. they are not owned by the producer or consumer).
- // Calling this may also cause some buffer slots to be emptied.
+ // setMaxAcquiredBufferCount sets the maximum number of buffers that can be acquired by the
+ // consumer at one time (default 1). If this method succeeds, any new buffer slots will be both
+ // unallocated and owned by the BufferQueue object (i.e. they are not owned by the producer or
+ // consumer). Calling this may also cause some buffer slots to be emptied.
//
- // This function should not be called with a value of maxAcquiredBuffers
- // that is less than the number of currently acquired buffer slots. Doing so
- // will result in a BAD_VALUE error.
+ // This function should not be called with a value of maxAcquiredBuffers that is less than the
+ // number of currently acquired buffer slots. Doing so will result in a BAD_VALUE error.
//
- // maxAcquiredBuffers must be (inclusive) between 1 and
- // MAX_MAX_ACQUIRED_BUFFERS. It also cannot cause the maxBufferCount value
- // to be exceeded.
+ // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot
+ // cause the maxBufferCount value to be exceeded.
//
// Return of a value other than NO_ERROR means an error has occurred:
- // * NO_INIT - the buffer queue has been abandoned
+ // * NO_INIT - the BufferQueue has been abandoned
// * BAD_VALUE - one of the below conditions occurred:
- // * maxAcquiredBuffers was out of range (see above).
- // * failure to adjust the number of available slots.
- // * client would have more than the requested number of
- // acquired buffers after this call
+ // * maxAcquiredBuffers was out of range (see above).
+ // * failure to adjust the number of available slots.
+ // * client would have more than the requested number of acquired buffers after
+ // this call
// * INVALID_OPERATION - attempting to call this after a producer connected.
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
// setConsumerName sets the name used in logging
- virtual void setConsumerName(const String8& name) = 0;
+ virtual status_t setConsumerName(const String8& name) = 0;
- // setDefaultBufferFormat allows the BufferQueue to create
- // GraphicBuffers of a defaultFormat if no format is specified
- // in dequeueBuffer.
- // The initial default is PIXEL_FORMAT_RGBA_8888.
+ // setDefaultBufferFormat allows the BufferQueue to create GraphicBuffers of a defaultFormat if
+ // no format is specified in dequeueBuffer. The initial default is PIXEL_FORMAT_RGBA_8888.
//
// Return of a value other than NO_ERROR means an unknown error has occurred.
virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) = 0;
- // setDefaultBufferDataSpace is a request to the producer to provide buffers
- // of the indicated dataSpace. The producer may ignore this request.
- // The initial default is HAL_DATASPACE_UNKNOWN.
+ // setDefaultBufferDataSpace is a request to the producer to provide buffers of the indicated
+ // dataSpace. The producer may ignore this request. The initial default is
+ // HAL_DATASPACE_UNKNOWN.
//
// Return of a value other than NO_ERROR means an unknown error has occurred.
- virtual status_t setDefaultBufferDataSpace(
- android_dataspace defaultDataSpace) = 0;
+ virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) = 0;
- // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
- // These are merged with the bits passed to dequeueBuffer. The values are
- // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
+ // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. These are merged
+ // with the bits passed to dequeueBuffer. The values are enumerated in gralloc.h,
+ // e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
//
// Return of a value other than NO_ERROR means an unknown error has occurred.
virtual status_t setConsumerUsageBits(uint32_t usage) = 0;
- // setTransformHint bakes in rotation to buffers so overlays can be used.
- // The values are enumerated in window.h, e.g.
- // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
+ // setConsumerIsProtected will turn on an internal bit that indicates whether
+ // the consumer can handle protected gralloc buffers (i.e. with
+ // GRALLOC_USAGE_PROTECTED set). IGraphicBufferProducer can query this
+ // capability using NATIVE_WINDOW_CONSUMER_IS_PROTECTED.
+ virtual status_t setConsumerIsProtected(bool isProtected) = 0;
+
+ // setTransformHint bakes in rotation to buffers so overlays can be used. The values are
+ // enumerated in window.h, e.g. NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0
+ // (no transform).
//
// Return of a value other than NO_ERROR means an unknown error has occurred.
virtual status_t setTransformHint(uint32_t hint) = 0;
// Retrieve the sideband buffer stream, if any.
- virtual sp<NativeHandle> getSidebandStream() const = 0;
+ virtual status_t getSidebandStream(sp<NativeHandle>* outStream) const = 0;
- // Retrieves any stored segments of the occupancy history of this
- // BufferQueue and clears them. Optionally closes out the pending segment if
- // forceFlush is true.
+ // Retrieves any stored segments of the occupancy history of this BufferQueue and clears them.
+ // Optionally closes out the pending segment if forceFlush is true.
virtual status_t getOccupancyHistory(bool forceFlush,
- std::vector<OccupancyTracker::Segment>* outHistory) = 0;
+ std::vector<OccupancyTracker::Segment>* outHistory) = 0;
- // discardFreeBuffers releases all currently-free buffers held by the queue,
- // in order to reduce the memory consumption of the queue to the minimum
- // possible without discarding data.
+ // discardFreeBuffers releases all currently-free buffers held by the BufferQueue, in order to
+ // reduce the memory consumption of the BufferQueue to the minimum possible without
+ // discarding data.
+ // The consumer invoking this method is responsible for calling getReleasedBuffers() after this
+ // call to free up any of its locally cached buffers.
virtual status_t discardFreeBuffers() = 0;
// dump state into a string
- virtual void dump(String8& result, const char* prefix) const = 0;
+ virtual status_t dumpState(const String8& prefix, String8* outResult) const = 0;
-public:
- DECLARE_META_INTERFACE(GraphicBufferConsumer);
+ // Provide backwards source compatibility
+ void dumpState(String8& result, const char* prefix) {
+ String8 returned;
+ dumpState(String8(prefix), &returned);
+ result.append(returned);
+ }
};
-// ----------------------------------------------------------------------------
-
-class BnGraphicBufferConsumer : public BnInterface<IGraphicBufferConsumer>
-{
+class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> {
public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
+ BnGraphicBufferConsumer()
+ : SafeBnInterface<IGraphicBufferConsumer>("BnGraphicBufferConsumer") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
};
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
+} // namespace android
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index bf427fe..9250806 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -32,12 +32,17 @@
#include <gui/FrameTimestamps.h>
+#include <hidl/HybridInterface.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
namespace android {
// ----------------------------------------------------------------------------
class IProducerListener;
class NativeHandle;
class Surface;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+ HGraphicBufferProducer;
/*
* This class defines the Binder IPC interface for the producer side of
@@ -56,7 +61,7 @@
class IGraphicBufferProducer : public IInterface
{
public:
- DECLARE_META_INTERFACE(GraphicBufferProducer);
+ DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer)
enum {
// A flag returned by dequeueBuffer when the client needs to call
@@ -190,7 +195,8 @@
// All other negative values are an unknown error returned downstream
// from the graphics allocator (typically errno).
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t usage) = 0;
+ uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) = 0;
// detachBuffer attempts to remove all ownership of the buffer in the given
// slot from the buffer queue. If this call succeeds, the slot will be
@@ -294,7 +300,8 @@
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
friend class Flattenable<QueueBufferInput>;
- inline QueueBufferInput(const Parcel& parcel);
+ explicit inline QueueBufferInput(const Parcel& parcel);
+
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
// dataSpace - description of the contents, interpretation depends on format
@@ -305,18 +312,23 @@
// set this to Fence::NO_FENCE if the buffer is ready immediately
// sticky - the sticky transform set in Surface (only used by the LEGACY
// camera mode).
- inline QueueBufferInput(int64_t timestamp, bool isAutoTimestamp,
- android_dataspace dataSpace, const Rect& crop, int scalingMode,
- uint32_t transform, const sp<Fence>& fence, uint32_t sticky = 0)
- : timestamp(timestamp), isAutoTimestamp(isAutoTimestamp),
- dataSpace(dataSpace), crop(crop), scalingMode(scalingMode),
- transform(transform), stickyTransform(sticky), fence(fence),
- surfaceDamage() { }
+ // getFrameTimestamps - whether or not the latest frame timestamps
+ // should be retrieved from the consumer.
+ inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp,
+ android_dataspace _dataSpace, const Rect& _crop,
+ int _scalingMode, uint32_t _transform, const sp<Fence>& _fence,
+ uint32_t _sticky = 0, bool _getFrameTimestamps = false)
+ : timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
+ dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
+ transform(_transform), stickyTransform(_sticky), fence(_fence),
+ surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { }
+
inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
android_dataspace* outDataSpace,
Rect* outCrop, int* outScalingMode,
uint32_t* outTransform, sp<Fence>* outFence,
- uint32_t* outStickyTransform = NULL) const {
+ uint32_t* outStickyTransform = nullptr,
+ bool* outGetFrameTimestamps = nullptr) const {
*outTimestamp = timestamp;
*outIsAutoTimestamp = bool(isAutoTimestamp);
*outDataSpace = dataSpace;
@@ -327,9 +339,13 @@
if (outStickyTransform != NULL) {
*outStickyTransform = stickyTransform;
}
+ if (outGetFrameTimestamps) {
+ *outGetFrameTimestamps = getFrameTimestamps;
+ }
}
// Flattenable protocol
+ static constexpr size_t minFlattenedSize();
size_t getFlattenedSize() const;
size_t getFdCount() const;
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
@@ -339,51 +355,42 @@
void setSurfaceDamage(const Region& damage) { surfaceDamage = damage; }
private:
- int64_t timestamp;
- int isAutoTimestamp;
- android_dataspace dataSpace;
+ int64_t timestamp{0};
+ int isAutoTimestamp{0};
+ android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN};
Rect crop;
- int scalingMode;
- uint32_t transform;
- uint32_t stickyTransform;
+ int scalingMode{0};
+ uint32_t transform{0};
+ uint32_t stickyTransform{0};
sp<Fence> fence;
Region surfaceDamage;
+ bool getFrameTimestamps{false};
};
- // QueueBufferOutput must be a POD structure
- struct __attribute__ ((__packed__)) QueueBufferOutput {
- inline QueueBufferOutput() { }
- // outWidth - filled with default width applied to the buffer
- // outHeight - filled with default height applied to the buffer
- // outTransformHint - filled with default transform applied to the buffer
- // outNumPendingBuffers - num buffers queued that haven't yet been acquired
- // (counting the currently queued buffer)
- inline void deflate(uint32_t* outWidth,
- uint32_t* outHeight,
- uint32_t* outTransformHint,
- uint32_t* outNumPendingBuffers,
- uint64_t* outNextFrameNumber) const {
- *outWidth = width;
- *outHeight = height;
- *outTransformHint = transformHint;
- *outNumPendingBuffers = numPendingBuffers;
- *outNextFrameNumber = nextFrameNumber;
- }
- inline void inflate(uint32_t inWidth, uint32_t inHeight,
- uint32_t inTransformHint, uint32_t inNumPendingBuffers,
- uint64_t inNextFrameNumber) {
- width = inWidth;
- height = inHeight;
- transformHint = inTransformHint;
- numPendingBuffers = inNumPendingBuffers;
- nextFrameNumber = inNextFrameNumber;
- }
- private:
- uint32_t width;
- uint32_t height;
- uint32_t transformHint;
- uint32_t numPendingBuffers;
+ struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
+ QueueBufferOutput() = default;
+
+ // Moveable.
+ QueueBufferOutput(QueueBufferOutput&& src) = default;
+ QueueBufferOutput& operator=(QueueBufferOutput&& src) = default;
+ // Not copyable.
+ QueueBufferOutput(const QueueBufferOutput& src) = delete;
+ QueueBufferOutput& operator=(const QueueBufferOutput& src) = delete;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ uint32_t width{0};
+ uint32_t height{0};
+ uint32_t transformHint{0};
+ uint32_t numPendingBuffers{0};
uint64_t nextFrameNumber{0};
+ FrameEventHistoryDelta frameTimestamps;
+ bool bufferReplaced{false};
};
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
@@ -480,6 +487,7 @@
// is considered a no-op.
//
// Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the producer is not connected
// * BAD_VALUE - one of the following has occurred:
// * the api specified does not match the one that was connected
// * api was out of range (see above).
@@ -580,13 +588,8 @@
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
- // Attempts to retrieve timestamp information for the given frame number.
- // If information for the given frame number is not found, returns false.
- // Returns true otherwise.
- //
- // If a fence has not yet signaled the timestamp returned will be 0;
- virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
- FrameTimestamps* /*outTimestamps*/) const { return false; }
+ // Gets the frame events that haven't already been retrieved.
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {}
// Returns a unique id for this BufferQueue
virtual status_t getUniqueId(uint64_t* outId) const = 0;
diff --git a/include/gui/IProducerListener.h b/include/gui/IProducerListener.h
index b7826c6..e808bd3 100644
--- a/include/gui/IProducerListener.h
+++ b/include/gui/IProducerListener.h
@@ -33,7 +33,7 @@
{
public:
ProducerListener() {}
- virtual ~ProducerListener() {}
+ virtual ~ProducerListener();
// onBufferReleased is called from IGraphicBufferConsumer::releaseBuffer to
// notify the producer that a new buffer is free and ready to be dequeued.
@@ -61,6 +61,7 @@
class DummyProducerListener : public BnProducerListener
{
public:
+ virtual ~DummyProducerListener();
virtual void onBufferReleased() {}
virtual bool needsReleaseNotify() { return false; }
};
diff --git a/include/gui/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h
deleted file mode 100644
index f64c6b8..0000000
--- a/include/gui/ISensorEventConnection.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
-#define ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <binder/IInterface.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class BitTube;
-
-class ISensorEventConnection : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(SensorEventConnection);
-
- virtual sp<BitTube> getSensorChannel() const = 0;
- virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
- nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0;
- virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
- virtual status_t flush() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSensorEventConnection : public BnInterface<ISensorEventConnection>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h
deleted file mode 100644
index 571acb5..0000000
--- a/include/gui/ISensorServer.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_ISENSORSERVER_H
-#define ANDROID_GUI_ISENSORSERVER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <binder/IInterface.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Sensor;
-class ISensorEventConnection;
-class String8;
-
-class ISensorServer : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(SensorServer);
-
- virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
- virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
-
- virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
- int mode, const String16& opPackageName) = 0;
- virtual int32_t isDataInjectionEnabled() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSensorServer : public BnInterface<ISensorServer>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_ISENSORSERVER_H
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 74a4123..f80ba00 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -28,21 +28,23 @@
#include <binder/IInterface.h>
#include <ui/FrameStats.h>
+#include <ui/PixelFormat.h>
-#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceComposerClient.h>
+#include <vector>
namespace android {
// ----------------------------------------------------------------------------
-class ComposerState;
-class DisplayState;
+struct ComposerState;
+struct DisplayState;
struct DisplayInfo;
struct DisplayStatInfo;
class HdrCapabilities;
class IDisplayEventConnection;
-class IMemoryHeap;
+class IGraphicBufferProducer;
+class ISurfaceComposerClient;
class Rect;
+enum class FrameEvent;
/*
* This class defines the Binder IPC interface for accessing various
@@ -50,7 +52,7 @@
*/
class ISurfaceComposer: public IInterface {
public:
- DECLARE_META_INTERFACE(SurfaceComposer);
+ DECLARE_META_INTERFACE(SurfaceComposer)
// flags for setTransactionState()
enum {
@@ -70,17 +72,30 @@
eRotate270 = 3
};
+ enum VsyncSource {
+ eVsyncSourceApp = 0,
+ eVsyncSourceSurfaceFlinger = 1
+ };
+
/* create connection with surface flinger, requires
* ACCESS_SURFACE_FLINGER permission
*/
virtual sp<ISurfaceComposerClient> createConnection() = 0;
- /* create a graphic buffer allocator
+ /** create a scoped connection with surface flinger.
+ * Surfaces produced with this connection will act
+ * as children of the passed in GBP. That is to say
+ * SurfaceFlinger will draw them relative and confined to
+ * drawing of buffers from the layer associated with parent.
+ * As this is graphically equivalent in reach to just drawing
+ * pixels into the parent buffers, it requires no special permission.
*/
- virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() = 0;
+ virtual sp<ISurfaceComposerClient> createScopedConnection(
+ const sp<IGraphicBufferProducer>& parent) = 0;
/* return an IDisplayEventConnection */
- virtual sp<IDisplayEventConnection> createDisplayEventConnection() = 0;
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection(
+ VsyncSource vsyncSource = eVsyncSourceApp) = 0;
/* create a virtual display
* requires ACCESS_SURFACE_FLINGER permission.
@@ -112,6 +127,11 @@
virtual bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& surface) const = 0;
+ /* Returns the frame timestamps supported by SurfaceFlinger.
+ */
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const = 0;
+
/* set display power mode. depending on the mode, it can either trigger
* screen on, off or low power mode and wait for it to complete.
* requires ACCESS_SURFACE_FLINGER permission.
@@ -149,7 +169,7 @@
virtual 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,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform,
Rotation rotation = eRotateNone) = 0;
@@ -171,6 +191,10 @@
*/
virtual status_t getHdrCapabilities(const sp<IBinder>& display,
HdrCapabilities* outCapabilities) const = 0;
+
+ virtual status_t enableVSyncInjections(bool enable) = 0;
+
+ virtual status_t injectVSync(nsecs_t when) = 0;
};
// ----------------------------------------------------------------------------
@@ -182,13 +206,14 @@
// Java by ActivityManagerService.
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
CREATE_CONNECTION,
- CREATE_GRAPHIC_BUFFER_ALLOC,
+ UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC
CREATE_DISPLAY_EVENT_CONNECTION,
CREATE_DISPLAY,
DESTROY_DISPLAY,
GET_BUILT_IN_DISPLAY,
SET_TRANSACTION_STATE,
AUTHENTICATE_SURFACE,
+ GET_SUPPORTED_FRAME_TIMESTAMPS,
GET_DISPLAY_CONFIGS,
GET_ACTIVE_CONFIG,
SET_ACTIVE_CONFIG,
@@ -202,6 +227,9 @@
GET_DISPLAY_COLOR_MODES,
GET_ACTIVE_COLOR_MODE,
SET_ACTIVE_COLOR_MODE,
+ ENABLE_VSYNC_INJECTIONS,
+ INJECT_VSYNC,
+ CREATE_SCOPED_CONNECTION
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/gui/ISurfaceComposerClient.h b/include/gui/ISurfaceComposerClient.h
index c27a741..2c613ea 100644
--- a/include/gui/ISurfaceComposerClient.h
+++ b/include/gui/ISurfaceComposerClient.h
@@ -14,54 +14,44 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_ISURFACE_COMPOSER_CLIENT_H
-#define ANDROID_GUI_ISURFACE_COMPOSER_CLIENT_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
+#pragma once
#include <binder/IInterface.h>
-
-#include <ui/FrameStats.h>
+#include <binder/SafeInterface.h>
#include <ui/PixelFormat.h>
namespace android {
-// ----------------------------------------------------------------------------
+class FrameStats;
class IGraphicBufferProducer;
-class ISurfaceComposerClient : public IInterface
-{
+class ISurfaceComposerClient : public IInterface {
public:
- DECLARE_META_INTERFACE(SurfaceComposerClient);
+ DECLARE_META_INTERFACE(SurfaceComposerClient)
// flags for createSurface()
enum { // (keep in sync with Surface.java)
- eHidden = 0x00000004,
- eDestroyBackbuffer = 0x00000020,
- eSecure = 0x00000080,
- eNonPremultiplied = 0x00000100,
- eOpaque = 0x00000400,
- eProtectedByApp = 0x00000800,
- eProtectedByDRM = 0x00001000,
- eCursorWindow = 0x00002000,
+ eHidden = 0x00000004,
+ eDestroyBackbuffer = 0x00000020,
+ eSecure = 0x00000080,
+ eNonPremultiplied = 0x00000100,
+ eOpaque = 0x00000400,
+ eProtectedByApp = 0x00000800,
+ eProtectedByDRM = 0x00001000,
+ eCursorWindow = 0x00002000,
- eFXSurfaceNormal = 0x00000000,
- eFXSurfaceDim = 0x00020000,
- eFXSurfaceMask = 0x000F0000,
+ eFXSurfaceNormal = 0x00000000,
+ eFXSurfaceDim = 0x00020000,
+ eFXSurfaceMask = 0x000F0000,
};
/*
* Requires ACCESS_SURFACE_FLINGER permission
*/
- virtual status_t createSurface(
- const String8& name, uint32_t w, uint32_t h,
- PixelFormat format, uint32_t flags,
- sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) = 0;
+ virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags, const sp<IBinder>& parent, uint32_t windowType,
+ uint32_t ownerUid, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
@@ -77,21 +67,14 @@
* Requires ACCESS_SURFACE_FLINGER permission
*/
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
-
- virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
- bool* outTransformToDisplayInverse) const = 0;
};
-// ----------------------------------------------------------------------------
-
-class BnSurfaceComposerClient: public BnInterface<ISurfaceComposerClient> {
+class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
public:
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
+ BnSurfaceComposerClient()
+ : SafeBnInterface<ISurfaceComposerClient>("BnSurfaceComposerClient") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
};
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_ISURFACE_COMPOSER_CLIENT_H
+} // namespace android
diff --git a/include/gui/OccupancyTracker.h b/include/gui/OccupancyTracker.h
index 1d15e7f..d4de8f2 100644
--- a/include/gui/OccupancyTracker.h
+++ b/include/gui/OccupancyTracker.h
@@ -45,12 +45,12 @@
occupancyAverage(0.0f),
usedThirdBuffer(false) {}
- Segment(nsecs_t totalTime, size_t numFrames, float occupancyAverage,
- bool usedThirdBuffer)
- : totalTime(totalTime),
- numFrames(numFrames),
- occupancyAverage(occupancyAverage),
- usedThirdBuffer(usedThirdBuffer) {}
+ Segment(nsecs_t _totalTime, size_t _numFrames, float _occupancyAverage,
+ bool _usedThirdBuffer)
+ : totalTime(_totalTime),
+ numFrames(_numFrames),
+ occupancyAverage(_occupancyAverage),
+ usedThirdBuffer(_usedThirdBuffer) {}
// Parcelable interface
virtual status_t writeToParcel(Parcel* parcel) const override;
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
deleted file mode 100644
index 094fd16..0000000
--- a/include/gui/Sensor.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_SENSOR_H
-#define ANDROID_GUI_SENSOR_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/Flattenable.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
-#include <hardware/sensors.h>
-
-#include <android/sensor.h>
-
-// ----------------------------------------------------------------------------
-// Concrete types for the NDK
-struct ASensor { };
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Parcel;
-
-// ----------------------------------------------------------------------------
-
-class Sensor : public ASensor, public LightFlattenable<Sensor>
-{
-public:
- enum {
- TYPE_ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER,
- TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
- TYPE_GYROSCOPE = ASENSOR_TYPE_GYROSCOPE,
- TYPE_LIGHT = ASENSOR_TYPE_LIGHT,
- TYPE_PROXIMITY = ASENSOR_TYPE_PROXIMITY
- };
-
- struct uuid_t{
- union {
- uint8_t b[16];
- int64_t i64[2];
- };
- uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));}
- uuid_t() : b{0} {}
- };
-
- Sensor(const char * name = "");
- Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
- Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0);
- ~Sensor();
-
- const String8& getName() const;
- const String8& getVendor() const;
- int32_t getHandle() const;
- int32_t getType() const;
- float getMinValue() const;
- float getMaxValue() const;
- float getResolution() const;
- float getPowerUsage() const;
- int32_t getMinDelay() const;
- nsecs_t getMinDelayNs() const;
- int32_t getVersion() const;
- uint32_t getFifoReservedEventCount() const;
- uint32_t getFifoMaxEventCount() const;
- const String8& getStringType() const;
- const String8& getRequiredPermission() const;
- bool isRequiredPermissionRuntime() const;
- int32_t getRequiredAppOp() const;
- int32_t getMaxDelay() const;
- uint32_t getFlags() const;
- bool isWakeUpSensor() const;
- bool isDynamicSensor() const;
- bool hasAdditionalInfo() const;
- int32_t getReportingMode() const;
-
- // Note that after setId() has been called, getUuid() no longer
- // returns the UUID.
- // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
- // make sure setId() doesn't change the UuidIndex.
- const uuid_t& getUuid() const;
- int32_t getId() const;
- void setId(int32_t id);
-
- // LightFlattenable protocol
- inline bool isFixedSize() const { return false; }
- size_t getFlattenedSize() const;
- status_t flatten(void* buffer, size_t size) const;
- status_t unflatten(void const* buffer, size_t size);
-
-private:
- String8 mName;
- String8 mVendor;
- int32_t mHandle;
- int32_t mType;
- float mMinValue;
- float mMaxValue;
- float mResolution;
- float mPower;
- int32_t mMinDelay;
- int32_t mVersion;
- uint32_t mFifoReservedEventCount;
- uint32_t mFifoMaxEventCount;
- String8 mStringType;
- String8 mRequiredPermission;
- bool mRequiredPermissionRuntime = false;
- int32_t mRequiredAppOp;
- int32_t mMaxDelay;
- uint32_t mFlags;
- // TODO(b/29547335): Get rid of this field and replace with an index.
- // The index will be into a separate global vector of UUIDs.
- // Also add an mId field (and change flatten/unflatten appropriately).
- uuid_t mUuid;
- static void flattenString8(void*& buffer, size_t& size, const String8& string8);
- static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SENSOR_H
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
deleted file mode 100644
index 4ee7c02..0000000
--- a/include/gui/SensorEventQueue.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SENSOR_EVENT_QUEUE_H
-#define ANDROID_SENSOR_EVENT_QUEUE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-#include <utils/String16.h>
-
-#include <gui/BitTube.h>
-
-// ----------------------------------------------------------------------------
-#define WAKE_UP_SENSOR_EVENT_NEEDS_ACK (1U << 31)
-struct ALooper;
-struct ASensorEvent;
-
-// Concrete types for the NDK
-struct ASensorEventQueue {
- ALooper* looper;
-};
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class ISensorEventConnection;
-class Sensor;
-class Looper;
-
-// ----------------------------------------------------------------------------
-
-class SensorEventQueue : public ASensorEventQueue, public RefBase
-{
-public:
-
- enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 };
-
- /**
- * Typical sensor delay (sample period) in microseconds.
- */
- // Fastest sampling, system will bound it to minDelay
- static constexpr int32_t SENSOR_DELAY_FASTEST = 0;
- // Typical sample period for game, 50Hz;
- static constexpr int32_t SENSOR_DELAY_GAME = 20000;
- // Typical sample period for UI, 15Hz
- static constexpr int32_t SENSOR_DELAY_UI = 66667;
- // Default sensor sample period
- static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
-
- SensorEventQueue(const sp<ISensorEventConnection>& connection);
- virtual ~SensorEventQueue();
- virtual void onFirstRef();
-
- int getFd() const;
-
- static ssize_t write(const sp<BitTube>& tube,
- ASensorEvent const* events, size_t numEvents);
-
- ssize_t read(ASensorEvent* events, size_t numEvents);
-
- status_t waitForEvent() const;
- status_t wake() const;
-
- status_t enableSensor(Sensor const* sensor) const;
- status_t enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const;
- status_t disableSensor(Sensor const* sensor) const;
- status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
-
- // these are here only to support SensorManager.java
- status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int maxBatchReportLatencyUs,
- int reservedFlags) const;
- status_t disableSensor(int32_t handle) const;
- status_t flush() const;
- // Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK.
- void sendAck(const ASensorEvent* events, int count);
-
- status_t injectSensorEvent(const ASensorEvent& event);
-private:
- sp<Looper> getLooper() const;
- sp<ISensorEventConnection> mSensorEventConnection;
- sp<BitTube> mSensorChannel;
- mutable Mutex mLock;
- mutable sp<Looper> mLooper;
- ASensorEvent* mRecBuffer;
- size_t mAvailable;
- size_t mConsumed;
- uint32_t mNumAcksToSend;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SENSOR_EVENT_QUEUE_H
diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h
deleted file mode 100644
index 6c6230f..0000000
--- a/include/gui/SensorManager.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_SENSOR_MANAGER_H
-#define ANDROID_GUI_SENSOR_MANAGER_H
-
-#include <map>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Singleton.h>
-#include <utils/Vector.h>
-#include <utils/String8.h>
-
-#include <gui/SensorEventQueue.h>
-
-// ----------------------------------------------------------------------------
-// Concrete types for the NDK
-struct ASensorManager { };
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class ISensorServer;
-class Sensor;
-class SensorEventQueue;
-// ----------------------------------------------------------------------------
-
-class SensorManager :
- public ASensorManager
-{
-public:
- static SensorManager& getInstanceForPackage(const String16& packageName);
- ~SensorManager();
-
- ssize_t getSensorList(Sensor const* const** list);
- ssize_t getDynamicSensorList(Vector<Sensor>& list);
- Sensor const* getDefaultSensor(int type);
- sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
- bool isDataInjectionEnabled();
-
-private:
- // DeathRecipient interface
- void sensorManagerDied();
-
- SensorManager(const String16& opPackageName);
- status_t assertStateLocked();
-
-private:
- static Mutex sLock;
- static std::map<String16, SensorManager*> sPackageInstances;
-
- Mutex mLock;
- sp<ISensorServer> mSensorServer;
- Sensor const** mSensorList;
- Vector<Sensor> mSensors;
- sp<IBinder::DeathRecipient> mDeathObserver;
- const String16 mOpPackageName;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SENSOR_MANAGER_H
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index f4a22cb..e8dc83e 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -18,21 +18,21 @@
#define ANDROID_GUI_SURFACE_H
#include <gui/IGraphicBufferProducer.h>
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
#include <ui/ANativeObjectBase.h>
#include <ui/Region.h>
-#include <binder/Parcelable.h>
-
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
#include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <utils/KeyedVector.h>
struct ANativeWindow_Buffer;
namespace android {
+class ISurfaceComposer;
+
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* BufferQueue.
@@ -66,7 +66,8 @@
* the controlledByApp flag indicates that this Surface (producer) is
* controlled by the application. This flag is used at connect time.
*/
- 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
@@ -134,17 +135,41 @@
status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]);
+ status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration);
+
+ /* Enables or disables frame timestamp tracking. It is disabled by default
+ * to avoid overhead during queue and dequeue for applications that don't
+ * need the feature. If disabled, calls to getFrameTimestamps will fail.
+ */
+ void enableFrameTimestamps(bool enable);
+
+ status_t getCompositorTiming(
+ nsecs_t* compositeDeadline, nsecs_t* compositeInterval,
+ nsecs_t* compositeToPresentLatency);
+
// See IGraphicBufferProducer::getFrameTimestamps
- bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
- nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
- nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+ status_t getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+ nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
nsecs_t* outReleaseTime);
+ status_t getWideColorSupport(bool* supported);
+ status_t getHdrSupport(bool* supported);
+
status_t getUniqueId(uint64_t* outId) const;
+ // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call
+ nsecs_t getLastDequeueStartTime() const;
+
protected:
virtual ~Surface();
+ // Virtual for testing.
+ virtual sp<ISurfaceComposer> composerService() const;
+ virtual nsecs_t now() const;
+
private:
// can't be copied
Surface& operator = (const Surface& rhs);
@@ -191,7 +216,13 @@
int dispatchSetSurfaceDamage(va_list args);
int dispatchSetSharedBufferMode(va_list args);
int dispatchSetAutoRefresh(va_list args);
+ int dispatchGetDisplayRefreshCycleDuration(va_list args);
+ int dispatchGetNextFrameId(va_list args);
+ int dispatchEnableFrameTimestamps(va_list args);
+ int dispatchGetCompositorTiming(va_list args);
int dispatchGetFrameTimestamps(va_list args);
+ int dispatchGetWideColorSupport(va_list args);
+ int dispatchGetHdrSupport(va_list args);
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -204,7 +235,6 @@
virtual int connect(int api);
virtual int setBufferCount(int bufferCount);
- virtual int setBuffersDimensions(uint32_t width, uint32_t height);
virtual int setBuffersUserDimensions(uint32_t width, uint32_t height);
virtual int setBuffersFormat(PixelFormat format);
virtual int setBuffersTransform(uint32_t transform);
@@ -224,20 +254,37 @@
virtual int setAsyncMode(bool async);
virtual int setSharedBufferMode(bool sharedBufferMode);
virtual int setAutoRefresh(bool autoRefresh);
+ virtual int setBuffersDimensions(uint32_t width, uint32_t height);
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
virtual int query(int what, int* value) const;
virtual int connect(int api, const sp<IProducerListener>& listener);
+
+ // When reportBufferRemoval is true, clients must call getAndFlushRemovedBuffers to fetch
+ // GraphicBuffers removed from this surface after a dequeueBuffer, detachNextBuffer or
+ // attachBuffer call. This allows clients with their own buffer caches to free up buffers no
+ // longer in use by this surface.
+ virtual int connect(
+ int api, const sp<IProducerListener>& listener,
+ bool reportBufferRemoval);
virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence);
virtual int attachBuffer(ANativeWindowBuffer*);
+ // When client connects to Surface with reportBufferRemoval set to true, any buffers removed
+ // from this Surface will be collected and returned here. Once this method returns, these
+ // buffers will no longer be referenced by this Surface unless they are attached to this
+ // Surface later. The list of removed buffers will only be stored until the next dequeueBuffer,
+ // detachNextBuffer, or attachBuffer call.
+ status_t getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out);
+
protected:
- enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS };
+ enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
-private:
+ void querySupportedTimestampsLocked() const;
+
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
@@ -377,48 +424,26 @@
nsecs_t mLastDequeueDuration = 0;
nsecs_t mLastQueueDuration = 0;
+ // Stores the time right before we call IGBP::dequeueBuffer
+ nsecs_t mLastDequeueStartTime = 0;
+
Condition mQueueBufferCondition;
- uint64_t mNextFrameNumber;
+ uint64_t mNextFrameNumber = 1;
+ uint64_t mLastFrameNumber = 0;
+
+ // Mutable because ANativeWindow::query needs this class const.
+ mutable bool mQueriedSupportedTimestamps;
+ mutable bool mFrameTimestampsSupportsPresent;
+
+ // A cached copy of the FrameEventHistory maintained by the consumer.
+ bool mEnableFrameTimestamps = false;
+ std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory;
+
+ bool mReportRemovedBuffers = false;
+ std::vector<sp<GraphicBuffer>> mRemovedBuffers;
};
-namespace view {
-
-/**
- * A simple holder for an IGraphicBufferProducer, to match the managed-side
- * android.view.Surface parcelable behavior.
- *
- * This implements android/view/Surface.aidl
- *
- * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly
- * used in managed Binder calls.
- */
-class Surface : public Parcelable {
- public:
-
- String16 name;
- sp<IGraphicBufferProducer> graphicBufferProducer;
-
- virtual status_t writeToParcel(Parcel* parcel) const override;
- virtual status_t readFromParcel(const Parcel* parcel) override;
-
- // nameAlreadyWritten set to true by Surface.java, because it splits
- // Parceling itself between managed and native code, so it only wants a part
- // of the full parceling to happen on its native side.
- status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const;
-
- // nameAlreadyRead set to true by Surface.java, because it splits
- // Parceling itself between managed and native code, so it only wants a part
- // of the full parceling to happen on its native side.
- status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
-
- private:
-
- static String16 readMaybeEmptyString16(const Parcel* parcel);
-};
-
-} // namespace view
-
-}; // namespace android
+} // namespace android
#endif // ANDROID_GUI_SURFACE_H
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index c4f88b6..ec310cf 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <binder/IBinder.h>
-#include <binder/IMemory.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
@@ -38,7 +37,7 @@
// ---------------------------------------------------------------------------
-class DisplayInfo;
+struct DisplayInfo;
class Composer;
class HdrCapabilities;
class ISurfaceComposerClient;
@@ -52,6 +51,7 @@
friend class Composer;
public:
SurfaceComposerClient();
+ SurfaceComposerClient(const sp<IGraphicBufferProducer>& parent);
virtual ~SurfaceComposerClient();
// Always make sure we could initialize
@@ -105,7 +105,10 @@
uint32_t w, // width in pixel
uint32_t h, // height in pixel
PixelFormat format, // pixel-format desired
- uint32_t flags = 0 // usage flags
+ uint32_t flags = 0, // usage flags
+ SurfaceControl* parent = nullptr, // parent
+ uint32_t windowType = 0, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
+ uint32_t ownerUid = 0 // UID of the task
);
//! Create a virtual display
@@ -131,6 +134,10 @@
//! Close a composer transaction on all active SurfaceComposerClients.
static void closeGlobalTransaction(bool synchronous = false);
+ static status_t enableVSyncInjections(bool enable);
+
+ static status_t injectVSync(nsecs_t when);
+
//! Flag the currently open transaction as an animation transaction.
static void setAnimationTransaction();
@@ -138,9 +145,11 @@
status_t show(const sp<IBinder>& id);
status_t setFlags(const sp<IBinder>& id, uint32_t flags, uint32_t mask);
status_t setTransparentRegionHint(const sp<IBinder>& id, const Region& transparent);
- status_t setLayer(const sp<IBinder>& id, uint32_t layer);
+ status_t setLayer(const sp<IBinder>& id, int32_t layer);
+ status_t setRelativeLayer(const sp<IBinder>& id,
+ const sp<IBinder>& relativeTo, int32_t layer);
status_t setAlpha(const sp<IBinder>& id, float alpha=1.0f);
- status_t setMatrix(const sp<IBinder>& id, float dsdx, float dtdx, float dsdy, float dtdy);
+ status_t setMatrix(const sp<IBinder>& id, float dsdx, float dtdx, float dtdy, float dsdy);
status_t setPosition(const sp<IBinder>& id, float x, float y);
status_t setSize(const sp<IBinder>& id, uint32_t w, uint32_t h);
status_t setCrop(const sp<IBinder>& id, const Rect& crop);
@@ -148,6 +157,11 @@
status_t setLayerStack(const sp<IBinder>& id, uint32_t layerStack);
status_t deferTransactionUntil(const sp<IBinder>& id,
const sp<IBinder>& handle, uint64_t frameNumber);
+ status_t deferTransactionUntil(const sp<IBinder>& id,
+ const sp<Surface>& handle, uint64_t frameNumber);
+ status_t reparentChildren(const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle);
+ status_t detachChildren(const sp<IBinder>& id);
status_t setOverrideScalingMode(const sp<IBinder>& id,
int32_t overrideScalingMode);
status_t setGeometryAppliesWithResize(const sp<IBinder>& id);
@@ -157,9 +171,6 @@
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const;
- status_t getTransformToDisplayInverse(const sp<IBinder>& token,
- bool* outTransformToDisplayInverse) const;
-
static status_t clearAnimationFrameStats();
static status_t getAnimationFrameStats(FrameStats* outStats);
@@ -195,6 +206,7 @@
status_t mStatus;
sp<ISurfaceComposerClient> mClient;
Composer& mComposer;
+ wp<IGraphicBufferProducer> mParent;
};
// ---------------------------------------------------------------------------
@@ -208,9 +220,15 @@
const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform);
-
+ static status_t captureToBuffer(
+ const sp<IBinder>& display,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ int32_t minLayerZ, int32_t maxLayerZ,
+ bool useIdentityTransform,
+ uint32_t rotation,
+ sp<GraphicBuffer>* outbuffer);
private:
mutable sp<CpuConsumer> mCpuConsumer;
mutable sp<IGraphicBufferProducer> mProducer;
@@ -231,11 +249,11 @@
bool useIdentityTransform);
status_t update(const sp<IBinder>& display,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform);
status_t update(const sp<IBinder>& display,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform, uint32_t rotation);
sp<CpuConsumer> getCpuConsumer() const;
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index 5e731c3..8bb705c 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -61,7 +61,28 @@
void disconnect();
status_t setLayerStack(uint32_t layerStack);
- status_t setLayer(uint32_t layer);
+ status_t setLayer(int32_t layer);
+
+ // Sets a Z order relative to the Surface specified by "relativeTo" but
+ // without becoming a full child of the relative. Z-ordering works exactly
+ // as if it were a child however.
+ //
+ // As a nod to sanity, only non-child surfaces may have a relative Z-order.
+ //
+ // This overrides any previous and is overriden by any future calls
+ // to setLayer.
+ //
+ // If the relative dissapears, the Surface will have no layer and be
+ // invisible, until the next time set(Relative)Layer is called.
+ //
+ // TODO: This is probably a hack. Currently it exists only to work around
+ // some framework usage of the hidden APPLICATION_MEDIA_OVERLAY window type
+ // which allows inserting a window between a SurfaceView and it's main application
+ // window. However, since we are using child windows for the SurfaceView, but not using
+ // child windows elsewhere in O, the WindowManager can't set the layer appropriately.
+ // This is only used by the "TvInputService" and following the port of ViewRootImpl
+ // to child surfaces, we can then port this and remove this method.
+ status_t setRelativeLayer(const sp<IBinder>& relativeTo, int32_t layer);
status_t setPosition(float x, float y);
status_t setSize(uint32_t w, uint32_t h);
status_t hide();
@@ -69,7 +90,7 @@
status_t setFlags(uint32_t flags, uint32_t mask);
status_t setTransparentRegionHint(const Region& transparent);
status_t setAlpha(float alpha=1.0f);
- status_t setMatrix(float dsdx, float dtdx, float dsdy, float dtdy);
+ status_t setMatrix(float dsdx, float dtdx, float dtdy, float dsdy);
status_t setCrop(const Rect& crop);
status_t setFinalCrop(const Rect& crop);
@@ -80,8 +101,30 @@
status_t setGeometryAppliesWithResize();
// Defers applying any changes made in this transaction until the Layer
- // identified by handle reaches the given frameNumber
- status_t deferTransactionUntil(sp<IBinder> handle, uint64_t frameNumber);
+ // identified by handle reaches the given frameNumber. If the Layer identified
+ // by handle is removed, then we will apply this transaction regardless of
+ // what frame number has been reached.
+ status_t deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
+
+ // A variant of deferTransactionUntil which identifies the Layer we wait for by
+ // Surface instead of Handle. Useful for clients which may not have the
+ // SurfaceControl for some of their Surfaces. Otherwise behaves identically.
+ status_t deferTransactionUntil(const sp<Surface>& barrier, uint64_t frameNumber);
+
+ // Reparents all children of this layer to the new parent handle.
+ status_t reparentChildren(const sp<IBinder>& newParentHandle);
+
+ // Detaches all child surfaces (and their children recursively)
+ // from their SurfaceControl.
+ // The child SurfaceControl's will not throw exceptions or return errors,
+ // but transactions will have no effect.
+ // The child surfaces will continue to follow their parent surfaces,
+ // and remain eligible for rendering, but their relative state will be
+ // frozen. We use this in the WindowManager, in app shutdown/relaunch
+ // scenarios, where the app would otherwise clean up its child Surfaces.
+ // Sometimes the WindowManager needs to extend their lifetime slightly
+ // in order to perform an exit animation or prevent flicker.
+ status_t detachChildren();
// Set an override scaling mode as documented in <system/window.h>
// the override scaling mode will take precedence over any client
@@ -92,13 +135,12 @@
const sp<SurfaceControl>& control, Parcel* parcel);
sp<Surface> getSurface() const;
+ sp<Surface> createSurface() const;
sp<IBinder> getHandle() const;
status_t clearLayerFrameStats() const;
status_t getLayerFrameStats(FrameStats* outStats) const;
- status_t getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const;
-
private:
// can't be copied
SurfaceControl& operator = (SurfaceControl& rhs);
@@ -114,6 +156,7 @@
~SurfaceControl();
+ sp<Surface> generateSurfaceLocked() const;
status_t validate() const;
void destroy();
diff --git a/include/gui/bufferqueue/1.0/B2HProducerListener.h b/include/gui/bufferqueue/1.0/B2HProducerListener.h
new file mode 100644
index 0000000..fa6c2d9
--- /dev/null
+++ b/include/gui/bufferqueue/1.0/B2HProducerListener.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/IBinder.h>
+#include <gui/IProducerListener.h>
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+ HProducerListener;
+
+typedef ::android::IProducerListener
+ BProducerListener;
+
+struct B2HProducerListener : public HProducerListener {
+ sp<BProducerListener> mBase;
+ B2HProducerListener(sp<BProducerListener> const& base);
+ Return<void> onBufferReleased() override;
+ Return<bool> needsReleaseNotify() override;
+};
+
+} // namespace utils
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+
diff --git a/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
new file mode 100644
index 0000000..93c452a
--- /dev/null
+++ b/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+
+#include <hidl/HybridInterface.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::media::V1_0::AnwBuffer;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+ HGraphicBufferProducer;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+ HProducerListener;
+
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+using ::android::BnGraphicBufferProducer;
+using ::android::IProducerListener;
+
+struct H2BGraphicBufferProducer : public ::android::H2BConverter<
+ HGraphicBufferProducer,
+ BGraphicBufferProducer,
+ BnGraphicBufferProducer> {
+ H2BGraphicBufferProducer(sp<HGraphicBufferProducer> const& base) : CBase(base) {}
+
+ status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override;
+ status_t setAsyncMode(bool async) override;
+ status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
+ uint32_t h, ::android::PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) override;
+ status_t detachBuffer(int slot) override;
+ status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence)
+ override;
+ status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer)
+ override;
+ status_t queueBuffer(int slot,
+ const QueueBufferInput& input,
+ QueueBufferOutput* output) override;
+ status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
+ int query(int what, int* value) override;
+ status_t connect(const sp<IProducerListener>& listener, int api,
+ bool producerControlledByApp, QueueBufferOutput* output) override;
+ status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api)
+ override;
+ status_t setSidebandStream(const sp<NativeHandle>& stream) override;
+ void allocateBuffers(uint32_t width, uint32_t height,
+ ::android::PixelFormat format, uint32_t usage) override;
+ status_t allowAllocation(bool allow) override;
+ status_t setGenerationNumber(uint32_t generationNumber) override;
+ String8 getConsumerName() const override;
+ status_t setSharedBufferMode(bool sharedBufferMode) override;
+ status_t setAutoRefresh(bool autoRefresh) override;
+ status_t setDequeueTimeout(nsecs_t timeout) override;
+ status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence, float outTransformMatrix[16]) override;
+ void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
+ status_t getUniqueId(uint64_t* outId) const override;
+};
+
+} // namespace utils
+} // namespace V1_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
diff --git a/include/gui/view/Surface.h b/include/gui/view/Surface.h
new file mode 100644
index 0000000..cc64fd4
--- /dev/null
+++ b/include/gui/view/Surface.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_VIEW_SURFACE_H
+#define ANDROID_GUI_VIEW_SURFACE_H
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/String16.h>
+
+#include <binder/Parcelable.h>
+
+namespace android {
+
+class IGraphicBufferProducer;
+
+namespace view {
+
+/**
+ * A simple holder for an IGraphicBufferProducer, to match the managed-side
+ * android.view.Surface parcelable behavior.
+ *
+ * This implements android/view/Surface.aidl
+ *
+ * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly
+ * used in managed Binder calls.
+ */
+class Surface : public Parcelable {
+ public:
+
+ String16 name;
+ sp<IGraphicBufferProducer> graphicBufferProducer;
+
+ virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual status_t readFromParcel(const Parcel* parcel) override;
+
+ // nameAlreadyWritten set to true by Surface.java, because it splits
+ // Parceling itself between managed and native code, so it only wants a part
+ // of the full parceling to happen on its native side.
+ status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const;
+
+ // nameAlreadyRead set to true by Surface.java, because it splits
+ // Parceling itself between managed and native code, so it only wants a part
+ // of the full parceling to happen on its native side.
+ status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
+
+ private:
+
+ static String16 readMaybeEmptyString16(const Parcel* parcel);
+};
+
+} // namespace view
+} // namespace android
+
+#endif // ANDROID_GUI_VIEW_SURFACE_H
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
new file mode 100644
index 0000000..86da4d3
--- /dev/null
+++ b/include/input/DisplayViewport.h
@@ -0,0 +1,104 @@
+/*
+ * 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 _LIBINPUT_DISPLAY_VIEWPORT_H
+#define _LIBINPUT_DISPLAY_VIEWPORT_H
+
+#include <ui/DisplayInfo.h>
+#include <input/Input.h>
+
+namespace android {
+
+/*
+ * Describes how coordinates are mapped on a physical display.
+ * See com.android.server.display.DisplayViewport.
+ */
+struct DisplayViewport {
+ int32_t displayId; // -1 if invalid
+ int32_t orientation;
+ int32_t logicalLeft;
+ int32_t logicalTop;
+ int32_t logicalRight;
+ int32_t logicalBottom;
+ int32_t physicalLeft;
+ int32_t physicalTop;
+ int32_t physicalRight;
+ int32_t physicalBottom;
+ int32_t deviceWidth;
+ int32_t deviceHeight;
+ String8 uniqueId;
+
+ DisplayViewport() :
+ displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
+ logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
+ physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
+ deviceWidth(0), deviceHeight(0) {
+ }
+
+ bool operator==(const DisplayViewport& other) const {
+ return displayId == other.displayId
+ && orientation == other.orientation
+ && logicalLeft == other.logicalLeft
+ && logicalTop == other.logicalTop
+ && logicalRight == other.logicalRight
+ && logicalBottom == other.logicalBottom
+ && physicalLeft == other.physicalLeft
+ && physicalTop == other.physicalTop
+ && physicalRight == other.physicalRight
+ && physicalBottom == other.physicalBottom
+ && deviceWidth == other.deviceWidth
+ && deviceHeight == other.deviceHeight
+ && uniqueId == other.uniqueId;
+ }
+
+ bool operator!=(const DisplayViewport& other) const {
+ return !(*this == other);
+ }
+
+ inline bool isValid() const {
+ return displayId >= 0;
+ }
+
+ void setNonDisplayViewport(int32_t width, int32_t height) {
+ displayId = ADISPLAY_ID_NONE;
+ orientation = DISPLAY_ORIENTATION_0;
+ logicalLeft = 0;
+ logicalTop = 0;
+ logicalRight = width;
+ logicalBottom = height;
+ physicalLeft = 0;
+ physicalTop = 0;
+ physicalRight = width;
+ physicalBottom = height;
+ deviceWidth = width;
+ deviceHeight = height;
+ uniqueId.clear();
+ }
+};
+
+/**
+ * Describes the different type of viewports supported by input flinger.
+ * Keep in sync with values in InputManagerService.java.
+ */
+enum class ViewportType : int32_t {
+ VIEWPORT_INTERNAL = 1,
+ VIEWPORT_EXTERNAL = 2,
+ VIEWPORT_VIRTUAL = 3,
+};
+
+} // namespace android
+
+#endif // _LIBINPUT_DISPLAY_VIEWPORT_H
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
index 629310f..11bb721 100644
--- a/include/input/IInputFlinger.h
+++ b/include/input/IInputFlinger.h
@@ -30,7 +30,7 @@
*/
class IInputFlinger : public IInterface {
public:
- DECLARE_META_INTERFACE(InputFlinger);
+ DECLARE_META_INTERFACE(InputFlinger)
};
diff --git a/include/input/Input.h b/include/input/Input.h
index 55787e7..cfcafab 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -398,7 +398,7 @@
inline int32_t getButtonState() const { return mButtonState; }
- inline int32_t setButtonState(int32_t buttonState) { mButtonState = buttonState; }
+ inline void setButtonState(int32_t buttonState) { mButtonState = buttonState; }
inline int32_t getActionButton() const { return mActionButton; }
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 0bd14ea..20154eb 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -425,30 +425,30 @@
return NULL;
}
-static int32_t getKeyCodeByLabel(const char* label) {
+static inline int32_t getKeyCodeByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, KEYCODES));
}
-static const char* getLabelByKeyCode(int32_t keyCode) {
- if (keyCode >= 0 && keyCode < size(KEYCODES)) {
+static inline const char* getLabelByKeyCode(int32_t keyCode) {
+ if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) {
return KEYCODES[keyCode].literal;
}
return NULL;
}
-static uint32_t getKeyFlagByLabel(const char* label) {
+static inline uint32_t getKeyFlagByLabel(const char* label) {
return uint32_t(lookupValueByLabel(label, FLAGS));
}
-static int32_t getAxisByLabel(const char* label) {
+static inline int32_t getAxisByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, AXES));
}
-static const char* getAxisLabel(int32_t axisId) {
+static inline const char* getAxisLabel(int32_t axisId) {
return lookupLabelByValue(axisId, AXES);
}
-static int32_t getLedByLabel(const char* label) {
+static inline int32_t getLedByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, LEDS));
}
diff --git a/include/media/cas/CasAPI.h b/include/media/cas/CasAPI.h
new file mode 100644
index 0000000..67f4511
--- /dev/null
+++ b/include/media/cas/CasAPI.h
@@ -0,0 +1,133 @@
+/*
+ * 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 CAS_API_H_
+#define CAS_API_H_
+
+#include <vector>
+#include <utils/String8.h>
+
+// Loadable CasPlugin shared libraries should define the entry points
+// as shown below:
+//
+// extern "C" {
+// extern android::CasFactory *createCasFactory();
+// extern android::DescramblerFactory *createDescramblerFactory();
+// }
+
+namespace android {
+
+struct CasPlugin;
+
+struct CasPluginDescriptor {
+ int32_t CA_system_id;
+ String8 name;
+};
+
+typedef std::vector<uint8_t> CasData;
+typedef std::vector<uint8_t> CasSessionId;
+typedef std::vector<uint8_t> CasEmm;
+typedef std::vector<uint8_t> CasEcm;
+typedef void (*CasPluginCallback)(
+ void *appData,
+ int32_t event,
+ int32_t arg,
+ uint8_t *data,
+ size_t size);
+
+struct CasFactory {
+ CasFactory() {}
+ virtual ~CasFactory() {}
+
+ // Determine if the plugin can handle the CA scheme identified by CA_system_id.
+ virtual bool isSystemIdSupported(
+ int32_t CA_system_id) const = 0;
+
+ // Get a list of the CA schemes supported by the plugin.
+ virtual status_t queryPlugins(
+ std::vector<CasPluginDescriptor> *descriptors) const = 0;
+
+ // Construct a new instance of a CasPlugin given a CA_system_id
+ virtual status_t createPlugin(
+ int32_t CA_system_id,
+ uint64_t appData,
+ CasPluginCallback callback,
+ CasPlugin **plugin) = 0;
+
+private:
+ CasFactory(const CasFactory &);
+ CasFactory &operator=(const CasFactory &); /* NOLINT */
+};
+
+struct CasPlugin {
+ CasPlugin() {}
+ virtual ~CasPlugin() {}
+
+ // Provide the CA private data from a CA_descriptor in the conditional
+ // access table to a CasPlugin.
+ virtual status_t setPrivateData(
+ const CasData &privateData) = 0;
+
+ // Open a session for descrambling a program, or one or more elementary
+ // streams.
+ virtual status_t openSession(CasSessionId *sessionId) = 0;
+
+ // Close a previously opened session.
+ virtual status_t closeSession(const CasSessionId &sessionId) = 0;
+
+ // Provide the CA private data from a CA_descriptor in the program map
+ // table to a CasPlugin.
+ virtual status_t setSessionPrivateData(
+ const CasSessionId &sessionId,
+ const CasData &privateData) = 0;
+
+ // Process an ECM from the ECM stream for this session’s elementary stream.
+ virtual status_t processEcm(
+ const CasSessionId &sessionId,
+ const CasEcm &ecm) = 0;
+
+ // Process an in-band EMM from the EMM stream.
+ virtual status_t processEmm(
+ const CasEmm &emm) = 0;
+
+ // Deliver an event to the CasPlugin. The format of the event is specific
+ // to the CA scheme and is opaque to the framework.
+ virtual status_t sendEvent(
+ int32_t event,
+ int32_t arg,
+ const CasData &eventData) = 0;
+
+ // Native implementation of the MediaCas Java API provision method.
+ virtual status_t provision(
+ const String8 &provisionString) = 0;
+
+ // Native implementation of the MediaCas Java API refreshEntitlements method
+ virtual status_t refreshEntitlements(
+ int32_t refreshType,
+ const CasData &refreshData) = 0;
+
+private:
+ CasPlugin(const CasPlugin &);
+ CasPlugin &operator=(const CasPlugin &); /* NOLINT */
+};
+
+extern "C" {
+ extern android::CasFactory *createCasFactory();
+}
+
+} // namespace android
+
+#endif // CAS_API_H_
diff --git a/include/media/cas/DescramblerAPI.h b/include/media/cas/DescramblerAPI.h
new file mode 100644
index 0000000..0a51952
--- /dev/null
+++ b/include/media/cas/DescramblerAPI.h
@@ -0,0 +1,105 @@
+/*
+ * 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 DESCRAMBLER_API_H_
+#define DESCRAMBLER_API_H_
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/cas/CasAPI.h>
+
+namespace android {
+
+struct AString;
+struct DescramblerPlugin;
+
+struct DescramblerFactory {
+ DescramblerFactory() {}
+ virtual ~DescramblerFactory() {}
+
+ // Determine if the plugin can handle the CA scheme identified by CA_system_id.
+ virtual bool isSystemIdSupported(
+ int32_t CA_system_id) const = 0;
+
+ // Construct a new instance of a DescramblerPlugin given a CA_system_id
+ virtual status_t createPlugin(
+ int32_t CA_system_id, DescramblerPlugin **plugin) = 0;
+
+private:
+ DescramblerFactory(const DescramblerFactory &);
+ DescramblerFactory &operator=(const DescramblerFactory &);
+};
+
+struct DescramblerPlugin {
+ enum ScramblingControl {
+ kScrambling_Unscrambled = 0,
+ kScrambling_Reserved = 1,
+ kScrambling_EvenKey = 2,
+ kScrambling_OddKey = 3,
+ };
+
+ struct SubSample {
+ uint32_t mNumBytesOfClearData;
+ uint32_t mNumBytesOfEncryptedData;
+ };
+
+ DescramblerPlugin() {}
+ virtual ~DescramblerPlugin() {}
+
+ // If this method returns false, a non-secure decoder will be used to
+ // decode the data after decryption. The decrypt API below will have
+ // to support insecure decryption of the data (secure = false) for
+ // media data of the given mime type.
+ virtual bool requiresSecureDecoderComponent(const char *mime) const = 0;
+
+ // A MediaCas session may be associated with a MediaCrypto session. The
+ // associated MediaCas session is used to load decryption keys
+ // into the crypto/cas plugin. The keys are then referenced by key-id
+ // in the 'key' parameter to the decrypt() method.
+ // Should return NO_ERROR on success, ERROR_DRM_SESSION_NOT_OPENED if
+ // the session is not opened and a code from MediaErrors.h otherwise.
+ virtual status_t setMediaCasSession(const CasSessionId& sessionId) = 0;
+
+ // If the error returned falls into the range
+ // ERROR_DRM_VENDOR_MIN..ERROR_DRM_VENDOR_MAX, errorDetailMsg should be
+ // filled in with an appropriate string.
+ // At the java level these special errors will then trigger a
+ // MediaCodec.CryptoException that gives clients access to both
+ // the error code and the errorDetailMsg.
+ // Returns a non-negative result to indicate the number of bytes written
+ // to the dstPtr, or a negative result to indicate an error.
+ virtual ssize_t descramble(
+ bool secure,
+ ScramblingControl scramblingControl,
+ size_t numSubSamples,
+ const SubSample *subSamples,
+ const void *srcPtr,
+ int32_t srcOffset,
+ void *dstPtr,
+ int32_t dstOffset,
+ AString *errorDetailMsg) = 0;
+
+private:
+ DescramblerPlugin(const DescramblerPlugin &);
+ DescramblerPlugin &operator=(const DescramblerPlugin &);
+};
+
+} // namespace android
+
+extern "C" {
+ extern android::DescramblerFactory *createDescramblerFactory();
+}
+
+#endif // DESCRAMBLER_API_H_
diff --git a/include/media/hardware/HDCPAPI.h b/include/media/hardware/HDCPAPI.h
index 3a53e9f..30cd5fd 100644
--- a/include/media/hardware/HDCPAPI.h
+++ b/include/media/hardware/HDCPAPI.h
@@ -73,7 +73,7 @@
// Module can call the notification function to signal completion/failure
// of asynchronous operations (such as initialization) or out of band
// events.
- HDCPModule(void *cookie, ObserverFunc observerNotify) {};
+ HDCPModule(void * /*cookie*/, ObserverFunc /*observerNotify*/) {};
virtual ~HDCPModule() {};
@@ -104,8 +104,8 @@
// 1 for the second and so on)
// inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
virtual status_t encrypt(
- const void *inData, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData) {
+ const void * /*inData*/, size_t /*size*/, uint32_t /*streamCTR*/,
+ uint64_t * /*outInputCTR*/, void * /*outData*/) {
return INVALID_OPERATION;
}
@@ -119,8 +119,8 @@
// 1 for the second and so on)
// inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
virtual status_t encryptNative(
- buffer_handle_t buffer, size_t offset, size_t size,
- uint32_t streamCTR, uint64_t *outInputCTR, void *outData) {
+ buffer_handle_t /*buffer*/, size_t /*offset*/, size_t /*size*/,
+ uint32_t /*streamCTR*/, uint64_t * /*outInputCTR*/, void * /*outData*/) {
return INVALID_OPERATION;
}
// DECRYPTION only:
@@ -133,9 +133,9 @@
// until outData contains size bytes of decrypted data.
// Both streamCTR and inputCTR will be provided by the caller.
virtual status_t decrypt(
- const void *inData, size_t size,
- uint32_t streamCTR, uint64_t inputCTR,
- void *outData) {
+ const void * /*inData*/, size_t /*size*/,
+ uint32_t /*streamCTR*/, uint64_t /*inputCTR*/,
+ void * /*outData*/) {
return INVALID_OPERATION;
}
diff --git a/include/media/hardware/HardwareAPI.h b/include/media/hardware/HardwareAPI.h
index 2c50ad6..cecf715 100644
--- a/include/media/hardware/HardwareAPI.h
+++ b/include/media/hardware/HardwareAPI.h
@@ -270,7 +270,7 @@
// output: fill out the MediaImage fields
MediaImage sMediaImage;
- DescribeColorFormatParams(const DescribeColorFormat2Params&); // for internal use only
+ explicit DescribeColorFormatParams(const DescribeColorFormat2Params&); // for internal use only
};
// A pointer to this struct is passed to OMX_GetParameter when the extension
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/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index 7ae07ad..56d7cc8 100644
--- a/include/media/openmax/OMX_AsString.h
+++ b/include/media/openmax/OMX_AsString.h
@@ -107,6 +107,7 @@
case OMX_AUDIO_AACObjectLTP: return "LTP";
case OMX_AUDIO_AACObjectHE: return "HE";
case OMX_AUDIO_AACObjectScalable: return "Scalable";
+ case OMX_AUDIO_AACObjectER_Scalable: return "ER_Scalable";
case OMX_AUDIO_AACObjectERLC: return "ERLC";
case OMX_AUDIO_AACObjectLD: return "LD";
case OMX_AUDIO_AACObjectHE_PS: return "HE_PS";
@@ -530,9 +531,12 @@
// case OMX_IndexConfigCallbackRequest: return "ConfigCallbackRequest";
// case OMX_IndexConfigCommitMode: return "ConfigCommitMode";
// case OMX_IndexConfigCommit: return "ConfigCommit";
+ case OMX_IndexConfigAndroidVendorExtension: return "ConfigAndroidVendorExtension";
case OMX_IndexParamAudioAndroidAc3: return "ParamAudioAndroidAc3";
case OMX_IndexParamAudioAndroidOpus: return "ParamAudioAndroidOpus";
case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation";
+ case OMX_IndexParamAudioAndroidEac3: return "ParamAudioAndroidEac3";
+ case OMX_IndexParamAudioProfileQuerySupported: return "ParamAudioProfileQuerySupported";
// case OMX_IndexParamNalStreamFormatSupported: return "ParamNalStreamFormatSupported";
// case OMX_IndexParamNalStreamFormat: return "ParamNalStreamFormat";
// case OMX_IndexParamNalStreamFormatSelect: return "ParamNalStreamFormatSelect";
@@ -545,10 +549,15 @@
case OMX_IndexConfigAndroidIntraRefresh: return "ConfigAndroidIntraRefresh";
case OMX_IndexParamAndroidVideoTemporalLayering: return "ParamAndroidVideoTemporalLayering";
case OMX_IndexConfigAndroidVideoTemporalLayering: return "ConfigAndroidVideoTemporalLayering";
+ case OMX_IndexParamMaxFrameDurationForBitrateControl:
+ return "ParamMaxFrameDurationForBitrateControl";
+ case OMX_IndexParamVideoVp9: return "ParamVideoVp9";
+ case OMX_IndexParamVideoAndroidVp9Encoder: return "ParamVideoAndroidVp9Encoder";
case OMX_IndexConfigAutoFramerateConversion: return "ConfigAutoFramerateConversion";
case OMX_IndexConfigPriority: return "ConfigPriority";
case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate";
case OMX_IndexParamConsumerUsageBits: return "ParamConsumerUsageBits";
+ case OMX_IndexConfigLatency: return "ConfigLatency";
default: return asString((OMX_INDEXTYPE)i, def);
}
}
diff --git a/include/media/openmax/OMX_Audio.h b/include/media/openmax/OMX_Audio.h
index d8bee76..9c0296b 100644
--- a/include/media/openmax/OMX_Audio.h
+++ b/include/media/openmax/OMX_Audio.h
@@ -259,6 +259,7 @@
OMX_AUDIO_AACObjectHE, /**< AAC High Efficiency (object type SBR, HE-AAC profile) */
OMX_AUDIO_AACObjectScalable, /**< AAC Scalable object */
OMX_AUDIO_AACObjectERLC = 17, /**< ER AAC Low Complexity object (Error Resilient AAC-LC) */
+ OMX_AUDIO_AACObjectER_Scalable = 20, /**< ER AAC scalable object */
OMX_AUDIO_AACObjectLD = 23, /**< AAC Low Delay object (Error Resilient) */
OMX_AUDIO_AACObjectHE_PS = 29, /**< AAC High Efficiency with Parametric Stereo coding (HE-AAC v2, object type PS) */
OMX_AUDIO_AACObjectELD = 39, /** AAC Enhanced Low Delay. NOTE: Pending Khronos standardization **/
diff --git a/include/media/openmax/OMX_Core.h b/include/media/openmax/OMX_Core.h
index 88dd585..bb974b3 100644
--- a/include/media/openmax/OMX_Core.h
+++ b/include/media/openmax/OMX_Core.h
@@ -738,7 +738,7 @@
pComponentVersion, \
pSpecVersion, \
pComponentUUID) \
- ((OMX_COMPONENTTYPE*)hComponent)->GetComponentVersion( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->GetComponentVersion(\
hComponent, \
pComponentName, \
pComponentVersion, \
@@ -804,7 +804,7 @@
Cmd, \
nParam, \
pCmdData) \
- ((OMX_COMPONENTTYPE*)hComponent)->SendCommand( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->SendCommand( \
hComponent, \
Cmd, \
nParam, \
@@ -843,8 +843,8 @@
#define OMX_GetParameter( \
hComponent, \
nParamIndex, \
- pComponentParameterStructure) \
- ((OMX_COMPONENTTYPE*)hComponent)->GetParameter( \
+ pComponentParameterStructure) \
+ ((OMX_COMPONENTTYPE*)(hComponent))->GetParameter( \
hComponent, \
nParamIndex, \
pComponentParameterStructure) /* Macro End */
@@ -882,8 +882,8 @@
#define OMX_SetParameter( \
hComponent, \
nParamIndex, \
- pComponentParameterStructure) \
- ((OMX_COMPONENTTYPE*)hComponent)->SetParameter( \
+ pComponentParameterStructure) \
+ ((OMX_COMPONENTTYPE*)(hComponent))->SetParameter( \
hComponent, \
nParamIndex, \
pComponentParameterStructure) /* Macro End */
@@ -918,8 +918,8 @@
#define OMX_GetConfig( \
hComponent, \
nConfigIndex, \
- pComponentConfigStructure) \
- ((OMX_COMPONENTTYPE*)hComponent)->GetConfig( \
+ pComponentConfigStructure) \
+ ((OMX_COMPONENTTYPE*)(hComponent))->GetConfig( \
hComponent, \
nConfigIndex, \
pComponentConfigStructure) /* Macro End */
@@ -954,8 +954,8 @@
#define OMX_SetConfig( \
hComponent, \
nConfigIndex, \
- pComponentConfigStructure) \
- ((OMX_COMPONENTTYPE*)hComponent)->SetConfig( \
+ pComponentConfigStructure) \
+ ((OMX_COMPONENTTYPE*)(hComponent))->SetConfig( \
hComponent, \
nConfigIndex, \
pComponentConfigStructure) /* Macro End */
@@ -989,7 +989,7 @@
hComponent, \
cParameterName, \
pIndexType) \
- ((OMX_COMPONENTTYPE*)hComponent)->GetExtensionIndex( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->GetExtensionIndex( \
hComponent, \
cParameterName, \
pIndexType) /* Macro End */
@@ -1015,7 +1015,7 @@
#define OMX_GetState( \
hComponent, \
pState) \
- ((OMX_COMPONENTTYPE*)hComponent)->GetState( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->GetState( \
hComponent, \
pState) /* Macro End */
@@ -1046,7 +1046,7 @@
pAppPrivate, \
nSizeBytes, \
pBuffer) \
- ((OMX_COMPONENTTYPE*)hComponent)->UseBuffer( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->UseBuffer( \
hComponent, \
ppBufferHdr, \
nPortIndex, \
@@ -1088,7 +1088,7 @@
nPortIndex, \
pAppPrivate, \
nSizeBytes) \
- ((OMX_COMPONENTTYPE*)hComponent)->AllocateBuffer( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->AllocateBuffer( \
hComponent, \
ppBuffer, \
nPortIndex, \
@@ -1122,7 +1122,7 @@
hComponent, \
nPortIndex, \
pBuffer) \
- ((OMX_COMPONENTTYPE*)hComponent)->FreeBuffer( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->FreeBuffer( \
hComponent, \
nPortIndex, \
pBuffer) /* Macro End */
@@ -1153,7 +1153,7 @@
#define OMX_EmptyThisBuffer( \
hComponent, \
pBuffer) \
- ((OMX_COMPONENTTYPE*)hComponent)->EmptyThisBuffer( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->EmptyThisBuffer( \
hComponent, \
pBuffer) /* Macro End */
@@ -1183,7 +1183,7 @@
#define OMX_FillThisBuffer( \
hComponent, \
pBuffer) \
- ((OMX_COMPONENTTYPE*)hComponent)->FillThisBuffer( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->FillThisBuffer( \
hComponent, \
pBuffer) /* Macro End */
@@ -1225,7 +1225,7 @@
nPortIndex, \
pAppPrivate, \
eglImage) \
- ((OMX_COMPONENTTYPE*)hComponent)->UseEGLImage( \
+ ((OMX_COMPONENTTYPE*)(hComponent))->UseEGLImage( \
hComponent, \
ppBufferHdr, \
nPortIndex, \
diff --git a/include/media/openmax/OMX_Index.h b/include/media/openmax/OMX_Index.h
index 1a2a548..5be1355 100644
--- a/include/media/openmax/OMX_Index.h
+++ b/include/media/openmax/OMX_Index.h
@@ -98,10 +98,13 @@
OMX_IndexParamMetadataKeyFilter, /**< reference: OMX_PARAM_METADATAFILTERTYPE */
OMX_IndexConfigPriorityMgmt, /**< reference: OMX_PRIORITYMGMTTYPE */
OMX_IndexParamStandardComponentRole, /**< reference: OMX_PARAM_COMPONENTROLETYPE */
+ OMX_IndexComponentEndUnused,
OMX_IndexPortStartUnused = 0x02000000,
OMX_IndexParamPortDefinition, /**< reference: OMX_PARAM_PORTDEFINITIONTYPE */
OMX_IndexParamCompBufferSupplier, /**< reference: OMX_PARAM_BUFFERSUPPLIERTYPE */
+ OMX_IndexPortEndUnused,
+
OMX_IndexReservedStartUnused = 0x03000000,
/* Audio parameters and configurations */
@@ -134,6 +137,7 @@
OMX_IndexParamAudioSmv, /**< reference: OMX_AUDIO_PARAM_SMVTYPE */
OMX_IndexParamAudioVorbis, /**< reference: OMX_AUDIO_PARAM_VORBISTYPE */
OMX_IndexParamAudioFlac, /**< reference: OMX_AUDIO_PARAM_FLACTYPE */
+ OMX_IndexAudioEndUnused,
OMX_IndexConfigAudioMidiImmediateEvent, /**< reference: OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE */
OMX_IndexConfigAudioMidiControl, /**< reference: OMX_AUDIO_CONFIG_MIDICONTROLTYPE */
@@ -194,6 +198,7 @@
OMX_IndexParamVideoSliceFMO, /**< reference: OMX_VIDEO_PARAM_AVCSLICEFMO */
OMX_IndexConfigVideoAVCIntraPeriod, /**< reference: OMX_VIDEO_CONFIG_AVCINTRAPERIOD */
OMX_IndexConfigVideoNalSize, /**< reference: OMX_VIDEO_CONFIG_NALSIZE */
+ OMX_IndexVideoEndUnused,
/* Image & Video common Configurations */
OMX_IndexCommonStartUnused = 0x07000000,
@@ -231,6 +236,7 @@
OMX_IndexConfigCommonFocusRegion, /**< reference: OMX_CONFIG_FOCUSREGIONTYPE */
OMX_IndexConfigCommonFocusStatus, /**< reference: OMX_PARAM_FOCUSSTATUSTYPE */
OMX_IndexConfigCommonTransitionEffect, /**< reference: OMX_CONFIG_TRANSITIONEFFECTTYPE */
+ OMX_IndexCommonEndUnused,
/* Reserved Configuration range */
OMX_IndexOtherStartUnused = 0x08000000,
diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index b688d1d..5a029d0 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -51,6 +51,7 @@
OMX_IndexConfigCallbackRequest, /**< reference: OMX_CONFIG_CALLBACKREQUESTTYPE */
OMX_IndexConfigCommitMode, /**< reference: OMX_CONFIG_COMMITMODETYPE */
OMX_IndexConfigCommit, /**< reference: OMX_CONFIG_COMMITTYPE */
+ OMX_IndexConfigAndroidVendorExtension, /**< reference: OMX_CONFIG_VENDOR_EXTENSIONTYPE */
/* Port parameters and configurations */
OMX_IndexExtPortStartUnused = OMX_IndexKhronosExtensions + 0x00200000,
@@ -62,6 +63,7 @@
OMX_IndexParamAudioAndroidAacPresentation, /**< reference: OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE */
OMX_IndexParamAudioAndroidEac3, /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */
OMX_IndexParamAudioProfileQuerySupported, /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */
+ OMX_IndexExtAudioEndUnused,
/* Image parameters and configurations */
OMX_IndexExtImageStartUnused = OMX_IndexKhronosExtensions + 0x00500000,
@@ -80,6 +82,10 @@
OMX_IndexConfigAndroidIntraRefresh, /**< reference: OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE */
OMX_IndexParamAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE */
OMX_IndexConfigAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE */
+ OMX_IndexParamMaxFrameDurationForBitrateControl,/**< reference: OMX_PARAM_U32TYPE */
+ OMX_IndexParamVideoVp9, /**< reference: OMX_VIDEO_PARAM_VP9TYPE */
+ OMX_IndexParamVideoAndroidVp9Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP9ENCODERTYPE */
+ OMX_IndexExtVideoEndUnused,
/* Image & Video common configurations */
OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000,
@@ -90,6 +96,8 @@
OMX_IndexConfigPriority, /**< reference: OMX_PARAM_U32TYPE */
OMX_IndexConfigOperatingRate, /**< reference: OMX_PARAM_U32TYPE in Q16 format for video and in Hz for audio */
OMX_IndexParamConsumerUsageBits, /**< reference: OMX_PARAM_U32TYPE */
+ OMX_IndexConfigLatency, /**< reference: OMX_PARAM_U32TYPE */
+ OMX_IndexExtOtherEndUnused,
/* Time configurations */
OMX_IndexExtTimeStartUnused = OMX_IndexKhronosExtensions + 0x00900000,
@@ -97,6 +105,117 @@
OMX_IndexExtMax = 0x7FFFFFFF
} OMX_INDEXEXTTYPE;
+#define OMX_MAX_STRINGVALUE_SIZE OMX_MAX_STRINGNAME_SIZE
+#define OMX_MAX_ANDROID_VENDOR_PARAMCOUNT 32
+
+typedef enum OMX_ANDROID_VENDOR_VALUETYPE {
+ OMX_AndroidVendorValueInt32 = 0, /*<< int32_t value */
+ OMX_AndroidVendorValueInt64, /*<< int64_t value */
+ OMX_AndroidVendorValueString, /*<< string value */
+ OMX_AndroidVendorValueEndUnused,
+} OMX_ANDROID_VENDOR_VALUETYPE;
+
+/**
+ * Structure describing a single value of an Android vendor extension.
+ *
+ * STRUCTURE MEMBERS:
+ * cKey : parameter value name.
+ * eValueType : parameter value type
+ * bSet : if false, the parameter is not set (for OMX_GetConfig) or is unset (OMX_SetConfig)
+ * if true, the parameter is set to the corresponding value below
+ * nInt64 : int64 value
+ * cString : string value
+ */
+typedef struct OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE {
+ OMX_U8 cKey[OMX_MAX_STRINGNAME_SIZE];
+ OMX_ANDROID_VENDOR_VALUETYPE eValueType;
+ OMX_BOOL bSet;
+ union {
+ OMX_S32 nInt32;
+ OMX_S64 nInt64;
+ OMX_U8 cString[OMX_MAX_STRINGVALUE_SIZE];
+ };
+} OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE;
+
+/**
+ * OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE is the structure for an Android vendor extension
+ * supported by the component. This structure enumerates the various extension parameters and their
+ * values.
+ *
+ * Android vendor extensions have a name and one or more parameter values - each with a string key -
+ * that are set together. The values are exposed to Android applications via a string key that is
+ * the concatenation of 'vendor', the extension name and the parameter key, each separated by dot
+ * (.), with any trailing '.value' suffix(es) removed (though optionally allowed).
+ *
+ * Extension names and parameter keys are subject to the following rules:
+ * - Each SHALL contain a set of lowercase alphanumeric (underscore allowed) tags separated by
+ * dot (.) or dash (-).
+ * - The first character of the first tag, and any tag following a dot SHALL not start with a
+ * digit.
+ * - Tags 'value', 'vendor', 'omx' and 'android' (even if trailed and/or followed by any number
+ * of underscores) are prohibited in the extension name.
+ * - Tags 'vendor', 'omx' and 'android' (even if trailed and/or followed by any number
+ * of underscores) are prohibited in parameter keys.
+ * - The tag 'value' (even if trailed and/or followed by any number
+ * of underscores) is prohibited in parameter keys with the following exception:
+ * the parameter key may be exactly 'value'
+ * - The parameter key for extensions with a single parameter value SHALL be 'value'
+ * - No two extensions SHALL have the same name
+ * - No extension's name SHALL start with another extension's NAME followed by a dot (.)
+ * - No two parameters of an extension SHALL have the same key
+ *
+ * This config can be used with both OMX_GetConfig and OMX_SetConfig. In the OMX_GetConfig
+ * case, the caller specifies nIndex and nParamSizeUsed. The component fills in cName,
+ * eDir and nParamCount. Additionally, if nParamSizeUsed is not less than nParamCount, the
+ * component fills out the parameter values (nParam) with the current values for each parameter
+ * of the vendor extension.
+ *
+ * The value of nIndex goes from 0 to N-1, where N is the number of Android vendor extensions
+ * supported by the component. The component does not need to report N as the caller can determine
+ * N by enumerating all extensions supported by the component. The component may not support any
+ * extensions. If there are no more extensions, OMX_GetParameter returns OMX_ErrorNoMore. The
+ * component supplies extensions in the order it wants clients to set them.
+ *
+ * The component SHALL return OMX_ErrorNone for all cases where nIndex is less than N (specifically
+ * even in the case of where nParamCount is greater than nParamSizeUsed).
+ *
+ * In the OMX_SetConfig case the field nIndex is ignored. If the component supports an Android
+ * vendor extension with the name in cName, it SHALL configure the parameter values for that
+ * extension according to the parameters in nParam. nParamCount is the number of valid parameters
+ * in the nParam array, and nParamSizeUsed is the size of the nParam array. (nParamSizeUsed
+ * SHALL be at least nParamCount) Parameters that are part of a vendor extension but are not
+ * in the nParam array are assumed to be unset (this is different from not changed).
+ * All parameter values SHALL have distinct keys in nParam (the component can assume that this
+ * is the case. Otherwise, the actual value for the parameters that are multiply defined can
+ * be any of the set values.)
+ *
+ * Return values in case of OMX_SetConfig:
+ * OMX_ErrorUnsupportedIndex: the component does not support the extension specified by cName
+ * OMX_ErrorUnsupportedSetting: the component does not support some or any of the parameters
+ * (names) specified in nParam
+ * OMX_ErrorBadParameter: the parameter is invalid (e.g. nParamCount is greater than
+ * nParamSizeUsed, or some parameter value has invalid type)
+ *
+ * STRUCTURE MEMBERS:
+ * nSize : size of the structure in bytes
+ * nVersion : OMX specification version information
+ * cName : name of vendor extension
+ * nParamCount : the number of parameter values that are part of this vendor extension
+ * nParamSizeUsed : the size of nParam
+ * (must be at least 1 and at most OMX_MAX_ANDROID_VENDOR_PARAMCOUNT)
+ * param : the parameter values
+ */
+typedef struct OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE {
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nIndex;
+ OMX_U8 cName[OMX_MAX_STRINGNAME_SIZE];
+ OMX_DIRTYPE eDir;
+ OMX_U32 nParamCount;
+ OMX_U32 nParamSizeUsed;
+ OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE param[1];
+} OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE;
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h
index 2c02431..128dd2d 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -75,39 +75,6 @@
OMX_VIDEO_VP8LevelMax = 0x7FFFFFFF
} OMX_VIDEO_VP8LEVELTYPE;
-/** VP9 profiles */
-typedef enum OMX_VIDEO_VP9PROFILETYPE {
- OMX_VIDEO_VP9Profile0 = 0x1,
- OMX_VIDEO_VP9Profile1 = 0x2,
- OMX_VIDEO_VP9Profile2 = 0x4,
- OMX_VIDEO_VP9Profile3 = 0x8,
- // HDR profiles also support passing HDR metadata
- OMX_VIDEO_VP9Profile2HDR = 0x1000,
- OMX_VIDEO_VP9Profile3HDR = 0x2000,
- OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
- OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF
-} OMX_VIDEO_VP9PROFILETYPE;
-
-/** VP9 levels */
-typedef enum OMX_VIDEO_VP9LEVELTYPE {
- OMX_VIDEO_VP9Level1 = 0x1,
- OMX_VIDEO_VP9Level11 = 0x2,
- OMX_VIDEO_VP9Level2 = 0x4,
- OMX_VIDEO_VP9Level21 = 0x8,
- OMX_VIDEO_VP9Level3 = 0x10,
- OMX_VIDEO_VP9Level31 = 0x20,
- OMX_VIDEO_VP9Level4 = 0x40,
- OMX_VIDEO_VP9Level41 = 0x80,
- OMX_VIDEO_VP9Level5 = 0x100,
- OMX_VIDEO_VP9Level51 = 0x200,
- OMX_VIDEO_VP9Level52 = 0x400,
- OMX_VIDEO_VP9Level6 = 0x800,
- OMX_VIDEO_VP9Level61 = 0x1000,
- OMX_VIDEO_VP9Level62 = 0x2000,
- OMX_VIDEO_VP9LevelUnknown = 0x6EFFFFFF,
- OMX_VIDEO_VP9LevelMax = 0x7FFFFFFF
-} OMX_VIDEO_VP9LEVELTYPE;
-
/** VP8 Param */
typedef struct OMX_VIDEO_PARAM_VP8TYPE {
OMX_U32 nSize;
@@ -152,7 +119,7 @@
} OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE;
/**
- * Android specific VP8 encoder params
+ * Android specific VP8/VP9 encoder params
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
@@ -182,6 +149,59 @@
OMX_U32 nMaxQuantizer;
} OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE;
+/** VP9 profiles */
+typedef enum OMX_VIDEO_VP9PROFILETYPE {
+ OMX_VIDEO_VP9Profile0 = 0x1,
+ OMX_VIDEO_VP9Profile1 = 0x2,
+ OMX_VIDEO_VP9Profile2 = 0x4,
+ OMX_VIDEO_VP9Profile3 = 0x8,
+ // HDR profiles also support passing HDR metadata
+ OMX_VIDEO_VP9Profile2HDR = 0x1000,
+ OMX_VIDEO_VP9Profile3HDR = 0x2000,
+ OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF
+} OMX_VIDEO_VP9PROFILETYPE;
+
+/** VP9 levels */
+typedef enum OMX_VIDEO_VP9LEVELTYPE {
+ OMX_VIDEO_VP9Level1 = 0x0,
+ OMX_VIDEO_VP9Level11 = 0x1,
+ OMX_VIDEO_VP9Level2 = 0x2,
+ OMX_VIDEO_VP9Level21 = 0x4,
+ OMX_VIDEO_VP9Level3 = 0x8,
+ OMX_VIDEO_VP9Level31 = 0x10,
+ OMX_VIDEO_VP9Level4 = 0x20,
+ OMX_VIDEO_VP9Level41 = 0x40,
+ OMX_VIDEO_VP9Level5 = 0x80,
+ OMX_VIDEO_VP9Level51 = 0x100,
+ OMX_VIDEO_VP9Level52 = 0x200,
+ OMX_VIDEO_VP9Level6 = 0x400,
+ OMX_VIDEO_VP9Level61 = 0x800,
+ OMX_VIDEO_VP9Level62 = 0x1000,
+ OMX_VIDEO_VP9LevelUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_VP9LevelMax = 0x7FFFFFFF
+} OMX_VIDEO_VP9LEVELTYPE;
+
+/**
+* VP9 Parameters.
+* Encoder specific parameters (decoders should ignore these fields):
+* - bErrorResilientMode
+* - nTileRows
+* - nTileColumns
+* - bEnableFrameParallelDecoding
+*/
+typedef struct OMX_VIDEO_PARAM_VP9TYPE {
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_VIDEO_VP9PROFILETYPE eProfile;
+ OMX_VIDEO_VP9LEVELTYPE eLevel;
+ OMX_BOOL bErrorResilientMode;
+ OMX_U32 nTileRows;
+ OMX_U32 nTileColumns;
+ OMX_BOOL bEnableFrameParallelDecoding;
+} OMX_VIDEO_PARAM_VP9TYPE;
+
/** HEVC Profile enum type */
typedef enum OMX_VIDEO_HEVCPROFILETYPE {
OMX_VIDEO_HEVCProfileUnknown = 0x0,
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
index 461fad7..3c81f0f 100644
--- a/include/powermanager/IPowerManager.h
+++ b/include/powermanager/IPowerManager.h
@@ -44,13 +44,15 @@
NAP = IBinder::FIRST_CALL_TRANSACTION + 10,
IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11,
IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12,
- SET_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 13,
- REBOOT = IBinder::FIRST_CALL_TRANSACTION + 14,
- SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 15,
- CRASH = IBinder::FIRST_CALL_TRANSACTION + 16,
+ GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13,
+ SET_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 14,
+ REBOOT = IBinder::FIRST_CALL_TRANSACTION + 17,
+ REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 18,
+ SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 19,
+ CRASH = IBinder::FIRST_CALL_TRANSACTION + 20,
};
- DECLARE_META_INTERFACE(PowerManager);
+ DECLARE_META_INTERFACE(PowerManager)
// The parcels created by these methods must be kept in sync with the
// corresponding methods from IPowerManager.aidl.
diff --git a/include/private/binder b/include/private/binder
new file mode 120000
index 0000000..09e9076
--- /dev/null
+++ b/include/private/binder
@@ -0,0 +1 @@
+../../libs/binder/include/private/binder
\ No newline at end of file
diff --git a/include/private/binder/Static.h b/include/private/binder/Static.h
deleted file mode 100644
index d104646..0000000
--- a/include/private/binder/Static.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-// All static variables go here, to control initialization and
-// destruction order in the library.
-
-#include <utils/threads.h>
-
-#include <binder/IBinder.h>
-#include <binder/IMemory.h>
-#include <binder/ProcessState.h>
-#include <binder/IPermissionController.h>
-#include <binder/IServiceManager.h>
-
-namespace android {
-
-// For TextStream.cpp
-extern Vector<int32_t> gTextBuffers;
-
-// For ProcessState.cpp
-extern Mutex gProcessMutex;
-extern sp<ProcessState> gProcess;
-
-// For IServiceManager.cpp
-extern Mutex gDefaultServiceManagerLock;
-extern sp<IServiceManager> gDefaultServiceManager;
-extern sp<IPermissionController> gPermissionController;
-
-} // namespace android
diff --git a/include/private/binder/binder_module.h b/include/private/binder/binder_module.h
deleted file mode 100644
index a8dd64f..0000000
--- a/include/private/binder/binder_module.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2008 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 _BINDER_MODULE_H_
-#define _BINDER_MODULE_H_
-
-#ifdef __cplusplus
-namespace android {
-#endif
-
-/* obtain structures and constants from the kernel header */
-
-#include <sys/ioctl.h>
-#include <linux/binder.h>
-
-#ifdef __cplusplus
-} // namespace android
-#endif
-
-#endif // _BINDER_MODULE_H_
diff --git a/include/private/gui/ComposerService.h b/include/private/gui/ComposerService.h
index ff2f9bf..50bd742 100644
--- a/include/private/gui/ComposerService.h
+++ b/include/private/gui/ComposerService.h
@@ -28,7 +28,6 @@
// ---------------------------------------------------------------------------
-class IMemoryHeap;
class ISurfaceComposer;
// ---------------------------------------------------------------------------
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 4b3fcc6..307c764 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -24,6 +24,7 @@
#include <ui/Region.h>
#include <ui/Rect.h>
+#include <gui/IGraphicBufferProducer.h>
namespace android {
@@ -56,6 +57,9 @@
eFinalCropChanged = 0x00000400,
eOverrideScalingModeChanged = 0x00000800,
eGeometryAppliesWithResize = 0x00001000,
+ eReparentChildren = 0x00002000,
+ eDetachChildren = 0x00004000,
+ eRelativeLayerChanged = 0x00008000
};
layer_state_t()
@@ -74,16 +78,16 @@
status_t read(const Parcel& input);
struct matrix22_t {
- float dsdx;
- float dtdx;
- float dsdy;
- float dtdy;
+ float dsdx{0};
+ float dtdx{0};
+ float dtdy{0};
+ float dsdy{0};
};
sp<IBinder> surface;
uint32_t what;
float x;
float y;
- uint32_t z;
+ int32_t z;
uint32_t w;
uint32_t h;
uint32_t layerStack;
@@ -94,9 +98,15 @@
matrix22_t matrix;
Rect crop;
Rect finalCrop;
- sp<IBinder> handle;
+ sp<IBinder> barrierHandle;
+ sp<IBinder> reparentHandle;
uint64_t frameNumber;
int32_t overrideScalingMode;
+
+ sp<IGraphicBufferProducer> barrierGbp;
+
+ sp<IBinder> relativeLayerHandle;
+
// non POD must be last. see write/read
Region transparentRegion;
};
diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h
index 84eb100..a86c586 100644
--- a/include/private/ui/RegionHelper.h
+++ b/include/private/ui/RegionHelper.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
#define ANDROID_UI_PRIVATE_REGION_HELPER_H
+#include <limits>
#include <stdint.h>
#include <sys/types.h>
@@ -27,10 +28,10 @@
class region_operator
{
public:
- typedef typename RECT::value_type TYPE;
- static const TYPE max_value = 0x7FFFFFF;
+ typedef typename RECT::value_type TYPE;
+ static const TYPE max_value = std::numeric_limits<TYPE>::max();
- /*
+ /*
* Common boolean operations:
* value is computed as 0b101 op 0b110
* other boolean operation are possible, simply compute
@@ -53,20 +54,20 @@
TYPE dy;
inline region(const region& rhs)
: rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { }
- inline region(RECT const* r, size_t c)
- : rects(r), count(c), dx(), dy() { }
- inline region(RECT const* r, size_t c, TYPE dx, TYPE dy)
- : rects(r), count(c), dx(dx), dy(dy) { }
+ inline region(RECT const* _r, size_t _c)
+ : rects(_r), count(_c), dx(), dy() { }
+ inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy)
+ : rects(_r), count(_c), dx(_dx), dy(_dy) { }
};
class region_rasterizer {
friend class region_operator;
virtual void operator()(const RECT& rect) = 0;
public:
- virtual ~region_rasterizer() { };
+ virtual ~region_rasterizer() { }
};
- inline region_operator(int op, const region& lhs, const region& rhs)
+ inline region_operator(uint32_t op, const region& lhs, const region& rhs)
: op_mask(op), spanner(lhs, rhs)
{
}
@@ -79,8 +80,8 @@
spannerInner.prepare(inside);
do {
TYPE left, right;
- int inside = spannerInner.next(current.left, current.right);
- if ((op_mask >> inside) & 1) {
+ int inner_inside = spannerInner.next(current.left, current.right);
+ if ((op_mask >> inner_inside) & 1) {
if (current.left < current.right &&
current.top < current.bottom) {
rasterizer(current);
@@ -162,8 +163,8 @@
region rhs;
public:
- inline Spanner(const region& lhs, const region& rhs)
- : lhs(lhs), rhs(rhs)
+ inline Spanner(const region& _lhs, const region& _rhs)
+ : lhs(_lhs), rhs(_rhs)
{
if (lhs.count) {
SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
@@ -223,8 +224,8 @@
region rhs;
public:
- inline SpannerInner(const region& lhs, const region& rhs)
- : lhs(lhs), rhs(rhs)
+ inline SpannerInner(const region& _lhs, const region& _rhs)
+ : lhs(_lhs), rhs(_rhs)
{
}
diff --git a/include/ui/ANativeObjectBase.h b/include/ui/ANativeObjectBase.h
index 76e850f..640e34b 100644
--- a/include/ui/ANativeObjectBase.h
+++ b/include/ui/ANativeObjectBase.h
@@ -18,9 +18,7 @@
#define ANDROID_ANDROID_NATIVES_H
#include <sys/types.h>
-#include <string.h>
-#include <hardware/gralloc.h>
#include <system/window.h>
// ---------------------------------------------------------------------------
diff --git a/include/ui/BufferQueueDefs.h b/include/ui/BufferQueueDefs.h
new file mode 100644
index 0000000..56de181
--- /dev/null
+++ b/include/ui/BufferQueueDefs.h
@@ -0,0 +1,29 @@
+/*
+ * 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_UI_BUFFERQUEUEDEFS_H
+#define ANDROID_UI_BUFFERQUEUEDEFS_H
+
+namespace android {
+ namespace BufferQueueDefs {
+ // BufferQueue will keep track of at most this value of buffers.
+ // Attempts at runtime to increase the number of buffers past this
+ // will fail.
+ static constexpr int NUM_BUFFER_SLOTS = 64;
+ } // namespace BufferQueueDefs
+} // namespace android
+
+#endif
diff --git a/include/ui/ColorSpace.h b/include/ui/ColorSpace.h
new file mode 100644
index 0000000..8ccf6d3
--- /dev/null
+++ b/include/ui/ColorSpace.h
@@ -0,0 +1,305 @@
+/*
+ * 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_UI_COLOR_SPACE
+#define ANDROID_UI_COLOR_SPACE
+
+#include <array>
+#include <cmath>
+#include <functional>
+#include <memory>
+#include <string>
+
+#include <math/mat3.h>
+#include <math/scalar.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+
+namespace android {
+
+class ColorSpace {
+public:
+ typedef std::function<float(float)> transfer_function;
+ typedef std::function<float(float)> clamping_function;
+
+ struct TransferParameters {
+ float g = 0.0f;
+ float a = 0.0f;
+ float b = 0.0f;
+ float c = 0.0f;
+ float d = 0.0f;
+ float e = 0.0f;
+ float f = 0.0f;
+ };
+
+ /**
+ * Creates a named color space with the specified RGB->XYZ
+ * conversion matrix. The white point and primaries will be
+ * computed from the supplied matrix.
+ *
+ * The default transfer functions are a linear response x->x
+ * and the default clamping function is a simple saturate
+ * (clamp(x, 0, 1)).
+ */
+ ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ transfer_function OETF = linearResponse,
+ transfer_function EOTF = linearResponse,
+ clamping_function clamper = saturate<float>
+ ) noexcept;
+
+ /**
+ * Creates a named color space with the specified RGB->XYZ
+ * conversion matrix. The white point and primaries will be
+ * computed from the supplied matrix.
+ *
+ * The transfer functions are defined by the set of supplied
+ * transfer parameters. The default clamping function is a
+ * simple saturate (clamp(x, 0, 1)).
+ */
+ ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ const TransferParameters parameters,
+ clamping_function clamper = saturate<float>
+ ) noexcept;
+
+ /**
+ * Creates a named color space with the specified RGB->XYZ
+ * conversion matrix. The white point and primaries will be
+ * computed from the supplied matrix.
+ *
+ * The transfer functions are defined by a simple gamma value.
+ * The default clamping function is a saturate (clamp(x, 0, 1)).
+ */
+ ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ float gamma,
+ clamping_function clamper = saturate<float>
+ ) noexcept;
+
+ /**
+ * Creates a named color space with the specified primaries
+ * and white point. The RGB<>XYZ conversion matrices are
+ * computed from the primaries and white point.
+ *
+ * The default transfer functions are a linear response x->x
+ * and the default clamping function is a simple saturate
+ * (clamp(x, 0, 1)).
+ */
+ ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ transfer_function OETF = linearResponse,
+ transfer_function EOTF = linearResponse,
+ clamping_function clamper = saturate<float>
+ ) noexcept;
+
+ /**
+ * Creates a named color space with the specified primaries
+ * and white point. The RGB<>XYZ conversion matrices are
+ * computed from the primaries and white point.
+ *
+ * The transfer functions are defined by the set of supplied
+ * transfer parameters. The default clamping function is a
+ * simple saturate (clamp(x, 0, 1)).
+ */
+ ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ const TransferParameters parameters,
+ clamping_function clamper = saturate<float>
+ ) noexcept;
+
+ /**
+ * Creates a named color space with the specified primaries
+ * and white point. The RGB<>XYZ conversion matrices are
+ * computed from the primaries and white point.
+ *
+ * The transfer functions are defined by a single gamma value.
+ * The default clamping function is a saturate (clamp(x, 0, 1)).
+ */
+ ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ float gamma,
+ clamping_function clamper = saturate<float>
+ ) noexcept;
+
+ ColorSpace() noexcept = delete;
+
+ /**
+ * Encodes the supplied RGB value using this color space's
+ * opto-electronic transfer function.
+ */
+ constexpr float3 fromLinear(const float3& v) const noexcept {
+ return apply(v, mOETF);
+ }
+
+ /**
+ * Decodes the supplied RGB value using this color space's
+ * electro-optical transfer function.
+ */
+ constexpr float3 toLinear(const float3& v) const noexcept {
+ return apply(v, mEOTF);
+ }
+
+ /**
+ * Converts the supplied XYZ value to RGB. The returned value
+ * is encoded with this color space's opto-electronic transfer
+ * function and clamped by this color space's clamping function.
+ */
+ constexpr float3 xyzToRGB(const float3& xyz) const noexcept {
+ return apply(fromLinear(mXYZtoRGB * xyz), mClamper);
+ }
+
+ /**
+ * Converts the supplied RGB value to XYZ. The input RGB value
+ * is decoded using this color space's electro-optical function
+ * before being converted to XYZ.
+ */
+ constexpr float3 rgbToXYZ(const float3& rgb) const noexcept {
+ return mRGBtoXYZ * toLinear(rgb);
+ }
+
+ constexpr const std::string& getName() const noexcept {
+ return mName;
+ }
+
+ constexpr const mat3& getRGBtoXYZ() const noexcept {
+ return mRGBtoXYZ;
+ }
+
+ constexpr const mat3& getXYZtoRGB() const noexcept {
+ return mXYZtoRGB;
+ }
+
+ constexpr const transfer_function& getOETF() const noexcept {
+ return mOETF;
+ }
+
+ constexpr const transfer_function& getEOTF() const noexcept {
+ return mEOTF;
+ }
+
+ constexpr const clamping_function& getClamper() const noexcept {
+ return mClamper;
+ }
+
+ constexpr const std::array<float2, 3>& getPrimaries() const noexcept {
+ return mPrimaries;
+ }
+
+ constexpr const float2& getWhitePoint() const noexcept {
+ return mWhitePoint;
+ }
+
+ constexpr const TransferParameters& getTransferParameters() const noexcept {
+ return mParameters;
+ }
+
+ /**
+ * Converts the supplied XYZ value to xyY.
+ */
+ static constexpr float2 xyY(const float3& XYZ) {
+ return XYZ.xy / dot(XYZ, float3{1});
+ }
+
+ /**
+ * Converts the supplied xyY value to XYZ.
+ */
+ static constexpr float3 XYZ(const float3& xyY) {
+ return float3{(xyY.x * xyY.z) / xyY.y, xyY.z, ((1 - xyY.x - xyY.y) * xyY.z) / xyY.y};
+ }
+
+ static const ColorSpace sRGB();
+ static const ColorSpace linearSRGB();
+ static const ColorSpace extendedSRGB();
+ static const ColorSpace linearExtendedSRGB();
+ static const ColorSpace NTSC();
+ static const ColorSpace BT709();
+ static const ColorSpace BT2020();
+ static const ColorSpace AdobeRGB();
+ static const ColorSpace ProPhotoRGB();
+ static const ColorSpace DisplayP3();
+ static const ColorSpace DCIP3();
+ static const ColorSpace ACES();
+ static const ColorSpace ACEScg();
+
+ // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256)
+ // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B
+ // The generated 3D LUT is meant to be used as a 3D texture and its Y
+ // axis is thus already flipped
+ // The source color space must define its values in the domain [0..1]
+ // The generated LUT transforms from gamma space to gamma space
+ static std::unique_ptr<float3> createLUT(uint32_t size,
+ const ColorSpace& src, const ColorSpace& dst);
+
+private:
+ static constexpr mat3 computeXYZMatrix(
+ const std::array<float2, 3>& primaries, const float2& whitePoint);
+
+ static constexpr float linearResponse(float v) {
+ return v;
+ }
+
+ std::string mName;
+
+ mat3 mRGBtoXYZ;
+ mat3 mXYZtoRGB;
+
+ TransferParameters mParameters;
+ transfer_function mOETF;
+ transfer_function mEOTF;
+ clamping_function mClamper;
+
+ std::array<float2, 3> mPrimaries;
+ float2 mWhitePoint;
+};
+
+class ColorSpaceConnector {
+public:
+ ColorSpaceConnector(const ColorSpace& src, const ColorSpace& dst) noexcept;
+
+ constexpr const ColorSpace& getSource() const noexcept { return mSource; }
+ constexpr const ColorSpace& getDestination() const noexcept { return mDestination; }
+
+ constexpr const mat3& getTransform() const noexcept { return mTransform; }
+
+ constexpr float3 transform(const float3& v) const noexcept {
+ float3 linear = mSource.toLinear(apply(v, mSource.getClamper()));
+ return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper());
+ }
+
+ constexpr float3 transformLinear(const float3& v) const noexcept {
+ float3 linear = apply(v, mSource.getClamper());
+ return apply(mTransform * linear, mDestination.getClamper());
+ }
+
+private:
+ ColorSpace mSource;
+ ColorSpace mDestination;
+ mat3 mTransform;
+};
+
+}; // namespace android
+
+#endif // ANDROID_UI_COLOR_SPACE
diff --git a/include/ui/DebugUtils.h b/include/ui/DebugUtils.h
new file mode 100644
index 0000000..8483808
--- /dev/null
+++ b/include/ui/DebugUtils.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <system/graphics.h>
+
+#include <string>
+
+std::string decodeStandard(android_dataspace dataspace);
+std::string decodeTransfer(android_dataspace dataspace);
+std::string decodeRange(android_dataspace dataspace);
+std::string dataspaceDetails(android_dataspace dataspace);
+std::string decodeColorMode(android_color_mode colormode);
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
index 799944f..94caf6b 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -19,23 +19,22 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Timers.h>
-#include <ui/PixelFormat.h>
+#include <utils/Timers.h>
namespace android {
struct DisplayInfo {
- uint32_t w;
- uint32_t h;
- float xdpi;
- float ydpi;
- float fps;
- float density;
- uint8_t orientation;
- bool secure;
- nsecs_t appVsyncOffset;
- nsecs_t presentationDeadline;
+ uint32_t w{0};
+ uint32_t h{0};
+ float xdpi{0};
+ float ydpi{0};
+ float fps{0};
+ float density{0};
+ uint8_t orientation{0};
+ bool secure{false};
+ nsecs_t appVsyncOffset{0};
+ nsecs_t presentationDeadline{0};
};
/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
diff --git a/include/ui/DisplayStatInfo.h b/include/ui/DisplayStatInfo.h
index 0549a83..09543ec 100644
--- a/include/ui/DisplayStatInfo.h
+++ b/include/ui/DisplayStatInfo.h
@@ -22,8 +22,8 @@
namespace android {
struct DisplayStatInfo {
- nsecs_t vsyncTime;
- nsecs_t vsyncPeriod;
+ nsecs_t vsyncTime{0};
+ nsecs_t vsyncPeriod{0};
};
}; // namespace android
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index 48a7aa3..37811bc 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -18,21 +18,15 @@
#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
// ===========================================================================
@@ -42,6 +36,11 @@
{
public:
static const sp<Fence> NO_FENCE;
+ static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX;
+ static constexpr nsecs_t SIGNAL_TIME_INVALID = -1;
+ static inline bool isValidTimestamp(nsecs_t time) {
+ return time >= 0 && time < INT64_MAX;
+ }
// TIMEOUT_NEVER may be passed to the wait method to indicate that it
// should wait indefinitely for the fence to signal.
@@ -55,7 +54,13 @@
// Construct a new Fence object to manage a given fence file descriptor.
// When the new Fence object is destructed the file descriptor will be
// closed.
- Fence(int fenceFd);
+ 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
@@ -94,30 +99,33 @@
// getSignalTime returns the system monotonic clock time at which the
// fence transitioned to the signaled state. If the fence is not signaled
- // then INT64_MAX is returned. If the fence is invalid or if an error
- // occurs then -1 is returned.
+ // then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an
+ // error occurs then SIGNAL_TIME_INVALID is returned.
nsecs_t getSignalTime() const;
-#if __cplusplus > 201103L
- // hasSignaled returns whether the fence has signaled yet. Prefer this to
+ enum class Status {
+ Invalid, // Fence is invalid
+ Unsignaled, // Fence is valid but has not yet signaled
+ Signaled, // Fence is valid and has signaled
+ };
+
+ // getStatus() returns whether the fence has signaled yet. Prefer this to
// getSignalTime() or wait() if all you care about is whether the fence has
- // signaled. Returns an optional bool, which will have a value if there was
- // no error.
- inline std::experimental::optional<bool> hasSignaled() {
+ // signaled.
+ inline Status getStatus() {
// The sync_wait call underlying wait() has been measured to be
// significantly faster than the sync_fence_info call underlying
// getSignalTime(), which might otherwise appear to be the more obvious
// way to check whether a fence has signaled.
switch (wait(0)) {
case NO_ERROR:
- return true;
+ return Status::Signaled;
case -ETIME:
- return false;
+ return Status::Unsignaled;
default:
- return {};
+ return Status::Invalid;
}
}
-#endif
// Flattenable interface
size_t getFlattenedSize() const;
@@ -130,11 +138,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
new file mode 100644
index 0000000..871fcf2
--- /dev/null
+++ b/include/ui/FenceTime.h
@@ -0,0 +1,208 @@
+/*
+ * 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_FENCE_TIME_H
+#define ANDROID_FENCE_TIME_H
+
+#include <ui/Fence.h>
+#include <utils/Flattenable.h>
+#include <utils/Timers.h>
+
+#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.
+ //
+ // This class is needed because the FenceTime class may not stay
+ // consistent for all steps of the flattening process.
+ //
+ // Not thread safe.
+ struct Snapshot : public Flattenable<Snapshot> {
+ enum class State {
+ EMPTY,
+ FENCE,
+ SIGNAL_TIME,
+ };
+
+ Snapshot() = default; // Creates an empty snapshot.
+ explicit Snapshot(const sp<Fence>& fence);
+ explicit Snapshot(nsecs_t signalTime);
+
+ // Movable.
+ Snapshot(Snapshot&& src) = default;
+ Snapshot& operator=(Snapshot&& src) = default;
+ // Not copyable.
+ Snapshot(const Snapshot& src) = delete;
+ Snapshot& operator=(const Snapshot&& src) = delete;
+
+ // Flattenable implementation.
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+ State state{State::EMPTY};
+ sp<Fence> fence{Fence::NO_FENCE};
+ nsecs_t signalTime{Fence::SIGNAL_TIME_INVALID};
+ };
+
+ static const std::shared_ptr<FenceTime> NO_FENCE;
+
+ explicit FenceTime(const sp<Fence>& fence);
+ explicit FenceTime(sp<Fence>&& fence);
+
+ // Passing in Fence::SIGNAL_TIME_PENDING is not allowed.
+ // Doing so will convert the signalTime to Fence::SIGNAL_TIME_INVALID.
+ explicit FenceTime(nsecs_t signalTime);
+
+ // Do not allow default construction. Share NO_FENCE or explicitly construct
+ // with Fence::SIGNAL_TIME_INVALID instead.
+ FenceTime() = delete;
+
+ // Do not allow copy, assign, or move. Use a shared_ptr to share the
+ // signalTime result. Or use getSnapshot() if a thread-safe copy is really
+ // needed.
+ FenceTime(const FenceTime&) = delete;
+ FenceTime(FenceTime&&) = delete;
+ FenceTime& operator=(const FenceTime&) = delete;
+ FenceTime& operator=(FenceTime&&) = delete;
+
+ // This method should only be called when replacing the fence with
+ // a signalTime. Since this is an indirect way of setting the signal time
+ // of a fence, the snapshot should come from a trusted source.
+ void applyTrustedSnapshot(const Snapshot& src);
+
+ bool isValid() const;
+
+ // Attempts to get the timestamp from the Fence if the timestamp isn't
+ // already cached. Otherwise, it returns the cached value.
+ nsecs_t getSignalTime();
+
+ // Gets the cached timestamp without attempting to query the Fence.
+ nsecs_t getCachedSignalTime() const;
+
+ // 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};
+
+ // mMutex guards mFence and mSignalTime.
+ // mSignalTime is also atomic since it is sometimes read outside the lock
+ // for quick checks.
+ mutable std::mutex mMutex;
+ sp<Fence> mFence{Fence::NO_FENCE};
+ std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
+};
+
+// A queue of FenceTimes that are expected to signal in FIFO order.
+// Only maintains a queue of weak pointers so it doesn't keep references
+// to Fences on its own.
+//
+// Can be used to get the signal time of a fence and close its file descriptor
+// without making a syscall for every fence later in the timeline.
+// Additionally, since the FenceTime caches the timestamp internally,
+// other timelines that reference the same FenceTime can avoid the syscall.
+//
+// FenceTimeline only keeps track of a limited number of entries to avoid
+// growing unbounded. Users of FenceTime must make sure they can work even
+// if FenceTimeline did nothing. i.e. they should eventually call
+// Fence::getSignalTime(), not only Fence::getCachedSignalTime().
+//
+// push() and updateSignalTimes() are safe to call simultaneously from
+// different threads.
+class FenceTimeline {
+public:
+ static constexpr size_t MAX_ENTRIES = 64;
+
+ void push(const std::shared_ptr<FenceTime>& fence);
+ void updateSignalTimes();
+
+private:
+ mutable std::mutex mMutex;
+ 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/FloatRect.h b/include/ui/FloatRect.h
new file mode 100644
index 0000000..270675c
--- /dev/null
+++ b/include/ui/FloatRect.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+namespace android {
+
+class FloatRect {
+public:
+ FloatRect() = default;
+ constexpr FloatRect(float _left, float _top, float _right, float _bottom)
+ : left(_left), top(_top), right(_right), bottom(_bottom) {}
+
+ float getWidth() const { return right - left; }
+ float getHeight() const { return bottom - top; }
+
+ float left = 0.0f;
+ float top = 0.0f;
+ float right = 0.0f;
+ float bottom = 0.0f;
+};
+
+inline bool operator==(const FloatRect& a, const FloatRect& b) {
+ return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom;
+}
+
+} // namespace android
diff --git a/include/ui/FrameStats.h b/include/ui/FrameStats.h
index 6bfe635..bc9d3ec 100644
--- a/include/ui/FrameStats.h
+++ b/include/ui/FrameStats.h
@@ -25,7 +25,7 @@
class FrameStats : public LightFlattenable<FrameStats> {
public:
- FrameStats() : refreshPeriodNano(0) {};
+ FrameStats() : refreshPeriodNano(0) {}
/*
* Approximate refresh time, in nanoseconds.
diff --git a/include/ui/Gralloc1.h b/include/ui/Gralloc1.h
deleted file mode 100644
index cf8c173..0000000
--- a/include/ui/Gralloc1.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_UI_GRALLOC1_H
-#define ANDROID_UI_GRALLOC1_H
-
-#define GRALLOC1_LOG_TAG "Gralloc1"
-
-#include <ui/Gralloc1On0Adapter.h>
-
-#include <unordered_set>
-
-namespace std {
- template <>
- struct hash<gralloc1_capability_t> {
- size_t operator()(gralloc1_capability_t capability) const {
- return std::hash<int32_t>()(static_cast<int32_t>(capability));
- }
- };
-}
-
-namespace android {
-
-class Fence;
-class GraphicBuffer;
-
-namespace Gralloc1 {
-
-class Device;
-
-class Descriptor {
-public:
- Descriptor(Device& device, gralloc1_buffer_descriptor_t deviceId)
- : mShimDevice(device),
- mDeviceId(deviceId),
- mWidth(0),
- mHeight(0),
- mFormat(static_cast<android_pixel_format_t>(0)),
- mProducerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
- mConsumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
-
- ~Descriptor();
-
- gralloc1_buffer_descriptor_t getDeviceId() const { return mDeviceId; }
-
- gralloc1_error_t setDimensions(uint32_t width, uint32_t height);
- gralloc1_error_t setFormat(android_pixel_format_t format);
- gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage);
- gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage);
-
-private:
- Device& mShimDevice;
- const gralloc1_buffer_descriptor_t mDeviceId;
-
- uint32_t mWidth;
- uint32_t mHeight;
- android_pixel_format_t mFormat;
- gralloc1_producer_usage_t mProducerUsage;
- gralloc1_consumer_usage_t mConsumerUsage;
-
-}; // Descriptor
-
-class Device {
- friend class Gralloc1::Descriptor;
-
-public:
- Device(gralloc1_device_t* device);
-
- bool hasCapability(gralloc1_capability_t capability) const;
-
- std::string dump();
-
- std::shared_ptr<Descriptor> createDescriptor();
-
- gralloc1_error_t getStride(buffer_handle_t buffer, uint32_t* outStride);
-
- gralloc1_error_t allocate(
- const std::vector<std::shared_ptr<const Descriptor>>& descriptors,
- std::vector<buffer_handle_t>* outBuffers);
- gralloc1_error_t allocate(
- const std::shared_ptr<const Descriptor>& descriptor,
- gralloc1_backing_store_t id, buffer_handle_t* outBuffer);
-
- gralloc1_error_t retain(buffer_handle_t buffer);
- gralloc1_error_t retain(const GraphicBuffer* buffer);
-
- gralloc1_error_t release(buffer_handle_t buffer);
-
- gralloc1_error_t getNumFlexPlanes(buffer_handle_t buffer,
- uint32_t* outNumPlanes);
-
- gralloc1_error_t lock(buffer_handle_t buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t* accessRegion, void** outData,
- const sp<Fence>& acquireFence);
- gralloc1_error_t lockFlex(buffer_handle_t buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t* accessRegion,
- struct android_flex_layout* outData, const sp<Fence>& acquireFence);
- gralloc1_error_t lockYCbCr(buffer_handle_t buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t* accessRegion, struct android_ycbcr* outData,
- const sp<Fence>& acquireFence);
-
- gralloc1_error_t unlock(buffer_handle_t buffer, sp<Fence>* outFence);
-
-private:
- std::unordered_set<gralloc1_capability_t> loadCapabilities();
-
- bool loadFunctions();
-
- template <typename LockType, typename OutType>
- gralloc1_error_t lockHelper(LockType pfn, buffer_handle_t buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t* accessRegion, OutType* outData,
- const sp<Fence>& acquireFence) {
- int32_t intError = pfn(mDevice, buffer,
- static_cast<uint64_t>(producerUsage),
- static_cast<uint64_t>(consumerUsage), accessRegion, outData,
- acquireFence->dup());
- return static_cast<gralloc1_error_t>(intError);
- }
-
- gralloc1_device_t* const mDevice;
-
- const std::unordered_set<gralloc1_capability_t> mCapabilities;
-
- template <typename PFN, gralloc1_function_descriptor_t descriptor>
- struct FunctionLoader {
- FunctionLoader() : pfn(nullptr) {}
-
- bool load(gralloc1_device_t* device, bool errorIfNull) {
- gralloc1_function_pointer_t rawPointer =
- device->getFunction(device, descriptor);
- pfn = reinterpret_cast<PFN>(rawPointer);
- if (errorIfNull && !rawPointer) {
- ALOG(LOG_ERROR, GRALLOC1_LOG_TAG,
- "Failed to load function pointer %d", descriptor);
- }
- return rawPointer != nullptr;
- }
-
- template <typename ...Args>
- typename std::result_of<PFN(Args...)>::type operator()(Args... args) {
- return pfn(args...);
- }
-
- PFN pfn;
- };
-
- // Function pointers
- struct Functions {
- FunctionLoader<GRALLOC1_PFN_DUMP, GRALLOC1_FUNCTION_DUMP> dump;
- FunctionLoader<GRALLOC1_PFN_CREATE_DESCRIPTOR,
- GRALLOC1_FUNCTION_CREATE_DESCRIPTOR> createDescriptor;
- FunctionLoader<GRALLOC1_PFN_DESTROY_DESCRIPTOR,
- GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR> destroyDescriptor;
- FunctionLoader<GRALLOC1_PFN_SET_CONSUMER_USAGE,
- GRALLOC1_FUNCTION_SET_CONSUMER_USAGE> setConsumerUsage;
- FunctionLoader<GRALLOC1_PFN_SET_DIMENSIONS,
- GRALLOC1_FUNCTION_SET_DIMENSIONS> setDimensions;
- FunctionLoader<GRALLOC1_PFN_SET_FORMAT,
- GRALLOC1_FUNCTION_SET_FORMAT> setFormat;
- FunctionLoader<GRALLOC1_PFN_SET_PRODUCER_USAGE,
- GRALLOC1_FUNCTION_SET_PRODUCER_USAGE> setProducerUsage;
- FunctionLoader<GRALLOC1_PFN_GET_BACKING_STORE,
- GRALLOC1_FUNCTION_GET_BACKING_STORE> getBackingStore;
- FunctionLoader<GRALLOC1_PFN_GET_CONSUMER_USAGE,
- GRALLOC1_FUNCTION_GET_CONSUMER_USAGE> getConsumerUsage;
- FunctionLoader<GRALLOC1_PFN_GET_DIMENSIONS,
- GRALLOC1_FUNCTION_GET_DIMENSIONS> getDimensions;
- FunctionLoader<GRALLOC1_PFN_GET_FORMAT,
- GRALLOC1_FUNCTION_GET_FORMAT> getFormat;
- FunctionLoader<GRALLOC1_PFN_GET_PRODUCER_USAGE,
- GRALLOC1_FUNCTION_GET_PRODUCER_USAGE> getProducerUsage;
- FunctionLoader<GRALLOC1_PFN_GET_STRIDE,
- GRALLOC1_FUNCTION_GET_STRIDE> getStride;
- FunctionLoader<GRALLOC1_PFN_ALLOCATE,
- GRALLOC1_FUNCTION_ALLOCATE> allocate;
- FunctionLoader<GRALLOC1_PFN_RETAIN,
- GRALLOC1_FUNCTION_RETAIN> retain;
- FunctionLoader<GRALLOC1_PFN_RELEASE,
- GRALLOC1_FUNCTION_RELEASE> release;
- FunctionLoader<GRALLOC1_PFN_GET_NUM_FLEX_PLANES,
- GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES> getNumFlexPlanes;
- FunctionLoader<GRALLOC1_PFN_LOCK,
- GRALLOC1_FUNCTION_LOCK> lock;
- FunctionLoader<GRALLOC1_PFN_LOCK_FLEX,
- GRALLOC1_FUNCTION_LOCK_FLEX> lockFlex;
- FunctionLoader<GRALLOC1_PFN_LOCK_YCBCR,
- GRALLOC1_FUNCTION_LOCK_YCBCR> lockYCbCr;
- FunctionLoader<GRALLOC1_PFN_UNLOCK,
- GRALLOC1_FUNCTION_UNLOCK> unlock;
-
- // Adapter-only functions
- FunctionLoader<GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER,
- GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER> retainGraphicBuffer;
- FunctionLoader<GRALLOC1_PFN_ALLOCATE_WITH_ID,
- GRALLOC1_FUNCTION_ALLOCATE_WITH_ID> allocateWithId;
- } mFunctions;
-
-}; // class android::Gralloc1::Device
-
-class Loader
-{
-public:
- Loader();
- ~Loader();
-
- std::unique_ptr<Device> getDevice();
-
-private:
- static std::unique_ptr<Gralloc1On0Adapter> mAdapter;
- std::unique_ptr<Device> mDevice;
-};
-
-} // namespace android::Gralloc1
-
-} // namespace android
-
-#endif
diff --git a/include/ui/Gralloc1On0Adapter.h b/include/ui/Gralloc1On0Adapter.h
deleted file mode 100644
index 97c9a89..0000000
--- a/include/ui/Gralloc1On0Adapter.h
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_UI_GRALLOC_1_ON_0_ADAPTER_H
-#define ANDROID_UI_GRALLOC_1_ON_0_ADAPTER_H
-
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-
-#include <hardware/gralloc1.h>
-
-#include <mutex>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-struct gralloc_module_t;
-
-// This is not an "official" capability (i.e., it is not found in gralloc1.h),
-// but we will use it to detect that we are running through the adapter, which
-// is capable of collaborating with GraphicBuffer such that queries on a
-// buffer_handle_t succeed
-static const auto GRALLOC1_CAPABILITY_ON_ADAPTER =
- static_cast<gralloc1_capability_t>(GRALLOC1_LAST_CAPABILITY + 1);
-
-static const auto GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER =
- static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 1);
-static const auto GRALLOC1_FUNCTION_ALLOCATE_WITH_ID =
- static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 2);
-static const auto GRALLOC1_FUNCTION_LOCK_YCBCR =
- static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 3);
-static const auto GRALLOC1_LAST_ADAPTER_FUNCTION = GRALLOC1_FUNCTION_LOCK_YCBCR;
-
-typedef gralloc1_error_t (*GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER)(
- gralloc1_device_t* device, const android::GraphicBuffer* buffer);
-typedef gralloc1_error_t (*GRALLOC1_PFN_ALLOCATE_WITH_ID)(
- gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptor,
- gralloc1_backing_store_t id, buffer_handle_t* outBuffer);
-typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_LOCK_YCBCR)(
- gralloc1_device_t* device, buffer_handle_t buffer,
- uint64_t /*gralloc1_producer_usage_t*/ producerUsage,
- uint64_t /*gralloc1_consumer_usage_t*/ consumerUsage,
- const gralloc1_rect_t* accessRegion, struct android_ycbcr* outYCbCr,
- int32_t acquireFence);
-
-namespace android {
-
-class Gralloc1On0Adapter : public gralloc1_device_t
-{
-public:
- Gralloc1On0Adapter(const hw_module_t* module);
- ~Gralloc1On0Adapter();
-
- gralloc1_device_t* getDevice() {
- return static_cast<gralloc1_device_t*>(this);
- }
-
-private:
- static inline Gralloc1On0Adapter* getAdapter(gralloc1_device_t* device) {
- return static_cast<Gralloc1On0Adapter*>(device);
- }
-
- // getCapabilities
-
- void doGetCapabilities(uint32_t* outCount,
- int32_t* /*gralloc1_capability_t*/ outCapabilities);
- static void getCapabilitiesHook(gralloc1_device_t* device,
- uint32_t* outCount,
- int32_t* /*gralloc1_capability_t*/ outCapabilities) {
- getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
- };
-
- // getFunction
-
- gralloc1_function_pointer_t doGetFunction(
- int32_t /*gralloc1_function_descriptor_t*/ descriptor);
- static gralloc1_function_pointer_t getFunctionHook(
- gralloc1_device_t* device,
- int32_t /*gralloc1_function_descriptor_t*/ descriptor) {
- return getAdapter(device)->doGetFunction(descriptor);
- }
-
- // dump
-
- void dump(uint32_t* outSize, char* outBuffer);
- static void dumpHook(gralloc1_device_t* device, uint32_t* outSize,
- char* outBuffer) {
- return getAdapter(device)->dump(outSize, outBuffer);
- }
- std::string mCachedDump;
-
- // Buffer descriptor lifecycle functions
-
- class Descriptor;
-
- gralloc1_error_t createDescriptor(
- gralloc1_buffer_descriptor_t* outDescriptor);
- static int32_t createDescriptorHook(gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t* outDescriptor) {
- auto error = getAdapter(device)->createDescriptor(outDescriptor);
- return static_cast<int32_t>(error);
- }
-
- gralloc1_error_t destroyDescriptor(gralloc1_buffer_descriptor_t descriptor);
- static int32_t destroyDescriptorHook(gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t descriptor) {
- auto error = getAdapter(device)->destroyDescriptor(descriptor);
- return static_cast<int32_t>(error);
- }
-
- // Buffer descriptor modification functions
-
- struct Descriptor : public std::enable_shared_from_this<Descriptor> {
- Descriptor(Gralloc1On0Adapter* adapter,
- gralloc1_buffer_descriptor_t id)
- : adapter(adapter),
- id(id),
- width(0),
- height(0),
- format(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
- producerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
- consumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
-
- gralloc1_error_t setDimensions(uint32_t w, uint32_t h) {
- width = w;
- height = h;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t setFormat(int32_t f) {
- format = f;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage) {
- producerUsage = usage;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage) {
- consumerUsage = usage;
- return GRALLOC1_ERROR_NONE;
- }
-
- Gralloc1On0Adapter* const adapter;
- const gralloc1_buffer_descriptor_t id;
-
- uint32_t width;
- uint32_t height;
- int32_t format;
- gralloc1_producer_usage_t producerUsage;
- gralloc1_consumer_usage_t consumerUsage;
- };
-
- template <typename ...Args>
- static int32_t callDescriptorFunction(gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t descriptorId,
- gralloc1_error_t (Descriptor::*member)(Args...), Args... args) {
- auto descriptor = getAdapter(device)->getDescriptor(descriptorId);
- if (!descriptor) {
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_DESCRIPTOR);
- }
- auto error = ((*descriptor).*member)(std::forward<Args>(args)...);
- return static_cast<int32_t>(error);
- }
-
- static int32_t setConsumerUsageHook(gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) {
- auto usage = static_cast<gralloc1_consumer_usage_t>(intUsage);
- return callDescriptorFunction(device, descriptorId,
- &Descriptor::setConsumerUsage, usage);
- }
-
- static int32_t setDimensionsHook(gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t descriptorId, uint32_t width,
- uint32_t height) {
- return callDescriptorFunction(device, descriptorId,
- &Descriptor::setDimensions, width, height);
- }
-
- static int32_t setFormatHook(gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t descriptorId, int32_t format) {
- return callDescriptorFunction(device, descriptorId,
- &Descriptor::setFormat, format);
- }
-
- static int32_t setProducerUsageHook(gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) {
- auto usage = static_cast<gralloc1_producer_usage_t>(intUsage);
- return callDescriptorFunction(device, descriptorId,
- &Descriptor::setProducerUsage, usage);
- }
-
- // Buffer handle query functions
-
- class Buffer {
- public:
- Buffer(buffer_handle_t handle, gralloc1_backing_store_t store,
- const Descriptor& descriptor, uint32_t stride,
- bool wasAllocated);
-
- buffer_handle_t getHandle() const { return mHandle; }
-
- void retain() { ++mReferenceCount; }
-
- // Returns true if the reference count has dropped to 0, indicating that
- // the buffer needs to be released
- bool release() { return --mReferenceCount == 0; }
-
- bool wasAllocated() const { return mWasAllocated; }
-
- gralloc1_error_t getBackingStore(
- gralloc1_backing_store_t* outStore) const {
- *outStore = mStore;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t getConsumerUsage(
- gralloc1_consumer_usage_t* outUsage) const {
- *outUsage = mDescriptor.consumerUsage;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t getDimensions(uint32_t* outWidth,
- uint32_t* outHeight) const {
- *outWidth = mDescriptor.width;
- *outHeight = mDescriptor.height;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t getFormat(int32_t* outFormat) const {
- *outFormat = mDescriptor.format;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t getNumFlexPlanes(uint32_t* outNumPlanes) const {
- // TODO: This is conservative, and we could do better by examining
- // the format, but it won't hurt anything for now
- *outNumPlanes = 4;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t getProducerUsage(
- gralloc1_producer_usage_t* outUsage) const {
- *outUsage = mDescriptor.producerUsage;
- return GRALLOC1_ERROR_NONE;
- }
-
- gralloc1_error_t getStride(uint32_t* outStride) const {
- *outStride = mStride;
- return GRALLOC1_ERROR_NONE;
- }
-
- private:
-
- const buffer_handle_t mHandle;
- size_t mReferenceCount;
-
- // Since we're adapting to gralloc0, there will always be a 1:1
- // correspondence between buffer handles and backing stores, and the
- // backing store ID will be the same as the GraphicBuffer unique ID
- const gralloc1_backing_store_t mStore;
-
- const Descriptor mDescriptor;
- const uint32_t mStride;
-
- // Whether this buffer allocated in this process (as opposed to just
- // being retained here), which determines whether to free or unregister
- // the buffer when this Buffer is released
- const bool mWasAllocated;
- };
-
- template <typename ...Args>
- static int32_t callBufferFunction(gralloc1_device_t* device,
- buffer_handle_t bufferHandle,
- gralloc1_error_t (Buffer::*member)(Args...) const, Args... args) {
- auto buffer = getAdapter(device)->getBuffer(bufferHandle);
- if (!buffer) {
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
- }
- auto error = ((*buffer).*member)(std::forward<Args>(args)...);
- return static_cast<int32_t>(error);
- }
-
- template <typename MF, MF memFunc, typename ...Args>
- static int32_t bufferHook(gralloc1_device_t* device,
- buffer_handle_t bufferHandle, Args... args) {
- return Gralloc1On0Adapter::callBufferFunction(device, bufferHandle,
- memFunc, std::forward<Args>(args)...);
- }
-
- static int32_t getConsumerUsageHook(gralloc1_device_t* device,
- buffer_handle_t bufferHandle, uint64_t* outUsage) {
- auto usage = GRALLOC1_CONSUMER_USAGE_NONE;
- auto error = callBufferFunction(device, bufferHandle,
- &Buffer::getConsumerUsage, &usage);
- if (error != GRALLOC1_ERROR_NONE) {
- *outUsage = static_cast<uint64_t>(usage);
- }
- return error;
- }
-
- static int32_t getProducerUsageHook(gralloc1_device_t* device,
- buffer_handle_t bufferHandle, uint64_t* outUsage) {
- auto usage = GRALLOC1_PRODUCER_USAGE_NONE;
- auto error = callBufferFunction(device, bufferHandle,
- &Buffer::getProducerUsage, &usage);
- if (error != GRALLOC1_ERROR_NONE) {
- *outUsage = static_cast<uint64_t>(usage);
- }
- return error;
- }
-
- // Buffer management functions
-
- // We don't provide GRALLOC1_FUNCTION_ALLOCATE, since this should always be
- // called through GRALLOC1_FUNCTION_ALLOCATE_WITH_ID
- gralloc1_error_t allocate(
- const std::shared_ptr<Descriptor>& descriptor,
- gralloc1_backing_store_t id,
- buffer_handle_t* outBufferHandle);
- static gralloc1_error_t allocateWithIdHook(gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t descriptors,
- gralloc1_backing_store_t id, buffer_handle_t* outBuffer);
-
- gralloc1_error_t retain(const std::shared_ptr<Buffer>& buffer);
- gralloc1_error_t release(const std::shared_ptr<Buffer>& buffer);
-
- // Member function pointer 'member' will either be retain or release
- template <gralloc1_error_t (Gralloc1On0Adapter::*member)(
- const std::shared_ptr<Buffer>& buffer)>
- static int32_t managementHook(gralloc1_device_t* device,
- buffer_handle_t bufferHandle) {
- auto adapter = getAdapter(device);
-
- auto buffer = adapter->getBuffer(bufferHandle);
- if (!buffer) {
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
- }
-
- auto error = ((*adapter).*member)(buffer);
- return static_cast<int32_t>(error);
- }
-
- gralloc1_error_t retain(const GraphicBuffer* buffer);
- static gralloc1_error_t retainGraphicBufferHook(gralloc1_device_t* device,
- const GraphicBuffer* buffer) {
- auto adapter = getAdapter(device);
- return adapter->retain(buffer);
- }
-
- // Buffer access functions
-
- gralloc1_error_t lock(const std::shared_ptr<Buffer>& buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t& accessRegion, void** outData,
- const sp<Fence>& acquireFence);
- gralloc1_error_t lockFlex(const std::shared_ptr<Buffer>& buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t& accessRegion,
- struct android_flex_layout* outFlex,
- const sp<Fence>& acquireFence);
- gralloc1_error_t lockYCbCr(const std::shared_ptr<Buffer>& buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t& accessRegion,
- struct android_ycbcr* outFlex,
- const sp<Fence>& acquireFence);
-
- template <typename OUT, gralloc1_error_t (Gralloc1On0Adapter::*member)(
- const std::shared_ptr<Buffer>&, gralloc1_producer_usage_t,
- gralloc1_consumer_usage_t, const gralloc1_rect_t&, OUT*,
- const sp<Fence>&)>
- static int32_t lockHook(gralloc1_device_t* device,
- buffer_handle_t bufferHandle,
- uint64_t /*gralloc1_producer_usage_t*/ uintProducerUsage,
- uint64_t /*gralloc1_consumer_usage_t*/ uintConsumerUsage,
- const gralloc1_rect_t* accessRegion, OUT* outData,
- int32_t acquireFenceFd) {
- auto adapter = getAdapter(device);
-
- // Exactly one of producer and consumer usage must be *_USAGE_NONE,
- // but we can't check this until the upper levels of the framework
- // correctly distinguish between producer and consumer usage
- /*
- bool hasProducerUsage =
- uintProducerUsage != GRALLOC1_PRODUCER_USAGE_NONE;
- bool hasConsumerUsage =
- uintConsumerUsage != GRALLOC1_CONSUMER_USAGE_NONE;
- if (hasProducerUsage && hasConsumerUsage ||
- !hasProducerUsage && !hasConsumerUsage) {
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
- }
- */
-
- auto producerUsage =
- static_cast<gralloc1_producer_usage_t>(uintProducerUsage);
- auto consumerUsage =
- static_cast<gralloc1_consumer_usage_t>(uintConsumerUsage);
-
- if (!outData) {
- const auto producerCpuUsage = GRALLOC1_PRODUCER_USAGE_CPU_READ |
- GRALLOC1_PRODUCER_USAGE_CPU_WRITE;
- if (producerUsage & producerCpuUsage != 0) {
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
- }
- if (consumerUsage & GRALLOC1_CONSUMER_USAGE_CPU_READ != 0) {
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
- }
- }
-
- auto buffer = adapter->getBuffer(bufferHandle);
- if (!buffer) {
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
- }
-
- if (!accessRegion) {
- ALOGE("accessRegion is null");
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
- }
-
- sp<Fence> acquireFence{new Fence(acquireFenceFd)};
- auto error = ((*adapter).*member)(buffer, producerUsage, consumerUsage,
- *accessRegion, outData, acquireFence);
- return static_cast<int32_t>(error);
- }
-
- gralloc1_error_t unlock(const std::shared_ptr<Buffer>& buffer,
- sp<Fence>* outReleaseFence);
- static int32_t unlockHook(gralloc1_device_t* device,
- buffer_handle_t bufferHandle, int32_t* outReleaseFenceFd) {
- auto adapter = getAdapter(device);
-
- auto buffer = adapter->getBuffer(bufferHandle);
- if (!buffer) {
- return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
- }
-
- sp<Fence> releaseFence = Fence::NO_FENCE;
- auto error = adapter->unlock(buffer, &releaseFence);
- if (error == GRALLOC1_ERROR_NONE) {
- *outReleaseFenceFd = releaseFence->dup();
- }
- return static_cast<int32_t>(error);
- }
-
- // Adapter internals
- const gralloc_module_t* mModule;
- uint8_t mMinorVersion;
- alloc_device_t* mDevice;
-
- std::shared_ptr<Descriptor> getDescriptor(
- gralloc1_buffer_descriptor_t descriptorId);
- std::shared_ptr<Buffer> getBuffer(buffer_handle_t bufferHandle);
-
- static std::atomic<gralloc1_buffer_descriptor_t> sNextBufferDescriptorId;
- std::mutex mDescriptorMutex;
- std::unordered_map<gralloc1_buffer_descriptor_t,
- std::shared_ptr<Descriptor>> mDescriptors;
- std::mutex mBufferMutex;
- std::unordered_map<buffer_handle_t, std::shared_ptr<Buffer>> mBuffers;
-};
-
-} // namespace android
-
-#endif
diff --git a/include/ui/Gralloc2.h b/include/ui/Gralloc2.h
new file mode 100644
index 0000000..f826b92
--- /dev/null
+++ b/include/ui/Gralloc2.h
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_GRALLOC2_H
+#define ANDROID_UI_GRALLOC2_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <system/window.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+using hardware::graphics::allocator::V2_0::IAllocator;
+using hardware::graphics::common::V1_0::BufferUsage;
+using hardware::graphics::common::V1_0::PixelFormat;
+using hardware::graphics::mapper::V2_0::BufferDescriptor;
+using hardware::graphics::mapper::V2_0::Error;
+using hardware::graphics::mapper::V2_0::IMapper;
+using hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+// A wrapper to IMapper
+class Mapper {
+public:
+ Mapper();
+
+ Error createDescriptor(
+ const IMapper::BufferDescriptorInfo& descriptorInfo,
+ BufferDescriptor* outDescriptor) const;
+
+ // Import a buffer that is from another HAL, another process, or is
+ // cloned.
+ //
+ // The returned handle must be freed with freeBuffer.
+ Error importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const;
+
+ void freeBuffer(buffer_handle_t bufferHandle) const;
+
+ // The ownership of acquireFence is always transferred to the callee, even
+ // on errors.
+ Error lock(buffer_handle_t bufferHandle, uint64_t usage,
+ const IMapper::Rect& accessRegion,
+ int acquireFence, void** outData) const;
+
+ // The ownership of acquireFence is always transferred to the callee, even
+ // on errors.
+ Error lock(buffer_handle_t bufferHandle, uint64_t usage,
+ const IMapper::Rect& accessRegion,
+ int acquireFence, YCbCrLayout* outLayout) const;
+
+ // unlock returns a fence sync object (or -1) and the fence sync object is
+ // owned by the caller
+ int unlock(buffer_handle_t bufferHandle) const;
+
+private:
+ sp<IMapper> mMapper;
+};
+
+// A wrapper to IAllocator
+class Allocator {
+public:
+ // An allocator relies on a mapper, and that mapper must be alive at all
+ // time.
+ Allocator(const Mapper& mapper);
+
+ std::string dumpDebugInfo() const;
+
+ /*
+ * The returned buffers are already imported and must not be imported
+ * again. outBufferHandles must point to a space that can contain at
+ * least "count" buffer_handle_t.
+ */
+ Error allocate(BufferDescriptor descriptor, uint32_t count,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles) const;
+
+ Error allocate(BufferDescriptor descriptor,
+ uint32_t* outStride, buffer_handle_t* outBufferHandle) const
+ {
+ return allocate(descriptor, 1, outStride, outBufferHandle);
+ }
+
+ Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles) const
+ {
+ BufferDescriptor descriptor;
+ Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
+ if (error == Error::NONE) {
+ error = allocate(descriptor, count, outStride, outBufferHandles);
+ }
+ return error;
+ }
+
+ Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ uint32_t* outStride, buffer_handle_t* outBufferHandle) const
+ {
+ return allocate(descriptorInfo, 1, outStride, outBufferHandle);
+ }
+
+private:
+ const Mapper& mMapper;
+ sp<IAllocator> mAllocator;
+};
+
+} // namespace Gralloc2
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC2_H
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 3e127a1..4b82cff 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -20,13 +20,15 @@
#include <stdint.h>
#include <sys/types.h>
+#include <string>
+
#include <ui/ANativeObjectBase.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/Flattenable.h>
#include <utils/RefBase.h>
-#include <string>
+#include <hardware/gralloc.h>
struct ANativeWindowBuffer;
@@ -70,20 +72,69 @@
USAGE_CURSOR = GRALLOC_USAGE_CURSOR,
};
+ static sp<GraphicBuffer> from(ANativeWindowBuffer *);
+
+
+ // Create a GraphicBuffer to be unflatten'ed into or be reallocated.
GraphicBuffer();
- // creates w * h buffer
+ // Create a GraphicBuffer by allocating and managing a buffer internally.
+ // This function is privileged. See reallocate for details.
+ GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+ uint32_t inLayerCount, uint64_t inUsage,
+ std::string requestorName = "<Unknown>");
+
+ // Create a GraphicBuffer from an existing handle.
+ enum HandleWrapMethod : uint8_t {
+ // Wrap and use the handle directly. It assumes the handle has been
+ // registered and never fails. The handle must have a longer lifetime
+ // than this wrapping GraphicBuffer.
+ //
+ // This can be used when, for example, you want to wrap a handle that
+ // is already managed by another GraphicBuffer.
+ WRAP_HANDLE,
+
+ // Take ownership of the handle and use it directly. It assumes the
+ // handle has been registered and never fails.
+ //
+ // This can be used to manage an already registered handle with
+ // GraphicBuffer.
+ TAKE_HANDLE,
+
+ // Take onwership of an unregistered handle and use it directly. It
+ // can fail when the buffer does not register. There is no ownership
+ // transfer on failures.
+ //
+ // This can be used to, for example, create a GraphicBuffer from a
+ // handle returned by Parcel::readNativeHandle.
+ TAKE_UNREGISTERED_HANDLE,
+
+ // Make a clone of the handle and use the cloned handle. It can fail
+ // when cloning fails or when the buffer does not register. There is
+ // never ownership transfer.
+ //
+ // This can be used to create a GraphicBuffer from a handle that
+ // cannot be used directly, such as one from hidl_handle.
+ CLONE_HANDLE,
+ };
+ GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method,
+ uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t stride);
+
+ // These functions are deprecated because they only take 32 bits of usage
+ GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method,
+ uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint32_t usage, uint32_t stride)
+ : GraphicBuffer(handle, method, width, height, format, layerCount,
+ static_cast<uint64_t>(usage), stride) {}
+ GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+ uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride,
+ native_handle_t* inHandle, bool keepOwnership);
GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inUsage, std::string requestorName = "<Unknown>");
- // create a buffer from an existing handle
- GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
- uint32_t inUsage, uint32_t inStride, native_handle_t* inHandle,
- bool keepOwnership);
-
- // create a buffer from an existing ANativeWindowBuffer
- GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership);
-
// return status
status_t initCheck() const;
@@ -92,6 +143,7 @@
uint32_t getStride() const { return static_cast<uint32_t>(stride); }
uint32_t getUsage() const { return static_cast<uint32_t>(usage); }
PixelFormat getPixelFormat() const { return format; }
+ uint32_t getLayerCount() const { return static_cast<uint32_t>(layerCount); }
Rect getBounds() const { return Rect(width, height); }
uint64_t getId() const { return mId; }
@@ -100,11 +152,14 @@
mGenerationNumber = generation;
}
+ // This function is privileged. It requires access to the allocator
+ // device or service, which usually involves adding suitable selinux
+ // rules.
status_t reallocate(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage);
+ PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage);
bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage);
+ PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage);
status_t lock(uint32_t inUsage, void** vaddr);
status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
@@ -116,6 +171,8 @@
status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd);
status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr,
int fenceFd);
+ status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage,
+ const Rect& rect, void** vaddr, int fenceFd);
status_t lockAsyncYCbCr(uint32_t inUsage, android_ycbcr *ycbcr,
int fenceFd);
status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect,
@@ -159,18 +216,20 @@
GraphicBuffer& operator = (const GraphicBuffer& rhs);
const GraphicBuffer& operator = (const GraphicBuffer& rhs) const;
- status_t initSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
- uint32_t inUsage, std::string requestorName);
+ status_t initWithSize(uint32_t inWidth, uint32_t inHeight,
+ PixelFormat inFormat, uint32_t inLayerCount,
+ uint64_t inUsage, std::string requestorName);
+
+ status_t initWithHandle(const native_handle_t* handle,
+ HandleWrapMethod method, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t stride);
void free_handle();
GraphicBufferMapper& mBufferMapper;
ssize_t mInitCheck;
- // If we're wrapping another buffer then this reference will make sure it
- // doesn't get freed.
- sp<ANativeWindowBuffer> mWrappedBuffer;
-
uint64_t mId;
// Stores the generation number of this buffer. If this number does not
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 28d0238..fe99de1 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -20,48 +20,38 @@
#include <stdint.h>
+#include <memory>
+#include <string>
+
#include <cutils/native_handle.h>
+#include <system/window.h>
+
+#include <ui/PixelFormat.h>
+
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
-#include <utils/threads.h>
+#include <utils/Mutex.h>
#include <utils/Singleton.h>
-#include <ui/Gralloc1.h>
-#include <ui/PixelFormat.h>
-
namespace android {
-class Gralloc1Loader;
+namespace Gralloc2 {
+class Allocator;
+}
+
+class GraphicBufferMapper;
class String8;
class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
{
public:
- enum {
- USAGE_SW_READ_NEVER = GRALLOC1_CONSUMER_USAGE_CPU_READ_NEVER,
- USAGE_SW_READ_RARELY = GRALLOC1_CONSUMER_USAGE_CPU_READ,
- USAGE_SW_READ_OFTEN = GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN,
- USAGE_SW_READ_MASK = GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN,
-
- USAGE_SW_WRITE_NEVER = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_NEVER,
- USAGE_SW_WRITE_RARELY = GRALLOC1_PRODUCER_USAGE_CPU_WRITE,
- USAGE_SW_WRITE_OFTEN = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN,
- USAGE_SW_WRITE_MASK = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN,
-
- USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK,
-
- USAGE_HW_TEXTURE = GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE,
- USAGE_HW_RENDER = GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET,
- USAGE_HW_2D = 0x00000400, // Deprecated
- USAGE_HW_MASK = 0x00071F00, // Deprecated
- };
-
static inline GraphicBufferAllocator& get() { return getInstance(); }
status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
- uint32_t usage, buffer_handle_t* handle, uint32_t* stride,
- uint64_t graphicBufferId, std::string requestorName);
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
+ std::string requestorName);
status_t free(buffer_handle_t handle);
@@ -74,7 +64,8 @@
uint32_t height;
uint32_t stride;
PixelFormat format;
- uint32_t usage;
+ uint32_t layerCount;
+ uint64_t usage;
size_t size;
std::string requestorName;
};
@@ -86,8 +77,8 @@
GraphicBufferAllocator();
~GraphicBufferAllocator();
- std::unique_ptr<Gralloc1::Loader> mLoader;
- std::unique_ptr<Gralloc1::Device> mDevice;
+ GraphicBufferMapper& mMapper;
+ const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
};
// ---------------------------------------------------------------------------
diff --git a/include/ui/GraphicBufferMapper.h b/include/ui/GraphicBufferMapper.h
index a25809c..e0702e9 100644
--- a/include/ui/GraphicBufferMapper.h
+++ b/include/ui/GraphicBufferMapper.h
@@ -20,14 +20,24 @@
#include <stdint.h>
#include <sys/types.h>
-#include <ui/Gralloc1.h>
+#include <memory>
#include <utils/Singleton.h>
+
+// Needed by code that still uses the GRALLOC_USAGE_* constants.
+// when/if we get rid of gralloc, we should provide aliases or fix call sites.
+#include <hardware/gralloc.h>
+
+
namespace android {
// ---------------------------------------------------------------------------
+namespace Gralloc2 {
+class Mapper;
+}
+
class Rect;
class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -35,10 +45,12 @@
public:
static inline GraphicBufferMapper& get() { return getInstance(); }
- status_t registerBuffer(buffer_handle_t handle);
- status_t registerBuffer(const GraphicBuffer* buffer);
+ // The imported outHandle must be freed with freeBuffer when no longer
+ // needed. rawHandle is owned by the caller.
+ status_t importBuffer(buffer_handle_t rawHandle,
+ buffer_handle_t* outHandle);
- status_t unregisterBuffer(buffer_handle_t handle);
+ status_t freeBuffer(buffer_handle_t handle);
status_t lock(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, void** vaddr);
@@ -51,19 +63,27 @@
status_t lockAsync(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd);
+ status_t lockAsync(buffer_handle_t handle,
+ uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
+ void** vaddr, int fenceFd);
+
status_t lockAsyncYCbCr(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr,
int fenceFd);
status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
+ const Gralloc2::Mapper& getGrallocMapper() const
+ {
+ return *mMapper;
+ }
+
private:
friend class Singleton<GraphicBufferMapper>;
GraphicBufferMapper();
- std::unique_ptr<Gralloc1::Loader> mLoader;
- std::unique_ptr<Gralloc1::Device> mDevice;
+ const std::unique_ptr<const Gralloc2::Mapper> mMapper;
};
// ---------------------------------------------------------------------------
diff --git a/include/ui/GraphicsEnv.h b/include/ui/GraphicsEnv.h
new file mode 100644
index 0000000..7817076
--- /dev/null
+++ b/include/ui/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_UI_GRAPHICS_ENV_H
+#define ANDROID_UI_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_UI_GRAPHICS_ENV_H
diff --git a/include/ui/HdrCapabilities.h b/include/ui/HdrCapabilities.h
index a7cd5fb..925aa1b 100644
--- a/include/ui/HdrCapabilities.h
+++ b/include/ui/HdrCapabilities.h
@@ -17,11 +17,15 @@
#ifndef ANDROID_UI_HDR_CAPABILTIES_H
#define ANDROID_UI_HDR_CAPABILTIES_H
-#include <binder/Parcelable.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include <utils/Flattenable.h>
namespace android {
-class HdrCapabilities : public Parcelable
+class HdrCapabilities : public LightFlattenable<HdrCapabilities>
{
public:
HdrCapabilities(const std::vector<int32_t /*android_hdr_t*/>& types,
@@ -32,8 +36,8 @@
mMinLuminance(minLuminance) {}
// Make this move-constructable and move-assignable
- HdrCapabilities(HdrCapabilities&& other) = default;
- HdrCapabilities& operator=(HdrCapabilities&& other) = default;
+ HdrCapabilities(HdrCapabilities&& other);
+ HdrCapabilities& operator=(HdrCapabilities&& other);
HdrCapabilities()
: mSupportedHdrTypes(),
@@ -41,7 +45,7 @@
mMaxAverageLuminance(-1.0f),
mMinLuminance(-1.0f) {}
- virtual ~HdrCapabilities() = default;
+ ~HdrCapabilities();
const std::vector<int32_t /*android_hdr_t*/>& getSupportedHdrTypes() const {
return mSupportedHdrTypes;
@@ -50,9 +54,11 @@
float getDesiredMaxAverageLuminance() const { return mMaxAverageLuminance; }
float getDesiredMinLuminance() const { return mMinLuminance; }
- // Parcelable interface
- virtual status_t writeToParcel(Parcel* parcel) const override;
- virtual status_t readFromParcel(const Parcel* parcel) override;
+ // Flattenable protocol
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
private:
std::vector<int32_t /*android_hdr_t*/> mSupportedHdrTypes;
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index f26fecb..02773d9 100644
--- a/include/ui/PixelFormat.h
+++ b/include/ui/PixelFormat.h
@@ -53,13 +53,15 @@
// real pixel formats supported for rendering -----------------------------
- PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA
- PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0
- PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB
- PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB
- PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA
- PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB
- PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB
+ PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA
+ PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0
+ PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB
+ PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB
+ PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA
+ PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB
+ PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB
+ PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA
+ PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
};
typedef int32_t PixelFormat;
diff --git a/include/ui/Point.h b/include/ui/Point.h
index 1d7f64d..d050ede 100644
--- a/include/ui/Point.h
+++ b/include/ui/Point.h
@@ -34,7 +34,7 @@
// Default constructor doesn't initialize the Point
inline Point() {
}
- inline Point(int x, int y) : x(x), y(y) {
+ inline Point(int _x, int _y) : x(_x), y(_y) {
}
inline bool operator == (const Point& rhs) const {
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index a8513a9..b50e4ec 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -20,6 +20,9 @@
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
+#include <log/log.h>
+
+#include <ui/FloatRect.h>
#include <ui/Point.h>
#include <android/rect.h>
@@ -42,13 +45,9 @@
template <typename T>
inline Rect(T w, T h) {
if (w > INT32_MAX) {
- ALOG(LOG_WARN, "Rect",
- "Width %u too large for Rect class, clamping", w);
w = INT32_MAX;
}
if (h > INT32_MAX) {
- ALOG(LOG_WARN, "Rect",
- "Height %u too large for Rect class, clamping", h);
h = INT32_MAX;
}
left = top = 0;
@@ -177,11 +176,15 @@
// this calculates (Region(*this) - exclude).bounds() efficiently
Rect reduce(const Rect& exclude) const;
-
// for backward compatibility
inline int32_t width() const { return getWidth(); }
inline int32_t height() const { return getHeight(); }
inline void set(const Rect& rhs) { operator = (rhs); }
+
+ FloatRect toFloatRect() const {
+ return {static_cast<float>(left), static_cast<float>(top),
+ static_cast<float>(right), static_cast<float>(bottom)};
+ }
};
ANDROID_BASIC_TYPES_TRAITS(Rect)
diff --git a/include/ui/Region.h b/include/ui/Region.h
index 810f098..7788452 100644
--- a/include/ui/Region.h
+++ b/include/ui/Region.h
@@ -147,21 +147,21 @@
class rasterizer;
friend class rasterizer;
- Region& operationSelf(const Rect& r, int op);
- Region& operationSelf(const Region& r, int op);
- Region& operationSelf(const Region& r, int dx, int dy, int op);
- const Region operation(const Rect& rhs, int op) const;
- const Region operation(const Region& rhs, int op) const;
- const Region operation(const Region& rhs, int dx, int dy, int op) const;
+ Region& operationSelf(const Rect& r, uint32_t op);
+ Region& operationSelf(const Region& r, uint32_t op);
+ Region& operationSelf(const Region& r, int dx, int dy, uint32_t op);
+ const Region operation(const Rect& rhs, uint32_t op) const;
+ const Region operation(const Region& rhs, uint32_t op) const;
+ const Region operation(const Region& rhs, int dx, int dy, uint32_t op) const;
- static void boolean_operation(int op, Region& dst,
+ static void boolean_operation(uint32_t op, Region& dst,
const Region& lhs, const Region& rhs, int dx, int dy);
- static void boolean_operation(int op, Region& dst,
+ static void boolean_operation(uint32_t op, Region& dst,
const Region& lhs, const Rect& rhs, int dx, int dy);
- static void boolean_operation(int op, Region& dst,
+ static void boolean_operation(uint32_t op, Region& dst,
const Region& lhs, const Region& rhs);
- static void boolean_operation(int op, Region& dst,
+ static void boolean_operation(uint32_t op, Region& dst,
const Region& lhs, const Rect& rhs);
static void translate(Region& reg, int dx, int dy);
diff --git a/include/ui/TMatHelpers.h b/include/ui/TMatHelpers.h
deleted file mode 100644
index a6aadca..0000000
--- a/include/ui/TMatHelpers.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#ifndef TMAT_IMPLEMENTATION
-#error "Don't include TMatHelpers.h directly. use ui/mat*.h instead"
-#else
-#undef TMAT_IMPLEMENTATION
-#endif
-
-
-#ifndef UI_TMAT_HELPERS_H
-#define UI_TMAT_HELPERS_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <math.h>
-#include <utils/Debug.h>
-#include <utils/String8.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-/*
- * No user serviceable parts here.
- *
- * Don't use this file directly, instead include ui/mat*.h
- */
-
-
-/*
- * Matrix utilities
- */
-
-namespace matrix {
-
-inline int PURE transpose(int v) { return v; }
-inline float PURE transpose(float v) { return v; }
-inline double PURE transpose(double v) { return v; }
-
-inline int PURE trace(int v) { return v; }
-inline float PURE trace(float v) { return v; }
-inline double PURE trace(double v) { return v; }
-
-template<typename MATRIX>
-MATRIX PURE inverse(const MATRIX& src) {
-
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::COL_SIZE == MATRIX::ROW_SIZE );
-
- typename MATRIX::value_type t;
- const size_t N = MATRIX::col_size();
- size_t swap;
- MATRIX tmp(src);
- MATRIX inverse(1);
-
- for (size_t i=0 ; i<N ; i++) {
- // look for largest element in column
- swap = i;
- for (size_t j=i+1 ; j<N ; j++) {
- if (fabs(tmp[j][i]) > fabs(tmp[i][i])) {
- swap = j;
- }
- }
-
- if (swap != i) {
- /* swap rows. */
- for (size_t k=0 ; k<N ; k++) {
- t = tmp[i][k];
- tmp[i][k] = tmp[swap][k];
- tmp[swap][k] = t;
-
- t = inverse[i][k];
- inverse[i][k] = inverse[swap][k];
- inverse[swap][k] = t;
- }
- }
-
- t = 1 / tmp[i][i];
- for (size_t k=0 ; k<N ; k++) {
- tmp[i][k] *= t;
- inverse[i][k] *= t;
- }
- for (size_t j=0 ; j<N ; j++) {
- if (j != i) {
- t = tmp[j][i];
- for (size_t k=0 ; k<N ; k++) {
- tmp[j][k] -= tmp[i][k] * t;
- inverse[j][k] -= inverse[i][k] * t;
- }
- }
- }
- }
- return inverse;
-}
-
-template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
-MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
- // pre-requisite:
- // lhs : D columns, R rows
- // rhs : C columns, D rows
- // res : C columns, R rows
-
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_A::ROW_SIZE == MATRIX_B::COL_SIZE );
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::ROW_SIZE == MATRIX_B::ROW_SIZE );
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::COL_SIZE == MATRIX_A::COL_SIZE );
-
- MATRIX_R res(MATRIX_R::NO_INIT);
- for (size_t r=0 ; r<MATRIX_R::row_size() ; r++) {
- res[r] = lhs * rhs[r];
- }
- return res;
-}
-
-// transpose. this handles matrices of matrices
-template <typename MATRIX>
-MATRIX PURE transpose(const MATRIX& m) {
- // for now we only handle square matrix transpose
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
- MATRIX result(MATRIX::NO_INIT);
- for (size_t r=0 ; r<MATRIX::row_size() ; r++)
- for (size_t c=0 ; c<MATRIX::col_size() ; c++)
- result[c][r] = transpose(m[r][c]);
- return result;
-}
-
-// trace. this handles matrices of matrices
-template <typename MATRIX>
-typename MATRIX::value_type PURE trace(const MATRIX& m) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
- typename MATRIX::value_type result(0);
- for (size_t r=0 ; r<MATRIX::row_size() ; r++)
- result += trace(m[r][r]);
- return result;
-}
-
-// trace. this handles matrices of matrices
-template <typename MATRIX>
-typename MATRIX::col_type PURE diag(const MATRIX& m) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
- typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
- for (size_t r=0 ; r<MATRIX::row_size() ; r++)
- result[r] = m[r][r];
- return result;
-}
-
-template <typename MATRIX>
-String8 asString(const MATRIX& m) {
- String8 s;
- for (size_t c=0 ; c<MATRIX::col_size() ; c++) {
- s.append("| ");
- for (size_t r=0 ; r<MATRIX::row_size() ; r++) {
- s.appendFormat("%7.2f ", m[r][c]);
- }
- s.append("|\n");
- }
- return s;
-}
-
-}; // namespace matrix
-
-// -------------------------------------------------------------------------------------
-
-/*
- * TMatProductOperators implements basic arithmetic and basic compound assignments
- * operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template <template<typename T> class BASE, typename T>
-class TMatProductOperators {
-public:
- // multiply by a scalar
- BASE<T>& operator *= (T v) {
- BASE<T>& lhs(static_cast< BASE<T>& >(*this));
- for (size_t r=0 ; r<lhs.row_size() ; r++) {
- lhs[r] *= v;
- }
- return lhs;
- }
-
- // divide by a scalar
- BASE<T>& operator /= (T v) {
- BASE<T>& lhs(static_cast< BASE<T>& >(*this));
- for (size_t r=0 ; r<lhs.row_size() ; r++) {
- lhs[r] /= v;
- }
- return lhs;
- }
-
- // matrix * matrix, result is a matrix of the same type than the lhs matrix
- template<typename U>
- friend BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
- return matrix::multiply<BASE<T> >(lhs, rhs);
- }
-};
-
-
-/*
- * TMatSquareFunctions implements functions on a matrix of type BASE<T>.
- *
- * BASE only needs to implement:
- * - operator[]
- * - col_type
- * - row_type
- * - COL_SIZE
- * - ROW_SIZE
- *
- * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template<template<typename U> class BASE, typename T>
-class TMatSquareFunctions {
-public:
- /*
- * NOTE: the functions below ARE NOT member methods. They are friend functions
- * with they definition inlined with their declaration. This makes these
- * template functions available to the compiler when (and only when) this class
- * is instantiated, at which point they're only templated on the 2nd parameter
- * (the first one, BASE<T> being known).
- */
- friend BASE<T> PURE inverse(const BASE<T>& m) { return matrix::inverse(m); }
- friend BASE<T> PURE transpose(const BASE<T>& m) { return matrix::transpose(m); }
- friend T PURE trace(const BASE<T>& m) { return matrix::trace(m); }
-};
-
-template <template<typename T> class BASE, typename T>
-class TMatDebug {
-public:
- String8 asString() const {
- return matrix::asString( static_cast< const BASE<T>& >(*this) );
- }
-};
-
-// -------------------------------------------------------------------------------------
-}; // namespace android
-
-#undef PURE
-
-#endif /* UI_TMAT_HELPERS_H */
diff --git a/include/ui/TVecHelpers.h b/include/ui/TVecHelpers.h
deleted file mode 100644
index bb7dbfc..0000000
--- a/include/ui/TVecHelpers.h
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#ifndef TVEC_IMPLEMENTATION
-#error "Don't include TVecHelpers.h directly. use ui/vec*.h instead"
-#else
-#undef TVEC_IMPLEMENTATION
-#endif
-
-
-#ifndef UI_TVEC_HELPERS_H
-#define UI_TVEC_HELPERS_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-/*
- * No user serviceable parts here.
- *
- * Don't use this file directly, instead include ui/vec{2|3|4}.h
- */
-
-/*
- * This class casts itself into anything and assign itself from anything!
- * Use with caution!
- */
-template <typename TYPE>
-struct Impersonator {
- Impersonator& operator = (const TYPE& rhs) {
- reinterpret_cast<TYPE&>(*this) = rhs;
- return *this;
- }
- operator TYPE& () {
- return reinterpret_cast<TYPE&>(*this);
- }
- operator TYPE const& () const {
- return reinterpret_cast<TYPE const&>(*this);
- }
-};
-
-/*
- * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
- * operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template <template<typename T> class BASE, typename T>
-class TVecAddOperators {
-public:
- /* compound assignment from a another vector of the same size but different
- * element type.
- */
- template <typename OTHER>
- BASE<T>& operator += (const BASE<OTHER>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] += v[i];
- }
- return rhs;
- }
- template <typename OTHER>
- BASE<T>& operator -= (const BASE<OTHER>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] -= v[i];
- }
- return rhs;
- }
-
- /* compound assignment from a another vector of the same type.
- * These operators can be used for implicit conversion and handle operations
- * like "vector *= scalar" by letting the compiler implicitly convert a scalar
- * to a vector (assuming the BASE<T> allows it).
- */
- BASE<T>& operator += (const BASE<T>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] += v[i];
- }
- return rhs;
- }
- BASE<T>& operator -= (const BASE<T>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] -= v[i];
- }
- return rhs;
- }
-
- /*
- * NOTE: the functions below ARE NOT member methods. They are friend functions
- * with they definition inlined with their declaration. This makes these
- * template functions available to the compiler when (and only when) this class
- * is instantiated, at which point they're only templated on the 2nd parameter
- * (the first one, BASE<T> being known).
- */
-
- /* The operators below handle operation between vectors of the same side
- * but of a different element type.
- */
- template<typename RT>
- friend inline
- BASE<T> PURE operator +(const BASE<T>& lv, const BASE<RT>& rv) {
- return BASE<T>(lv) += rv;
- }
- template<typename RT>
- friend inline
- BASE<T> PURE operator -(const BASE<T>& lv, const BASE<RT>& rv) {
- return BASE<T>(lv) -= rv;
- }
-
- /* The operators below (which are not templates once this class is instanced,
- * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
- * These handle operations like "vector * scalar" and "scalar * vector" by
- * letting the compiler implicitly convert a scalar to a vector (assuming
- * the BASE<T> allows it).
- */
- friend inline
- BASE<T> PURE operator +(const BASE<T>& lv, const BASE<T>& rv) {
- return BASE<T>(lv) += rv;
- }
- friend inline
- BASE<T> PURE operator -(const BASE<T>& lv, const BASE<T>& rv) {
- return BASE<T>(lv) -= rv;
- }
-};
-
-template <template<typename T> class BASE, typename T>
-class TVecProductOperators {
-public:
- /* compound assignment from a another vector of the same size but different
- * element type.
- */
- template <typename OTHER>
- BASE<T>& operator *= (const BASE<OTHER>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] *= v[i];
- }
- return rhs;
- }
- template <typename OTHER>
- BASE<T>& operator /= (const BASE<OTHER>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] /= v[i];
- }
- return rhs;
- }
-
- /* compound assignment from a another vector of the same type.
- * These operators can be used for implicit conversion and handle operations
- * like "vector *= scalar" by letting the compiler implicitly convert a scalar
- * to a vector (assuming the BASE<T> allows it).
- */
- BASE<T>& operator *= (const BASE<T>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] *= v[i];
- }
- return rhs;
- }
- BASE<T>& operator /= (const BASE<T>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] /= v[i];
- }
- return rhs;
- }
-
- /*
- * NOTE: the functions below ARE NOT member methods. They are friend functions
- * with they definition inlined with their declaration. This makes these
- * template functions available to the compiler when (and only when) this class
- * is instantiated, at which point they're only templated on the 2nd parameter
- * (the first one, BASE<T> being known).
- */
-
- /* The operators below handle operation between vectors of the same side
- * but of a different element type.
- */
- template<typename RT>
- friend inline
- BASE<T> PURE operator *(const BASE<T>& lv, const BASE<RT>& rv) {
- return BASE<T>(lv) *= rv;
- }
- template<typename RT>
- friend inline
- BASE<T> PURE operator /(const BASE<T>& lv, const BASE<RT>& rv) {
- return BASE<T>(lv) /= rv;
- }
-
- /* The operators below (which are not templates once this class is instanced,
- * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
- * These handle operations like "vector * scalar" and "scalar * vector" by
- * letting the compiler implicitly convert a scalar to a vector (assuming
- * the BASE<T> allows it).
- */
- friend inline
- BASE<T> PURE operator *(const BASE<T>& lv, const BASE<T>& rv) {
- return BASE<T>(lv) *= rv;
- }
- friend inline
- BASE<T> PURE operator /(const BASE<T>& lv, const BASE<T>& rv) {
- return BASE<T>(lv) /= rv;
- }
-};
-
-/*
- * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- *
- * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
- */
-template <template<typename T> class BASE, typename T>
-class TVecUnaryOperators {
-public:
- BASE<T>& operator ++ () {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- ++rhs[i];
- }
- return rhs;
- }
- BASE<T>& operator -- () {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- --rhs[i];
- }
- return rhs;
- }
- BASE<T> operator - () const {
- BASE<T> r(BASE<T>::NO_INIT);
- BASE<T> const& rv(static_cast<BASE<T> const&>(*this));
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- r[i] = -rv[i];
- }
- return r;
- }
-};
-
-
-/*
- * TVecComparisonOperators implements relational/comparison operators
- * on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-template <template<typename T> class BASE, typename T>
-class TVecComparisonOperators {
-public:
- /*
- * NOTE: the functions below ARE NOT member methods. They are friend functions
- * with they definition inlined with their declaration. This makes these
- * template functions available to the compiler when (and only when) this class
- * is instantiated, at which point they're only templated on the 2nd parameter
- * (the first one, BASE<T> being known).
- */
- template<typename RT>
- friend inline
- bool PURE operator ==(const BASE<T>& lv, const BASE<RT>& rv) {
- for (size_t i = 0; i < BASE<T>::size(); i++)
- if (lv[i] != rv[i])
- return false;
- return true;
- }
-
- template<typename RT>
- friend inline
- bool PURE operator !=(const BASE<T>& lv, const BASE<RT>& rv) {
- return !operator ==(lv, rv);
- }
-
- template<typename RT>
- friend inline
- bool PURE operator >(const BASE<T>& lv, const BASE<RT>& rv) {
- for (size_t i = 0; i < BASE<T>::size(); i++)
- if (lv[i] <= rv[i])
- return false;
- return true;
- }
-
- template<typename RT>
- friend inline
- bool PURE operator <=(const BASE<T>& lv, const BASE<RT>& rv) {
- return !(lv > rv);
- }
-
- template<typename RT>
- friend inline
- bool PURE operator <(const BASE<T>& lv, const BASE<RT>& rv) {
- for (size_t i = 0; i < BASE<T>::size(); i++)
- if (lv[i] >= rv[i])
- return false;
- return true;
- }
-
- template<typename RT>
- friend inline
- bool PURE operator >=(const BASE<T>& lv, const BASE<RT>& rv) {
- return !(lv < rv);
- }
-};
-
-
-/*
- * TVecFunctions implements functions on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-template <template<typename T> class BASE, typename T>
-class TVecFunctions {
-public:
- /*
- * NOTE: the functions below ARE NOT member methods. They are friend functions
- * with they definition inlined with their declaration. This makes these
- * template functions available to the compiler when (and only when) this class
- * is instantiated, at which point they're only templated on the 2nd parameter
- * (the first one, BASE<T> being known).
- */
- template<typename RT>
- friend inline
- T PURE dot(const BASE<T>& lv, const BASE<RT>& rv) {
- T r(0);
- for (size_t i = 0; i < BASE<T>::size(); i++)
- r += lv[i]*rv[i];
- return r;
- }
-
- friend inline
- T PURE length(const BASE<T>& lv) {
- return sqrt( dot(lv, lv) );
- }
-
- template<typename RT>
- friend inline
- T PURE distance(const BASE<T>& lv, const BASE<RT>& rv) {
- return length(rv - lv);
- }
-
- friend inline
- BASE<T> PURE normalize(const BASE<T>& lv) {
- return lv * (1 / length(lv));
- }
-};
-
-#undef PURE
-
-// -------------------------------------------------------------------------------------
-}; // namespace android
-
-
-#endif /* UI_TVEC_HELPERS_H */
diff --git a/include/ui/mat4.h b/include/ui/mat4.h
deleted file mode 100644
index 4fd1eff..0000000
--- a/include/ui/mat4.h
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#ifndef UI_MAT4_H
-#define UI_MAT4_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec4.h>
-#include <utils/String8.h>
-
-#define TMAT_IMPLEMENTATION
-#include <ui/TMatHelpers.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tmat44 : public TVecUnaryOperators<tmat44, T>,
- public TVecComparisonOperators<tmat44, T>,
- public TVecAddOperators<tmat44, T>,
- public TMatProductOperators<tmat44, T>,
- public TMatSquareFunctions<tmat44, T>,
- public TMatDebug<tmat44, T>
-{
-public:
- enum no_init { NO_INIT };
- typedef T value_type;
- typedef T& reference;
- typedef T const& const_reference;
- typedef size_t size_type;
- typedef tvec4<T> col_type;
- typedef tvec4<T> row_type;
-
- // size of a column (i.e.: number of rows)
- enum { COL_SIZE = col_type::SIZE };
- static inline size_t col_size() { return COL_SIZE; }
-
- // size of a row (i.e.: number of columns)
- enum { ROW_SIZE = row_type::SIZE };
- static inline size_t row_size() { return ROW_SIZE; }
- static inline size_t size() { return row_size(); } // for TVec*<>
-
-private:
-
- /*
- * <-- N columns -->
- *
- * a00 a10 a20 ... aN0 ^
- * a01 a11 a21 ... aN1 |
- * a02 a12 a22 ... aN2 M rows
- * ... |
- * a0M a1M a2M ... aNM v
- *
- * COL_SIZE = M
- * ROW_SIZE = N
- * m[0] = [a00 a01 a02 ... a01M]
- */
-
- col_type mValue[ROW_SIZE];
-
-public:
- // array access
- inline col_type const& operator [] (size_t i) const { return mValue[i]; }
- inline col_type& operator [] (size_t i) { return mValue[i]; }
-
- T const* asArray() const { return &mValue[0][0]; }
-
- // -----------------------------------------------------------------------
- // we don't provide copy-ctor and operator= on purpose
- // because we want the compiler generated versions
-
- /*
- * constructors
- */
-
- // leaves object uninitialized. use with caution.
- explicit tmat44(no_init) { }
-
- // initialize to identity
- tmat44();
-
- // initialize to Identity*scalar.
- template<typename U>
- explicit tmat44(U v);
-
- // sets the diagonal to the passed vector
- template <typename U>
- explicit tmat44(const tvec4<U>& rhs);
-
- // construct from another matrix of the same size
- template <typename U>
- explicit tmat44(const tmat44<U>& rhs);
-
- // construct from 4 column vectors
- template <typename A, typename B, typename C, typename D>
- tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3);
-
- // construct from 16 scalars
- template <
- typename A, typename B, typename C, typename D,
- typename E, typename F, typename G, typename H,
- typename I, typename J, typename K, typename L,
- typename M, typename N, typename O, typename P>
- tmat44( A m00, B m01, C m02, D m03,
- E m10, F m11, G m12, H m13,
- I m20, J m21, K m22, L m23,
- M m30, N m31, O m32, P m33);
-
- // construct from a C array
- template <typename U>
- explicit tmat44(U const* rawArray);
-
- /*
- * helpers
- */
-
- static tmat44 ortho(T left, T right, T bottom, T top, T near, T far);
-
- static tmat44 frustum(T left, T right, T bottom, T top, T near, T far);
-
- template <typename A, typename B, typename C>
- static tmat44 lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up);
-
- template <typename A>
- static tmat44 translate(const tvec4<A>& t);
-
- template <typename A>
- static tmat44 scale(const tvec4<A>& s);
-
- template <typename A, typename B>
- static tmat44 rotate(A radian, const tvec3<B>& about);
-};
-
-// ----------------------------------------------------------------------------------------
-// Constructors
-// ----------------------------------------------------------------------------------------
-
-/*
- * Since the matrix code could become pretty big quickly, we don't inline most
- * operations.
- */
-
-template <typename T>
-tmat44<T>::tmat44() {
- mValue[0] = col_type(1,0,0,0);
- mValue[1] = col_type(0,1,0,0);
- mValue[2] = col_type(0,0,1,0);
- mValue[3] = col_type(0,0,0,1);
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(U v) {
- mValue[0] = col_type(v,0,0,0);
- mValue[1] = col_type(0,v,0,0);
- mValue[2] = col_type(0,0,v,0);
- mValue[3] = col_type(0,0,0,v);
-}
-
-template<typename T>
-template<typename U>
-tmat44<T>::tmat44(const tvec4<U>& v) {
- mValue[0] = col_type(v.x,0,0,0);
- mValue[1] = col_type(0,v.y,0,0);
- mValue[2] = col_type(0,0,v.z,0);
- mValue[3] = col_type(0,0,0,v.w);
-}
-
-// construct from 16 scalars
-template<typename T>
-template <
- typename A, typename B, typename C, typename D,
- typename E, typename F, typename G, typename H,
- typename I, typename J, typename K, typename L,
- typename M, typename N, typename O, typename P>
-tmat44<T>::tmat44( A m00, B m01, C m02, D m03,
- E m10, F m11, G m12, H m13,
- I m20, J m21, K m22, L m23,
- M m30, N m31, O m32, P m33) {
- mValue[0] = col_type(m00, m01, m02, m03);
- mValue[1] = col_type(m10, m11, m12, m13);
- mValue[2] = col_type(m20, m21, m22, m23);
- mValue[3] = col_type(m30, m31, m32, m33);
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(const tmat44<U>& rhs) {
- for (size_t r=0 ; r<row_size() ; r++)
- mValue[r] = rhs[r];
-}
-
-template <typename T>
-template <typename A, typename B, typename C, typename D>
-tmat44<T>::tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3) {
- mValue[0] = v0;
- mValue[1] = v1;
- mValue[2] = v2;
- mValue[3] = v3;
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(U const* rawArray) {
- for (size_t r=0 ; r<row_size() ; r++)
- for (size_t c=0 ; c<col_size() ; c++)
- mValue[r][c] = *rawArray++;
-}
-
-// ----------------------------------------------------------------------------------------
-// Helpers
-// ----------------------------------------------------------------------------------------
-
-template <typename T>
-tmat44<T> tmat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
- tmat44<T> m;
- m[0][0] = 2 / (right - left);
- m[1][1] = 2 / (top - bottom);
- m[2][2] = -2 / (far - near);
- m[3][0] = -(right + left) / (right - left);
- m[3][1] = -(top + bottom) / (top - bottom);
- m[3][2] = -(far + near) / (far - near);
- return m;
-}
-
-template <typename T>
-tmat44<T> tmat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
- tmat44<T> m;
- T A = (right + left) / (right - left);
- T B = (top + bottom) / (top - bottom);
- T C = (far + near) / (far - near);
- T D = (2 * far * near) / (far - near);
- m[0][0] = (2 * near) / (right - left);
- m[1][1] = (2 * near) / (top - bottom);
- m[2][0] = A;
- m[2][1] = B;
- m[2][2] = C;
- m[2][3] =-1;
- m[3][2] = D;
- m[3][3] = 0;
- return m;
-}
-
-template <typename T>
-template <typename A, typename B, typename C>
-tmat44<T> tmat44<T>::lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up) {
- tvec3<T> L(normalize(center - eye));
- tvec3<T> S(normalize( cross(L, up) ));
- tvec3<T> U(cross(S, L));
- return tmat44<T>(
- tvec4<T>( S, 0),
- tvec4<T>( U, 0),
- tvec4<T>(-L, 0),
- tvec4<T>(-eye, 1));
-}
-
-template <typename T>
-template <typename A>
-tmat44<T> tmat44<T>::translate(const tvec4<A>& t) {
- tmat44<T> r;
- r[3] = t;
- return r;
-}
-
-template <typename T>
-template <typename A>
-tmat44<T> tmat44<T>::scale(const tvec4<A>& s) {
- tmat44<T> r;
- r[0][0] = s[0];
- r[1][1] = s[1];
- r[2][2] = s[2];
- r[3][3] = s[3];
- return r;
-}
-
-template <typename T>
-template <typename A, typename B>
-tmat44<T> tmat44<T>::rotate(A radian, const tvec3<B>& about) {
- tmat44<T> rotation;
- T* r = const_cast<T*>(rotation.asArray());
- T c = cos(radian);
- T s = sin(radian);
- if (about.x==1 && about.y==0 && about.z==0) {
- r[5] = c; r[10]= c;
- r[6] = s; r[9] = -s;
- } else if (about.x==0 && about.y==1 && about.z==0) {
- r[0] = c; r[10]= c;
- r[8] = s; r[2] = -s;
- } else if (about.x==0 && about.y==0 && about.z==1) {
- r[0] = c; r[5] = c;
- r[1] = s; r[4] = -s;
- } else {
- tvec3<B> nabout = normalize(about);
- B x = nabout.x;
- B y = nabout.y;
- B z = nabout.z;
- T nc = 1 - c;
- T xy = x * y;
- T yz = y * z;
- T zx = z * x;
- T xs = x * s;
- T ys = y * s;
- T zs = z * s;
- r[ 0] = x*x*nc + c; r[ 4] = xy*nc - zs; r[ 8] = zx*nc + ys;
- r[ 1] = xy*nc + zs; r[ 5] = y*y*nc + c; r[ 9] = yz*nc - xs;
- r[ 2] = zx*nc - ys; r[ 6] = yz*nc + xs; r[10] = z*z*nc + c;
- }
- return rotation;
-}
-
-// ----------------------------------------------------------------------------------------
-// Arithmetic operators outside of class
-// ----------------------------------------------------------------------------------------
-
-/* We use non-friend functions here to prevent the compiler from using
- * implicit conversions, for instance of a scalar to a vector. The result would
- * not be what the caller expects.
- *
- * Also note that the order of the arguments in the inner loop is important since
- * it determines the output type (only relevant when T != U).
- */
-
-// matrix * vector, result is a vector of the same type than the input vector
-template <typename T, typename U>
-typename tmat44<U>::col_type PURE operator *(const tmat44<T>& lv, const tvec4<U>& rv) {
- typename tmat44<U>::col_type result;
- for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
- result += rv[r]*lv[r];
- return result;
-}
-
-// vector * matrix, result is a vector of the same type than the input vector
-template <typename T, typename U>
-typename tmat44<U>::row_type PURE operator *(const tvec4<U>& rv, const tmat44<T>& lv) {
- typename tmat44<U>::row_type result(tmat44<U>::row_type::NO_INIT);
- for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
- result[r] = dot(rv, lv[r]);
- return result;
-}
-
-// matrix * scalar, result is a matrix of the same type than the input matrix
-template <typename T, typename U>
-tmat44<T> PURE operator *(const tmat44<T>& lv, U rv) {
- tmat44<T> result(tmat44<T>::NO_INIT);
- for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
- result[r] = lv[r]*rv;
- return result;
-}
-
-// scalar * matrix, result is a matrix of the same type than the input matrix
-template <typename T, typename U>
-tmat44<T> PURE operator *(U rv, const tmat44<T>& lv) {
- tmat44<T> result(tmat44<T>::NO_INIT);
- for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
- result[r] = lv[r]*rv;
- return result;
-}
-
-// ----------------------------------------------------------------------------------------
-
-/* FIXME: this should go into TMatSquareFunctions<> but for some reason
- * BASE<T>::col_type is not accessible from there (???)
- */
-template<typename T>
-typename tmat44<T>::col_type PURE diag(const tmat44<T>& m) {
- return matrix::diag(m);
-}
-
-// ----------------------------------------------------------------------------------------
-
-typedef tmat44<float> mat4;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#undef PURE
-
-#endif /* UI_MAT4_H */
diff --git a/include/ui/vec2.h b/include/ui/vec2.h
deleted file mode 100644
index c31d0e4..0000000
--- a/include/ui/vec2.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#ifndef UI_VEC2_H
-#define UI_VEC2_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#define TVEC_IMPLEMENTATION
-#include <ui/TVecHelpers.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec2 : public TVecProductOperators<tvec2, T>,
- public TVecAddOperators<tvec2, T>,
- public TVecUnaryOperators<tvec2, T>,
- public TVecComparisonOperators<tvec2, T>,
- public TVecFunctions<tvec2, T>
-{
-public:
- enum no_init { NO_INIT };
- typedef T value_type;
- typedef T& reference;
- typedef T const& const_reference;
- typedef size_t size_type;
-
- union {
- struct { T x, y; };
- struct { T s, t; };
- struct { T r, g; };
- };
-
- enum { SIZE = 2 };
- inline static size_type size() { return SIZE; }
-
- // array access
- inline T const& operator [] (size_t i) const { return (&x)[i]; }
- inline T& operator [] (size_t i) { return (&x)[i]; }
-
- // -----------------------------------------------------------------------
- // we don't provide copy-ctor and operator= on purpose
- // because we want the compiler generated versions
-
- // constructors
-
- // leaves object uninitialized. use with caution.
- explicit tvec2(no_init) { }
-
- // default constructor
- tvec2() : x(0), y(0) { }
-
- // handles implicit conversion to a tvec4. must not be explicit.
- template<typename A>
- tvec2(A v) : x(v), y(v) { }
-
- template<typename A, typename B>
- tvec2(A x, B y) : x(x), y(y) { }
-
- template<typename A>
- explicit tvec2(const tvec2<A>& v) : x(v.x), y(v.y) { }
-
- template<typename A>
- tvec2(const Impersonator< tvec2<A> >& v)
- : x(((const tvec2<A>&)v).x),
- y(((const tvec2<A>&)v).y) { }
-};
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec2<float> vec2;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/ui/vec3.h b/include/ui/vec3.h
deleted file mode 100644
index dde59a9..0000000
--- a/include/ui/vec3.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#ifndef UI_VEC3_H
-#define UI_VEC3_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec2.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec3 : public TVecProductOperators<tvec3, T>,
- public TVecAddOperators<tvec3, T>,
- public TVecUnaryOperators<tvec3, T>,
- public TVecComparisonOperators<tvec3, T>,
- public TVecFunctions<tvec3, T>
-{
-public:
- enum no_init { NO_INIT };
- typedef T value_type;
- typedef T& reference;
- typedef T const& const_reference;
- typedef size_t size_type;
-
- union {
- struct { T x, y, z; };
- struct { T s, t, p; };
- struct { T r, g, b; };
- Impersonator< tvec2<T> > xy;
- Impersonator< tvec2<T> > st;
- Impersonator< tvec2<T> > rg;
- };
-
- enum { SIZE = 3 };
- inline static size_type size() { return SIZE; }
-
- // array access
- inline T const& operator [] (size_t i) const { return (&x)[i]; }
- inline T& operator [] (size_t i) { return (&x)[i]; }
-
- // -----------------------------------------------------------------------
- // we don't provide copy-ctor and operator= on purpose
- // because we want the compiler generated versions
-
- // constructors
- // leaves object uninitialized. use with caution.
- explicit tvec3(no_init) { }
-
- // default constructor
- tvec3() : x(0), y(0), z(0) { }
-
- // handles implicit conversion to a tvec4. must not be explicit.
- template<typename A>
- tvec3(A v) : x(v), y(v), z(v) { }
-
- template<typename A, typename B, typename C>
- tvec3(A x, B y, C z) : x(x), y(y), z(z) { }
-
- template<typename A, typename B>
- tvec3(const tvec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
-
- template<typename A>
- explicit tvec3(const tvec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
-
- template<typename A>
- tvec3(const Impersonator< tvec3<A> >& v)
- : x(((const tvec3<A>&)v).x),
- y(((const tvec3<A>&)v).y),
- z(((const tvec3<A>&)v).z) { }
-
- template<typename A, typename B>
- tvec3(const Impersonator< tvec2<A> >& v, B z)
- : x(((const tvec2<A>&)v).x),
- y(((const tvec2<A>&)v).y),
- z(z) { }
-
- // cross product works only on vectors of size 3
- template <typename RT>
- friend inline
- tvec3 __attribute__((pure)) cross(const tvec3& u, const tvec3<RT>& v) {
- return tvec3(
- u.y*v.z - u.z*v.y,
- u.z*v.x - u.x*v.z,
- u.x*v.y - u.y*v.x);
- }
-};
-
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec3<float> vec3;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/ui/vec4.h b/include/ui/vec4.h
deleted file mode 100644
index e03d331..0000000
--- a/include/ui/vec4.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#ifndef UI_VEC4_H
-#define UI_VEC4_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec3.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec4 : public TVecProductOperators<tvec4, T>,
- public TVecAddOperators<tvec4, T>,
- public TVecUnaryOperators<tvec4, T>,
- public TVecComparisonOperators<tvec4, T>,
- public TVecFunctions<tvec4, T>
-{
-public:
- enum no_init { NO_INIT };
- typedef T value_type;
- typedef T& reference;
- typedef T const& const_reference;
- typedef size_t size_type;
-
- union {
- struct { T x, y, z, w; };
- struct { T s, t, p, q; };
- struct { T r, g, b, a; };
- Impersonator< tvec2<T> > xy;
- Impersonator< tvec2<T> > st;
- Impersonator< tvec2<T> > rg;
- Impersonator< tvec3<T> > xyz;
- Impersonator< tvec3<T> > stp;
- Impersonator< tvec3<T> > rgb;
- };
-
- enum { SIZE = 4 };
- inline static size_type size() { return SIZE; }
-
- // array access
- inline T const& operator [] (size_t i) const { return (&x)[i]; }
- inline T& operator [] (size_t i) { return (&x)[i]; }
-
- // -----------------------------------------------------------------------
- // we don't provide copy-ctor and operator= on purpose
- // because we want the compiler generated versions
-
- // constructors
-
- // leaves object uninitialized. use with caution.
- explicit tvec4(no_init) { }
-
- // default constructor
- tvec4() : x(0), y(0), z(0), w(0) { }
-
- // handles implicit conversion to a tvec4. must not be explicit.
- template<typename A>
- tvec4(A v) : x(v), y(v), z(v), w(v) { }
-
- template<typename A, typename B, typename C, typename D>
- tvec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
-
- template<typename A, typename B, typename C>
- tvec4(const tvec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
-
- template<typename A, typename B>
- tvec4(const tvec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
-
- template<typename A>
- explicit tvec4(const tvec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
-
- template<typename A>
- tvec4(const Impersonator< tvec4<A> >& v)
- : x(((const tvec4<A>&)v).x),
- y(((const tvec4<A>&)v).y),
- z(((const tvec4<A>&)v).z),
- w(((const tvec4<A>&)v).w) { }
-
- template<typename A, typename B>
- tvec4(const Impersonator< tvec3<A> >& v, B w)
- : x(((const tvec3<A>&)v).x),
- y(((const tvec3<A>&)v).y),
- z(((const tvec3<A>&)v).z),
- w(w) { }
-
- template<typename A, typename B, typename C>
- tvec4(const Impersonator< tvec2<A> >& v, B z, C w)
- : x(((const tvec2<A>&)v).x),
- y(((const tvec2<A>&)v).y),
- z(z),
- w(w) { }
-};
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec4<float> vec4;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/vr/vr_manager/vr_manager.h b/include/vr/vr_manager/vr_manager.h
new file mode 100644
index 0000000..9df2c6b
--- /dev/null
+++ b/include/vr/vr_manager/vr_manager.h
@@ -0,0 +1,91 @@
+/*
+ * 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_VR_MANAGER_H
+#define ANDROID_VR_MANAGER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// Must be kept in sync with interface defined in IVrStateCallbacks.aidl.
+
+class IVrStateCallbacks : public IInterface {
+public:
+ DECLARE_META_INTERFACE(VrStateCallbacks)
+
+ virtual void onVrStateChanged(bool enabled) = 0;
+};
+
+enum VrStateCallbacksTransaction {
+ ON_VR_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BnVrStateCallbacks : public BnInterface<IVrStateCallbacks> {
+public:
+ status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0) override;
+};
+
+
+// Must be kept in sync with interface defined in
+// IPersistentVrStateCallbacks.aidl.
+
+class IPersistentVrStateCallbacks : public IInterface {
+public:
+ DECLARE_META_INTERFACE(PersistentVrStateCallbacks)
+
+ virtual void onPersistentVrStateChanged(bool enabled) = 0;
+};
+
+enum PersistentVrStateCallbacksTransaction {
+ ON_PERSISTENT_VR_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BnPersistentVrStateCallbacks
+ : public BnInterface<IPersistentVrStateCallbacks> {
+public:
+ status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0) override;
+};
+
+
+// Must be kept in sync with interface defined in IVrManager.aidl.
+
+class IVrManager : public IInterface {
+public:
+ DECLARE_META_INTERFACE(VrManager)
+
+ virtual void registerListener(const sp<IVrStateCallbacks>& cb) = 0;
+ virtual void unregisterListener(const sp<IVrStateCallbacks>& cb) = 0;
+ virtual void registerPersistentVrStateListener(
+ const sp<IPersistentVrStateCallbacks>& cb) = 0;
+ virtual void unregisterPersistentVrStateListener(
+ const sp<IPersistentVrStateCallbacks>& cb) = 0;
+ virtual bool getVrModeState() = 0;
+};
+
+enum VrManagerTransaction {
+ REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
+ UNREGISTER_LISTENER,
+ REGISTER_PERSISTENT_VR_STATE_LISTENER,
+ UNREGISTER_PERSISTENT_VR_STATE_LISTENER,
+ GET_VR_MODE_STATE,
+};
+
+}; // namespace android
+
+#endif // ANDROID_VR_MANAGER_H
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
new file mode 100644
index 0000000..0d25176
--- /dev/null
+++ b/libs/arect/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+ndk_headers {
+ name: "libarect_headers",
+ from: "include/android",
+ to: "android",
+ srcs: ["include/android/*.h"],
+ license: "NOTICE",
+}
+
+cc_library_static {
+ name: "libarect",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
diff --git a/libs/arect/MODULE_LICENSE_APACHE2 b/libs/arect/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/arect/MODULE_LICENSE_APACHE2
diff --git a/libs/arect/NOTICE b/libs/arect/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/arect/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/include/android/rect.h b/libs/arect/include/android/rect.h
similarity index 100%
rename from include/android/rect.h
rename to libs/arect/include/android/rect.h
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
new file mode 100644
index 0000000..204fdb5
--- /dev/null
+++ b/libs/binder/Android.bp
@@ -0,0 +1,92 @@
+// Copyright (C) 2009 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.
+
+cc_library_headers {
+ name: "libbinder_headers",
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libbinder",
+
+ // for vndbinder
+ vendor_available: true,
+
+ srcs: [
+ "AppOpsManager.cpp",
+ "Binder.cpp",
+ "BpBinder.cpp",
+ "BufferedTextOutput.cpp",
+ "Debug.cpp",
+ "IActivityManager.cpp",
+ "IAppOpsCallback.cpp",
+ "IAppOpsService.cpp",
+ "IBatteryStats.cpp",
+ "IInterface.cpp",
+ "IMediaResourceMonitor.cpp",
+ "IMemory.cpp",
+ "IPCThreadState.cpp",
+ "IPermissionController.cpp",
+ "IProcessInfoService.cpp",
+ "IResultReceiver.cpp",
+ "IServiceManager.cpp",
+ "IShellCallback.cpp",
+ "MemoryBase.cpp",
+ "MemoryDealer.cpp",
+ "MemoryHeapBase.cpp",
+ "Parcel.cpp",
+ "PermissionCache.cpp",
+ "PersistableBundle.cpp",
+ "ProcessInfoService.cpp",
+ "ProcessState.cpp",
+ "Static.cpp",
+ "Status.cpp",
+ "TextOutput.cpp",
+ "IpPrefix.cpp",
+ "Value.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ product_variables: {
+ binder32bit: {
+ cflags: ["-DBINDER_IPC_32BIT=1"],
+ },
+ },
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libutils",
+ ],
+ export_shared_lib_headers: [
+ "libbase",
+ "libutils",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ clang: true,
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+}
+
+subdirs = ["tests"]
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
deleted file mode 100644
index 14be920..0000000
--- a/libs/binder/Android.mk
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright (C) 2009 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.
-
-# we have the common sources, plus some device-specific stuff
-sources := \
- AppOpsManager.cpp \
- Binder.cpp \
- BpBinder.cpp \
- BufferedTextOutput.cpp \
- Debug.cpp \
- IAppOpsCallback.cpp \
- IAppOpsService.cpp \
- IBatteryStats.cpp \
- IInterface.cpp \
- IMediaResourceMonitor.cpp \
- IMemory.cpp \
- IPCThreadState.cpp \
- IPermissionController.cpp \
- IProcessInfoService.cpp \
- IResultReceiver.cpp \
- IServiceManager.cpp \
- MemoryBase.cpp \
- MemoryDealer.cpp \
- MemoryHeapBase.cpp \
- Parcel.cpp \
- PermissionCache.cpp \
- PersistableBundle.cpp \
- ProcessInfoService.cpp \
- ProcessState.cpp \
- Static.cpp \
- Status.cpp \
- TextOutput.cpp \
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinder
-LOCAL_SHARED_LIBRARIES := liblog libcutils libutils
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES := $(sources)
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-LOCAL_CFLAGS += -DBINDER_IPC_32BIT=1
-endif
-endif
-LOCAL_CFLAGS += -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinder
-LOCAL_STATIC_LIBRARIES += libutils
-LOCAL_SRC_FILES := $(sources)
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-LOCAL_CFLAGS += -DBINDER_IPC_32BIT=1
-endif
-endif
-LOCAL_CFLAGS += -Werror
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 9a061a0..f3b86ae 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <mutex>
#include <binder/AppOpsManager.h>
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
@@ -22,6 +23,19 @@
namespace android {
+namespace {
+
+#if defined(__BRILLO__)
+// Because Brillo has no application model, security policy is managed
+// statically (at build time) with SELinux controls.
+// As a consequence, it also never runs the AppOpsManager service.
+const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_ALLOWED;
+#else
+const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_IGNORED;
+#endif // defined(__BRILLO__)
+
+} // namespace
+
static String16 _appops("appops");
static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
static sp<IBinder> gToken;
@@ -39,10 +53,15 @@
{
}
+#if defined(__BRILLO__)
+// There is no AppOpsService on Brillo
+sp<IAppOpsService> AppOpsManager::getService() { return NULL; }
+#else
sp<IAppOpsService> AppOpsManager::getService()
{
+
+ std::lock_guard<Mutex> scoped_lock(mLock);
int64_t startTime = 0;
- mLock.lock();
sp<IAppOpsService> service = mService;
while (service == NULL || !IInterface::asBinder(service)->isBinderAlive()) {
sp<IBinder> binder = defaultServiceManager()->checkService(_appops);
@@ -53,7 +72,8 @@
ALOGI("Waiting for app ops service");
} else if ((uptimeMillis()-startTime) > 10000) {
ALOGW("Waiting too long for app ops service, giving up");
- return NULL;
+ service = NULL;
+ break;
}
sleep(1);
} else {
@@ -61,25 +81,30 @@
mService = service;
}
}
- mLock.unlock();
return service;
}
+#endif // defined(__BRILLO__)
int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
{
sp<IAppOpsService> service = getService();
- return service != NULL ? service->checkOperation(op, uid, callingPackage) : MODE_IGNORED;
+ return service != NULL
+ ? service->checkOperation(op, uid, callingPackage)
+ : APP_OPS_MANAGER_UNAVAILABLE_MODE;
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
sp<IAppOpsService> service = getService();
- return service != NULL ? service->noteOperation(op, uid, callingPackage) : MODE_IGNORED;
+ return service != NULL
+ ? service->noteOperation(op, uid, callingPackage)
+ : APP_OPS_MANAGER_UNAVAILABLE_MODE;
}
int32_t AppOpsManager::startOp(int32_t op, int32_t uid, const String16& callingPackage) {
sp<IAppOpsService> service = getService();
- return service != NULL ? service->startOperation(getToken(service), op, uid, callingPackage)
- : MODE_IGNORED;
+ return service != NULL
+ ? service->startOperation(getToken(service), op, uid, callingPackage)
+ : APP_OPS_MANAGER_UNAVAILABLE_MODE;
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index c4d47ca..890ef30 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -21,6 +21,7 @@
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
#include <binder/IResultReceiver.h>
+#include <binder/IShellCallback.h>
#include <binder/Parcel.h>
#include <stdio.h>
@@ -62,7 +63,8 @@
status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err,
- Vector<String16>& args, const sp<IResultReceiver>& resultReceiver)
+ Vector<String16>& args, const sp<IShellCallback>& callback,
+ const sp<IResultReceiver>& resultReceiver)
{
Parcel send;
Parcel reply;
@@ -74,6 +76,7 @@
for (size_t i = 0; i < numArgs; i++) {
send.writeString16(args[i]);
}
+ send.writeStrongBinder(callback != NULL ? IInterface::asBinder(callback) : NULL);
send.writeStrongBinder(resultReceiver != NULL ? IInterface::asBinder(resultReceiver) : NULL);
return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);
}
@@ -232,11 +235,17 @@
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(data.readString16());
}
+ sp<IShellCallback> shellCallback = IShellCallback::asInterface(
+ data.readStrongBinder());
sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
data.readStrongBinder());
// XXX can't add virtuals until binaries are updated.
//return shellCommand(in, out, err, args, resultReceiver);
+ (void)in;
+ (void)out;
+ (void)err;
+
if (resultReceiver != NULL) {
resultReceiver->send(INVALID_OPERATION);
}
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 1339a67..a2443c0 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -34,7 +34,7 @@
struct BufferedTextOutput::BufferState : public RefBase
{
- BufferState(int32_t _seq)
+ explicit BufferState(int32_t _seq)
: seq(_seq)
, buffer(NULL)
, bufferPos(0)
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
new file mode 100644
index 0000000..50a8b28
--- /dev/null
+++ b/libs/binder/IActivityManager.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IActivityManager.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class BpActivityManager : public BpInterface<IActivityManager>
+{
+public:
+ explicit BpActivityManager(const sp<IBinder>& impl)
+ : BpInterface<IActivityManager>(impl)
+ {
+ }
+
+ virtual int openContentUri(const String16& stringUri)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeString16(stringUri);
+ status_t ret = remote()->transact(OPEN_CONTENT_URI_TRANSACTION, data, & reply);
+ int fd = -1;
+ if (ret == NO_ERROR) {
+ int32_t exceptionCode = reply.readExceptionCode();
+ if (!exceptionCode) {
+ // Success is indicated here by a nonzero int followed by the fd;
+ // failure by a zero int with no data following.
+ if (reply.readInt32() != 0) {
+ fd = fcntl(reply.readParcelFileDescriptor(), F_DUPFD_CLOEXEC, 0);
+ }
+ } else {
+ // An exception was thrown back; fall through to return failure
+ ALOGD("openContentUri(%s) caught exception %d\n",
+ String8(stringUri).string(), exceptionCode);
+ }
+ }
+ return fd;
+ }
+};
+
+// ------------------------------------------------------------------------------------
+
+IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager");
+
+}; // namespace android
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
index 2aaf566..f9ec593 100644
--- a/libs/binder/IAppOpsCallback.cpp
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -31,7 +31,7 @@
class BpAppOpsCallback : public BpInterface<IAppOpsCallback>
{
public:
- BpAppOpsCallback(const sp<IBinder>& impl)
+ explicit BpAppOpsCallback(const sp<IBinder>& impl)
: BpInterface<IAppOpsCallback>(impl)
{
}
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 9558376..638ae5c 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -31,7 +31,7 @@
class BpAppOpsService : public BpInterface<IAppOpsService>
{
public:
- BpAppOpsService(const sp<IBinder>& impl)
+ explicit BpAppOpsService(const sp<IBinder>& impl)
: BpInterface<IAppOpsService>(impl)
{
}
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index e32c628..ad1e69f 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -29,7 +29,7 @@
class BpBatteryStats : public BpInterface<IBatteryStats>
{
public:
- BpBatteryStats(const sp<IBinder>& impl)
+ explicit BpBatteryStats(const sp<IBinder>& impl)
: BpInterface<IBatteryStats>(impl)
{
}
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 4800f5b..77e3d23 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -25,7 +25,7 @@
class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
public:
- BpMediaResourceMonitor(const sp<IBinder>& impl)
+ explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
: BpInterface<IMediaResourceMonitor>(impl) {}
virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 5f345cf..5c1a4f4 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -16,22 +16,24 @@
#define LOG_TAG "IMemory"
+#include <atomic>
+#include <stdatomic.h>
+
+#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-
#include <sys/types.h>
#include <sys/mman.h>
+#include <unistd.h>
#include <binder/IMemory.h>
-#include <cutils/log.h>
+#include <binder/Parcel.h>
+#include <log/log.h>
+
+#include <utils/CallStack.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
-#include <utils/Atomic.h>
-#include <binder/Parcel.h>
-#include <utils/CallStack.h>
#define VERBOSE 0
@@ -56,12 +58,15 @@
struct heap_info_t {
sp<IMemoryHeap> heap;
int32_t count;
+ // Note that this cannot be meaningfully copied.
};
void free_heap(const wp<IBinder>& binder);
- Mutex mHeapCacheLock;
+ Mutex mHeapCacheLock; // Protects entire vector below.
KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
+ // We do not use the copy-on-write capabilities of KeyedVector.
+ // TODO: Reimplemement based on standard C++ container?
};
static sp<HeapCache> gHeapCache = new HeapCache();
@@ -75,7 +80,7 @@
class BpMemoryHeap : public BpInterface<IMemoryHeap>
{
public:
- BpMemoryHeap(const sp<IBinder>& impl);
+ explicit BpMemoryHeap(const sp<IBinder>& impl);
virtual ~BpMemoryHeap();
virtual int getHeapID() const;
@@ -105,7 +110,7 @@
void assertMapped() const;
void assertReallyMapped() const;
- mutable volatile int32_t mHeapId;
+ mutable std::atomic<int32_t> mHeapId;
mutable void* mBase;
mutable size_t mSize;
mutable uint32_t mFlags;
@@ -123,7 +128,7 @@
class BpMemory : public BpInterface<IMemory>
{
public:
- BpMemory(const sp<IBinder>& impl);
+ explicit BpMemory(const sp<IBinder>& impl);
virtual ~BpMemory();
virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const;
@@ -248,8 +253,9 @@
}
BpMemoryHeap::~BpMemoryHeap() {
- if (mHeapId != -1) {
- close(mHeapId);
+ int32_t heapId = mHeapId.load(memory_order_relaxed);
+ if (heapId != -1) {
+ close(heapId);
if (mRealHeap) {
// by construction we're the last one
if (mBase != MAP_FAILED) {
@@ -257,7 +263,7 @@
if (VERBOSE) {
ALOGD("UNMAPPING binder=%p, heap=%p, size=%zu, fd=%d",
- binder.get(), this, mSize, mHeapId);
+ binder.get(), this, mSize, heapId);
CallStack stack(LOG_TAG);
}
@@ -273,17 +279,21 @@
void BpMemoryHeap::assertMapped() const
{
- if (mHeapId == -1) {
+ int32_t heapId = mHeapId.load(memory_order_acquire);
+ if (heapId == -1) {
sp<IBinder> binder(IInterface::asBinder(const_cast<BpMemoryHeap*>(this)));
sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
heap->assertReallyMapped();
if (heap->mBase != MAP_FAILED) {
Mutex::Autolock _l(mLock);
- if (mHeapId == -1) {
+ if (mHeapId.load(memory_order_relaxed) == -1) {
mBase = heap->mBase;
mSize = heap->mSize;
mOffset = heap->mOffset;
- android_atomic_write( dup( heap->mHeapId ), &mHeapId );
+ int fd = fcntl(heap->mHeapId.load(memory_order_relaxed), F_DUPFD_CLOEXEC, 0);
+ ALOGE_IF(fd==-1, "cannot dup fd=%d",
+ heap->mHeapId.load(memory_order_relaxed));
+ mHeapId.store(fd, memory_order_release);
}
} else {
// something went wrong
@@ -294,7 +304,8 @@
void BpMemoryHeap::assertReallyMapped() const
{
- if (mHeapId == -1) {
+ int32_t heapId = mHeapId.load(memory_order_acquire);
+ if (heapId == -1) {
// remote call without mLock held, worse case scenario, we end up
// calling transact() from multiple threads, but that's not a problem,
@@ -313,8 +324,8 @@
parcel_fd, size, err, strerror(-err));
Mutex::Autolock _l(mLock);
- if (mHeapId == -1) {
- int fd = dup( parcel_fd );
+ if (mHeapId.load(memory_order_relaxed) == -1) {
+ int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0);
ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
parcel_fd, size, err, strerror(errno));
@@ -322,7 +333,6 @@
if (!(flags & READ_ONLY)) {
access |= PROT_WRITE;
}
-
mRealHeap = true;
mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
if (mBase == MAP_FAILED) {
@@ -333,7 +343,7 @@
mSize = size;
mFlags = flags;
mOffset = offset;
- android_atomic_write(fd, &mHeapId);
+ mHeapId.store(fd, memory_order_release);
}
}
}
@@ -341,7 +351,8 @@
int BpMemoryHeap::getHeapID() const {
assertMapped();
- return mHeapId;
+ // We either stored mHeapId ourselves, or loaded it with acquire semantics.
+ return mHeapId.load(memory_order_relaxed);
}
void* BpMemoryHeap::getBase() const {
@@ -418,9 +429,10 @@
"found binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
binder.get(), info.heap.get(),
static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
- static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
+ static_cast<BpMemoryHeap*>(info.heap.get())
+ ->mHeapId.load(memory_order_relaxed),
info.count);
- android_atomic_inc(&info.count);
+ ++info.count;
return info.heap;
} else {
heap_info_t info;
@@ -445,13 +457,13 @@
ssize_t i = mHeapCache.indexOfKey(binder);
if (i>=0) {
heap_info_t& info(mHeapCache.editValueAt(i));
- int32_t c = android_atomic_dec(&info.count);
- if (c == 1) {
+ if (--info.count == 0) {
ALOGD_IF(VERBOSE,
"removing binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
binder.unsafe_get(), info.heap.get(),
static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
- static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
+ static_cast<BpMemoryHeap*>(info.heap.get())
+ ->mHeapId.load(memory_order_relaxed),
info.count);
rel = mHeapCache.valueAt(i).heap;
mHeapCache.removeItemsAt(i);
@@ -482,7 +494,7 @@
ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)",
mHeapCache.keyAt(i).unsafe_get(),
info.heap.get(), info.count,
- h->mHeapId, h->mBase, h->mSize);
+ h->mHeapId.load(memory_order_relaxed), h->mBase, h->mSize);
}
}
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index eccc400..e832961 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -65,10 +65,6 @@
namespace android {
-static const char* getReturnString(size_t idx);
-static const void* printReturnCommand(TextOutput& out, const void* _cmd);
-static const void* printCommand(TextOutput& out, const void* _cmd);
-
// Static const and functions will be optimized out if not used,
// when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out.
static const char *kReturnStrings[] = {
@@ -112,8 +108,9 @@
"BC_DEAD_BINDER_DONE"
};
-static const char* getReturnString(size_t idx)
+static const char* getReturnString(uint32_t cmd)
{
+ size_t idx = cmd & 0xff;
if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0]))
return kReturnStrings[idx];
else
@@ -331,6 +328,7 @@
delete st;
pthread_setspecific(gTLS, NULL);
}
+ pthread_key_delete(gTLS);
gHaveTLS = false;
}
}
@@ -340,6 +338,11 @@
gDisableBackgroundScheduling = disable;
}
+bool IPCThreadState::backgroundSchedulingDisabled()
+{
+ return gDisableBackgroundScheduling;
+}
+
sp<ProcessState> IPCThreadState::process()
{
return mProcess;
@@ -512,9 +515,9 @@
}
} while (result != -ECONNREFUSED && result != -EBADF);
- LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
- (void*)pthread_self(), getpid(), (void*)result);
-
+ LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",
+ (void*)pthread_self(), getpid(), result);
+
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}
@@ -655,7 +658,7 @@
waitForResponse(NULL, &result);
#if LOG_REFCOUNTS
- printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n",
+ ALOGV("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n",
handle, result == NO_ERROR ? "SUCCESS" : "FAILURE");
#endif
@@ -670,7 +673,7 @@
void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder)
{
#if LOG_REFCOUNTS
- printf("IPCThreadState::expungeHandle(%ld)\n", handle);
+ ALOGV("IPCThreadState::expungeHandle(%ld)\n", handle);
#endif
self()->mProcess->expungeHandle(handle, binder);
}
@@ -1136,7 +1139,7 @@
break;
default:
- printf("*** BAD COMMAND %d received from Binder driver\n", cmd);
+ ALOGE("*** BAD COMMAND %d received from Binder driver\n", cmd);
result = UNKNOWN_ERROR;
break;
}
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
index 6bba996..674bddf 100644
--- a/libs/binder/IPermissionController.cpp
+++ b/libs/binder/IPermissionController.cpp
@@ -31,7 +31,7 @@
class BpPermissionController : public BpInterface<IPermissionController>
{
public:
- BpPermissionController(const sp<IBinder>& impl)
+ explicit BpPermissionController(const sp<IBinder>& impl)
: BpInterface<IPermissionController>(impl)
{
}
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index 76508b8..96e1a8c 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -25,7 +25,7 @@
class BpProcessInfoService : public BpInterface<IProcessInfoService> {
public:
- BpProcessInfoService(const sp<IBinder>& impl)
+ explicit BpProcessInfoService(const sp<IBinder>& impl)
: BpInterface<IProcessInfoService>(impl) {}
virtual status_t getProcessStatesFromPids(size_t length, /*in*/ int32_t* pids,
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index 2a22b69..646809e 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -31,7 +31,7 @@
class BpResultReceiver : public BpInterface<IResultReceiver>
{
public:
- BpResultReceiver(const sp<IBinder>& impl)
+ explicit BpResultReceiver(const sp<IBinder>& impl)
: BpInterface<IResultReceiver>(impl)
{
}
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 44d235f..c7a0f43 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -23,6 +23,7 @@
#include <binder/Parcel.h>
#include <utils/String8.h>
#include <utils/SystemClock.h>
+#include <utils/CallStack.h>
#include <private/binder/Static.h>
@@ -67,11 +68,6 @@
bool checkPermission(const String16& permission, pid_t pid, uid_t uid)
{
-#ifdef __BRILLO__
- // Brillo doesn't currently run ActivityManager or support framework permissions.
- return true;
-#endif
-
sp<IPermissionController> pc;
gDefaultServiceManagerLock.lock();
pc = gPermissionController;
@@ -131,7 +127,7 @@
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
- BpServiceManager(const sp<IBinder>& impl)
+ explicit BpServiceManager(const sp<IBinder>& impl)
: BpInterface<IServiceManager>(impl)
{
}
@@ -141,7 +137,12 @@
unsigned n;
for (n = 0; n < 5; n++){
if (n > 0) {
- ALOGI("Waiting for service %s...", String8(name).string());
+ if (!strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder")) {
+ ALOGI("Waiting for vendor service %s...", String8(name).string());
+ CallStack stack(LOG_TAG);
+ } else {
+ ALOGI("Waiting for service %s...", String8(name).string());
+ }
sleep(1);
}
sp<IBinder> svc = checkService(name);
diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp
new file mode 100644
index 0000000..c793df3
--- /dev/null
+++ b/libs/binder/IShellCallback.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ShellCallback"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IShellCallback.h>
+
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpShellCallback : public BpInterface<IShellCallback>
+{
+public:
+ explicit BpShellCallback(const sp<IBinder>& impl)
+ : BpInterface<IShellCallback>(impl)
+ {
+ }
+
+ virtual int openOutputFile(const String16& path, const String16& seLinuxContext) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IShellCallback::getInterfaceDescriptor());
+ data.writeString16(path);
+ data.writeString16(seLinuxContext);
+ remote()->transact(OP_OPEN_OUTPUT_FILE, data, &reply, 0);
+ reply.readExceptionCode();
+ int fd = reply.readParcelFileDescriptor();
+ return fd >= 0 ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd;
+
+ }
+};
+
+IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback");
+
+// ----------------------------------------------------------------------
+
+status_t BnShellCallback::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case OP_OPEN_OUTPUT_FILE: {
+ CHECK_INTERFACE(IShellCallback, data, reply);
+ String16 path(data.readString16());
+ String16 seLinuxContext(data.readString16());
+ int fd = openOutputFile(path, seLinuxContext);
+ if (reply != NULL) {
+ reply->writeNoException();
+ if (fd >= 0) {
+ reply->writeInt32(1);
+ reply->writeParcelFileDescriptor(fd, true);
+ } else {
+ reply->writeInt32(0);
+ }
+ } else if (fd >= 0) {
+ close(fd);
+ }
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp
new file mode 100644
index 0000000..3a8a63c
--- /dev/null
+++ b/libs/binder/IpPrefix.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 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 "IpPrefix"
+
+#include <binder/IpPrefix.h>
+#include <vector>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+using android::BAD_TYPE;
+using android::BAD_VALUE;
+using android::NO_ERROR;
+using android::Parcel;
+using android::status_t;
+using android::UNEXPECTED_NULL;
+using namespace ::android::binder;
+
+namespace android {
+
+namespace net {
+
+#define RETURN_IF_FAILED(calledOnce) \
+ { \
+ status_t returnStatus = calledOnce; \
+ if (returnStatus) { \
+ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+ return returnStatus; \
+ } \
+ }
+
+status_t IpPrefix::writeToParcel(Parcel* parcel) const {
+ /*
+ * Keep implementation in sync with writeToParcel() in
+ * frameworks/base/core/java/android/net/IpPrefix.java.
+ */
+ std::vector<uint8_t> byte_vector;
+
+ if (mIsIpv6) {
+ const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr);
+ byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
+ } else {
+ const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr);
+ byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
+ }
+
+ RETURN_IF_FAILED(parcel->writeByteVector(byte_vector));
+ RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength)));
+
+ return NO_ERROR;
+}
+
+status_t IpPrefix::readFromParcel(const Parcel* parcel) {
+ /*
+ * Keep implementation in sync with readFromParcel() in
+ * frameworks/base/core/java/android/net/IpPrefix.java.
+ */
+ std::vector<uint8_t> byte_vector;
+
+ RETURN_IF_FAILED(parcel->readByteVector(&byte_vector));
+ RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength));
+
+ if (byte_vector.size() == 16) {
+ mIsIpv6 = true;
+ memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr));
+
+ } else if (byte_vector.size() == 4) {
+ mIsIpv6 = false;
+ memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr));
+
+ } else {
+ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const
+{
+ return mUnion.mIn6Addr;
+}
+
+const struct in_addr& IpPrefix::getAddressAsInAddr() const
+{
+ return mUnion.mInAddr;
+}
+
+bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const
+{
+ if (isIpv6()) {
+ *addr = mUnion.mIn6Addr;
+ return true;
+ }
+ return false;
+}
+
+bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const
+{
+ if (isIpv4()) {
+ *addr = mUnion.mInAddr;
+ return true;
+ }
+ return false;
+}
+
+bool IpPrefix::isIpv6() const
+{
+ return mIsIpv6;
+}
+
+bool IpPrefix::isIpv4() const
+{
+ return !mIsIpv6;
+}
+
+int32_t IpPrefix::getPrefixLength() const
+{
+ return mPrefixLength;
+}
+
+void IpPrefix::setAddress(const struct in6_addr& addr)
+{
+ mUnion.mIn6Addr = addr;
+ mIsIpv6 = true;
+}
+
+void IpPrefix::setAddress(const struct in_addr& addr)
+{
+ mUnion.mInAddr = addr;
+ mIsIpv6 = false;
+}
+
+void IpPrefix::setPrefixLength(int32_t prefix)
+{
+ mPrefixLength = prefix;
+}
+
+bool operator==(const IpPrefix& lhs, const IpPrefix& rhs)
+{
+ if (lhs.mIsIpv6 != rhs.mIsIpv6) {
+ return false;
+ }
+
+ if (lhs.mPrefixLength != rhs.mPrefixLength) {
+ return false;
+ }
+
+ if (lhs.mIsIpv6) {
+ return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr));
+ }
+
+ return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr));
+}
+
+} // namespace net
+
+} // namespace android
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index 51eac11..2a15773 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -126,7 +126,7 @@
PAGE_ALIGNED = 0x00000001
};
public:
- SimpleBestFitAllocator(size_t size);
+ explicit SimpleBestFitAllocator(size_t size);
~SimpleBestFitAllocator();
size_t allocate(size_t size, uint32_t flags = 0);
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 43a01e4..03f00be 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -16,20 +16,19 @@
#define LOG_TAG "MemoryHeapBase"
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <sys/ioctl.h>
-
-#include <cutils/log.h>
-#include <cutils/ashmem.h>
-#include <cutils/atomic.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <binder/MemoryHeapBase.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+#include <log/log.h>
namespace android {
@@ -83,7 +82,7 @@
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- mapfd(dup(fd), size, offset);
+ mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
}
status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 19ce3eb..aec8f10 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -37,6 +37,7 @@
#include <binder/ProcessState.h>
#include <binder/Status.h>
#include <binder/TextOutput.h>
+#include <binder/Value.h>
#include <cutils/ashmem.h>
#include <utils/Debug.h>
@@ -100,32 +101,6 @@
BLOB_ASHMEM_MUTABLE = 2,
};
-static dev_t ashmem_rdev()
-{
- static dev_t __ashmem_rdev;
- static pthread_mutex_t __ashmem_rdev_lock = PTHREAD_MUTEX_INITIALIZER;
-
- pthread_mutex_lock(&__ashmem_rdev_lock);
-
- dev_t rdev = __ashmem_rdev;
- if (!rdev) {
- int fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDONLY));
- if (fd >= 0) {
- struct stat st;
-
- int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
- close(fd);
- if ((ret >= 0) && S_ISCHR(st.st_mode)) {
- rdev = __ashmem_rdev = st.st_rdev;
- }
- }
- }
-
- pthread_mutex_unlock(&__ashmem_rdev_lock);
-
- return rdev;
-}
-
void acquire_object(const sp<ProcessState>& proc,
const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
{
@@ -154,15 +129,11 @@
return;
}
case BINDER_TYPE_FD: {
- if ((obj.cookie != 0) && (outAshmemSize != NULL)) {
- struct stat st;
- int ret = fstat(obj.handle, &st);
- if (!ret && S_ISCHR(st.st_mode) && (st.st_rdev == ashmem_rdev())) {
- // If we own an ashmem fd, keep track of how much memory it refers to.
- int size = ashmem_get_size_region(obj.handle);
- if (size > 0) {
- *outAshmemSize += size;
- }
+ if ((obj.cookie != 0) && (outAshmemSize != NULL) && ashmem_valid(obj.handle)) {
+ // If we own an ashmem fd, keep track of how much memory it refers to.
+ int size = ashmem_get_size_region(obj.handle);
+ if (size > 0) {
+ *outAshmemSize += size;
}
}
return;
@@ -207,14 +178,10 @@
}
case BINDER_TYPE_FD: {
if (obj.cookie != 0) { // owned
- if (outAshmemSize != NULL) {
- struct stat st;
- int ret = fstat(obj.handle, &st);
- if (!ret && S_ISCHR(st.st_mode) && (st.st_rdev == ashmem_rdev())) {
- int size = ashmem_get_size_region(obj.handle);
- if (size > 0) {
- *outAshmemSize -= size;
- }
+ if ((outAshmemSize != NULL) && ashmem_valid(obj.handle)) {
+ int size = ashmem_get_size_region(obj.handle);
+ if (size > 0) {
+ *outAshmemSize -= size;
}
}
@@ -244,7 +211,14 @@
{
flat_binder_object obj;
- obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
+ /* minimum priority for all nodes is nice 0 */
+ obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ } else {
+ /* minimum priority for all nodes is MAX_NICE(19) */
+ obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ }
+
if (binder != NULL) {
IBinder *local = binder->localBinder();
if (!local) {
@@ -573,7 +547,7 @@
// If this is a file descriptor, we need to dup it so the
// new Parcel now owns its own fd, and can declare that we
// officially know we have fds.
- flat->handle = dup(flat->handle);
+ flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
flat->cookie = 1;
mHasFds = mFdsKnown = true;
if (!mAllowFds) {
@@ -586,6 +560,14 @@
return err;
}
+int Parcel::compareData(const Parcel& other) {
+ size_t size = dataSize();
+ if (size != other.dataSize()) {
+ return size < other.dataSize() ? -1 : 1;
+ }
+ return memcmp(data(), other.data(), size);
+}
+
bool Parcel::allowFds() const
{
return mAllowFds;
@@ -784,7 +766,7 @@
const uint8_t* strData = (uint8_t*)str.data();
const size_t strLen= str.length();
const ssize_t utf16Len = utf8_to_utf16_length(strData, strLen);
- if (utf16Len < 0 || utf16Len> std::numeric_limits<int32_t>::max()) {
+ if (utf16Len < 0 || utf16Len > std::numeric_limits<int32_t>::max()) {
return BAD_VALUE;
}
@@ -799,7 +781,7 @@
return NO_MEMORY;
}
- utf8_to_utf16(strData, strLen, (char16_t*)dst);
+ utf8_to_utf16(strData, strLen, (char16_t*)dst, (size_t) utf16Len + 1);
return NO_ERROR;
}
@@ -1112,7 +1094,7 @@
}
status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const {
- return readNullableTypedVector(val, &Parcel::readStrongBinder);
+ return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
}
status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const {
@@ -1140,6 +1122,10 @@
return parcelable.writeToParcel(this);
}
+status_t Parcel::writeValue(const binder::Value& value) {
+ return value.writeToParcel(this);
+}
+
status_t Parcel::writeNativeHandle(const native_handle* handle)
{
if (!handle || handle->version != sizeof(native_handle))
@@ -1176,7 +1162,7 @@
status_t Parcel::writeDupFileDescriptor(int fd)
{
- int dupFd = dup(fd);
+ int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (dupFd < 0) {
return -errno;
}
@@ -1187,15 +1173,21 @@
return err;
}
-status_t Parcel::writeUniqueFileDescriptor(const ScopedFd& fd) {
+status_t Parcel::writeParcelFileDescriptor(int fd, bool takeOwnership)
+{
+ writeInt32(0);
+ return writeFileDescriptor(fd, takeOwnership);
+}
+
+status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) {
return writeDupFileDescriptor(fd.get());
}
-status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<ScopedFd>& val) {
+status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<base::unique_fd>& val) {
return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor);
}
-status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<ScopedFd>>& val) {
+status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) {
return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
}
@@ -1358,6 +1350,120 @@
return status.writeToParcel(this);
}
+status_t Parcel::writeMap(const ::android::binder::Map& map_in)
+{
+ using ::std::map;
+ using ::android::binder::Value;
+ using ::android::binder::Map;
+
+ Map::const_iterator iter;
+ status_t ret;
+
+ ret = writeInt32(map_in.size());
+
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ for (iter = map_in.begin(); iter != map_in.end(); ++iter) {
+ ret = writeValue(Value(iter->first));
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ ret = writeValue(iter->second);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+status_t Parcel::writeNullableMap(const std::unique_ptr<binder::Map>& map)
+{
+ if (map == NULL) {
+ return writeInt32(-1);
+ }
+
+ return writeMap(*map.get());
+}
+
+status_t Parcel::readMap(::android::binder::Map* map_out)const
+{
+ using ::std::map;
+ using ::android::String16;
+ using ::android::String8;
+ using ::android::binder::Value;
+ using ::android::binder::Map;
+
+ status_t ret = NO_ERROR;
+ int32_t count;
+
+ ret = readInt32(&count);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ if (count < 0) {
+ ALOGE("readMap: Unexpected count: %d", count);
+ return (count == -1)
+ ? UNEXPECTED_NULL
+ : BAD_VALUE;
+ }
+
+ map_out->clear();
+
+ while (count--) {
+ Map::key_type key;
+ Value value;
+
+ ret = readValue(&value);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ if (!value.getString(&key)) {
+ ALOGE("readMap: Key type not a string (parcelType = %d)", value.parcelType());
+ return BAD_VALUE;
+ }
+
+ ret = readValue(&value);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ (*map_out)[key] = value;
+ }
+
+ return ret;
+}
+
+status_t Parcel::readNullableMap(std::unique_ptr<binder::Map>* map) const
+{
+ const size_t start = dataPosition();
+ int32_t count;
+ status_t status = readInt32(&count);
+ map->reset();
+
+ if (status != OK || count == -1) {
+ return status;
+ }
+
+ setDataPosition(start);
+ map->reset(new binder::Map());
+
+ status = readMap(map->get());
+
+ if (status != OK) {
+ map->reset();
+ }
+
+ return status;
+}
+
+
+
void Parcel::remove(size_t /*start*/, size_t /*amt*/)
{
LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
@@ -1461,13 +1567,13 @@
return status;
}
- const void* data = parcel->readInplace(size);
+ T* data = const_cast<T*>(reinterpret_cast<const T*>(parcel->readInplace(size)));
if (!data) {
status = BAD_VALUE;
return status;
}
- val->resize(size);
- memcpy(val->data(), data, size);
+ val->reserve(size);
+ val->insert(val->end(), data, data + size);
return status;
}
@@ -1842,13 +1948,37 @@
String8 Parcel::readString8() const
{
- int32_t size = readInt32();
- // watch for potential int overflow adding 1 for trailing NUL
- if (size > 0 && size < INT32_MAX) {
- const char* str = (const char*)readInplace(size+1);
- if (str) return String8(str, size);
+ String8 retString;
+ status_t status = readString8(&retString);
+ if (status != OK) {
+ // We don't care about errors here, so just return an empty string.
+ return String8();
}
- return String8();
+ return retString;
+}
+
+status_t Parcel::readString8(String8* pArg) const
+{
+ int32_t size;
+ status_t status = readInt32(&size);
+ if (status != OK) {
+ return status;
+ }
+ // watch for potential int overflow from size+1
+ if (size < 0 || size >= INT32_MAX) {
+ return BAD_VALUE;
+ }
+ // |writeString8| writes nothing for empty string.
+ if (size == 0) {
+ *pArg = String8();
+ return OK;
+ }
+ const char* str = (const char*)readInplace(size + 1);
+ if (str == NULL) {
+ return BAD_VALUE;
+ }
+ pArg->setTo(str, size);
+ return OK;
}
String16 Parcel::readString16() const
@@ -1913,13 +2043,25 @@
status_t Parcel::readStrongBinder(sp<IBinder>* val) const
{
+ status_t status = readNullableStrongBinder(val);
+ if (status == OK && !val->get()) {
+ status = UNEXPECTED_NULL;
+ }
+ return status;
+}
+
+status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
+{
return unflatten_binder(ProcessState::self(), *this, val);
}
sp<IBinder> Parcel::readStrongBinder() const
{
sp<IBinder> val;
- readStrongBinder(&val);
+ // Note that a lot of code in Android reads binders by hand with this
+ // method, and that code has historically been ok with getting nullptr
+ // back (while ignoring error codes).
+ readNullableStrongBinder(&val);
return val;
}
@@ -1942,6 +2084,10 @@
return parcelable->readFromParcel(this);
}
+status_t Parcel::readValue(binder::Value* value) const {
+ return value->readFromParcel(this);
+}
+
int32_t Parcel::readExceptionCode() const
{
binder::Status status;
@@ -1964,7 +2110,7 @@
}
for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {
- h->data[i] = dup(readFileDescriptor());
+ h->data[i] = fcntl(readFileDescriptor(), F_DUPFD_CLOEXEC, 0);
if (h->data[i] < 0) {
for (int j = 0; j < i; j++) {
close(h->data[j]);
@@ -1982,7 +2128,6 @@
return h;
}
-
int Parcel::readFileDescriptor() const
{
const flat_binder_object* flat = readObject(true);
@@ -1994,7 +2139,18 @@
return BAD_TYPE;
}
-status_t Parcel::readUniqueFileDescriptor(ScopedFd* val) const
+int Parcel::readParcelFileDescriptor() const
+{
+ int32_t hasComm = readInt32();
+ int fd = readFileDescriptor();
+ if (hasComm != 0) {
+ // skip
+ readFileDescriptor();
+ }
+ return fd;
+}
+
+status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const
{
int got = readFileDescriptor();
@@ -2002,7 +2158,7 @@
return BAD_TYPE;
}
- val->reset(dup(got));
+ val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0));
if (val->get() < 0) {
return BAD_VALUE;
@@ -2012,11 +2168,11 @@
}
-status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<ScopedFd>>* val) const {
+status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const {
return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
}
-status_t Parcel::readUniqueFileDescriptorVector(std::vector<ScopedFd>* val) const {
+status_t Parcel::readUniqueFileDescriptorVector(std::vector<base::unique_fd>* val) const {
return readTypedVector(val, &Parcel::readUniqueFileDescriptor);
}
@@ -2076,11 +2232,15 @@
status_t err = NO_ERROR;
for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
- fds[i] = dup(this->readFileDescriptor());
- if (fds[i] < 0) {
+ int fd = this->readFileDescriptor();
+ if (fd < 0 || ((fds[i] = fcntl(fd, F_DUPFD_CLOEXEC, 0)) < 0)) {
err = BAD_VALUE;
- ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
- i, fds[i], fd_count, strerror(errno));
+ ALOGE("fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
+ i, fds[i], fd_count, strerror(fd < 0 ? -fd : errno));
+ // Close all the file descriptors that were dup-ed.
+ for (size_t j=0; j<i ;j++) {
+ close(fds[j]);
+ }
}
}
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index aef791c..d617b5a 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "PersistableBundle"
#include <binder/PersistableBundle.h>
+#include <private/binder/ParcelValTypes.h>
#include <limits>
@@ -32,35 +33,34 @@
using android::sp;
using android::status_t;
using android::UNEXPECTED_NULL;
+using std::map;
+using std::set;
+using std::vector;
+using namespace ::android::binder;
enum {
// Keep in sync with BUNDLE_MAGIC in frameworks/base/core/java/android/os/BaseBundle.java.
BUNDLE_MAGIC = 0x4C444E42,
};
-enum {
- // Keep in sync with frameworks/base/core/java/android/os/Parcel.java.
- VAL_STRING = 0,
- VAL_INTEGER = 1,
- VAL_LONG = 6,
- VAL_DOUBLE = 8,
- VAL_BOOLEAN = 9,
- VAL_STRINGARRAY = 14,
- VAL_INTARRAY = 18,
- VAL_LONGARRAY = 19,
- VAL_BOOLEANARRAY = 23,
- VAL_PERSISTABLEBUNDLE = 25,
- VAL_DOUBLEARRAY = 28,
-};
-
namespace {
template <typename T>
-bool getValue(const android::String16& key, T* out, const std::map<android::String16, T>& map) {
+bool getValue(const android::String16& key, T* out, const map<android::String16, T>& map) {
const auto& it = map.find(key);
if (it == map.end()) return false;
*out = it->second;
return true;
}
+
+template <typename T>
+set<android::String16> getKeys(const map<android::String16, T>& map) {
+ if (map.empty()) return set<android::String16>();
+ set<android::String16> keys;
+ for (const auto& key_value_pair : map) {
+ keys.emplace(key_value_pair.first);
+ }
+ return keys;
+}
} // namespace
namespace android {
@@ -78,7 +78,7 @@
#define RETURN_IF_ENTRY_ERASED(map, key) \
{ \
- size_t num_erased = map.erase(key); \
+ size_t num_erased = (map).erase(key); \
if (num_erased) { \
ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
return num_erased; \
@@ -188,27 +188,27 @@
mStringMap[key] = value;
}
-void PersistableBundle::putBooleanVector(const String16& key, const std::vector<bool>& value) {
+void PersistableBundle::putBooleanVector(const String16& key, const vector<bool>& value) {
erase(key);
mBoolVectorMap[key] = value;
}
-void PersistableBundle::putIntVector(const String16& key, const std::vector<int32_t>& value) {
+void PersistableBundle::putIntVector(const String16& key, const vector<int32_t>& value) {
erase(key);
mIntVectorMap[key] = value;
}
-void PersistableBundle::putLongVector(const String16& key, const std::vector<int64_t>& value) {
+void PersistableBundle::putLongVector(const String16& key, const vector<int64_t>& value) {
erase(key);
mLongVectorMap[key] = value;
}
-void PersistableBundle::putDoubleVector(const String16& key, const std::vector<double>& value) {
+void PersistableBundle::putDoubleVector(const String16& key, const vector<double>& value) {
erase(key);
mDoubleVectorMap[key] = value;
}
-void PersistableBundle::putStringVector(const String16& key, const std::vector<String16>& value) {
+void PersistableBundle::putStringVector(const String16& key, const vector<String16>& value) {
erase(key);
mStringVectorMap[key] = value;
}
@@ -238,23 +238,23 @@
return getValue(key, out, mStringMap);
}
-bool PersistableBundle::getBooleanVector(const String16& key, std::vector<bool>* out) const {
+bool PersistableBundle::getBooleanVector(const String16& key, vector<bool>* out) const {
return getValue(key, out, mBoolVectorMap);
}
-bool PersistableBundle::getIntVector(const String16& key, std::vector<int32_t>* out) const {
+bool PersistableBundle::getIntVector(const String16& key, vector<int32_t>* out) const {
return getValue(key, out, mIntVectorMap);
}
-bool PersistableBundle::getLongVector(const String16& key, std::vector<int64_t>* out) const {
+bool PersistableBundle::getLongVector(const String16& key, vector<int64_t>* out) const {
return getValue(key, out, mLongVectorMap);
}
-bool PersistableBundle::getDoubleVector(const String16& key, std::vector<double>* out) const {
+bool PersistableBundle::getDoubleVector(const String16& key, vector<double>* out) const {
return getValue(key, out, mDoubleVectorMap);
}
-bool PersistableBundle::getStringVector(const String16& key, std::vector<String16>* out) const {
+bool PersistableBundle::getStringVector(const String16& key, vector<String16>* out) const {
return getValue(key, out, mStringVectorMap);
}
@@ -262,6 +262,50 @@
return getValue(key, out, mPersistableBundleMap);
}
+set<String16> PersistableBundle::getBooleanKeys() const {
+ return getKeys(mBoolMap);
+}
+
+set<String16> PersistableBundle::getIntKeys() const {
+ return getKeys(mIntMap);
+}
+
+set<String16> PersistableBundle::getLongKeys() const {
+ return getKeys(mLongMap);
+}
+
+set<String16> PersistableBundle::getDoubleKeys() const {
+ return getKeys(mDoubleMap);
+}
+
+set<String16> PersistableBundle::getStringKeys() const {
+ return getKeys(mStringMap);
+}
+
+set<String16> PersistableBundle::getBooleanVectorKeys() const {
+ return getKeys(mBoolVectorMap);
+}
+
+set<String16> PersistableBundle::getIntVectorKeys() const {
+ return getKeys(mIntVectorMap);
+}
+
+set<String16> PersistableBundle::getLongVectorKeys() const {
+ return getKeys(mLongVectorMap);
+}
+
+set<String16> PersistableBundle::getDoubleVectorKeys() const {
+ return getKeys(mDoubleVectorMap);
+}
+
+set<String16> PersistableBundle::getStringVectorKeys() const {
+ return getKeys(mStringVectorMap);
+}
+
+set<String16> PersistableBundle::getPersistableBundleKeys() const {
+ return getKeys(mPersistableBundleMap);
+}
+
status_t PersistableBundle::writeToParcelInner(Parcel* parcel) const {
/*
* To keep this implementation in sync with writeArrayMapInternal() in
@@ -363,7 +407,6 @@
RETURN_IF_FAILED(parcel->readInt32(&num_entries));
for (; num_entries > 0; --num_entries) {
- size_t start_pos = parcel->dataPosition();
String16 key;
int32_t value_type;
RETURN_IF_FAILED(parcel->readString16(&key));
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index fb28643..8939d9c 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -57,6 +57,40 @@
return TIMED_OUT;
}
+status_t ProcessInfoService::getProcessStatesScoresImpl(size_t length,
+ /*in*/ int32_t* pids, /*out*/ int32_t* states,
+ /*out*/ int32_t *scores) {
+ status_t err = NO_ERROR;
+ sp<IProcessInfoService> pis;
+ mProcessInfoLock.lock();
+ pis = mProcessInfoService;
+ mProcessInfoLock.unlock();
+
+ for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) {
+
+ if (pis != NULL) {
+ err = pis->getProcessStatesAndOomScoresFromPids(length,
+ /*in*/ pids, /*out*/ states, /*out*/ scores);
+ if (err == NO_ERROR) return NO_ERROR; // success
+ if (IInterface::asBinder(pis)->isBinderAlive()) return err;
+ }
+ sleep(1);
+
+ mProcessInfoLock.lock();
+ if (pis == mProcessInfoService) {
+ updateBinderLocked();
+ }
+ pis = mProcessInfoService;
+ mProcessInfoLock.unlock();
+ }
+
+ ALOGW("%s: Could not retrieve process states and scores "
+ "from ProcessInfoService after %d retries.", __FUNCTION__,
+ BINDER_ATTEMPT_LIMIT);
+
+ return TIMED_OUT;
+}
+
void ProcessInfoService::updateBinderLocked() {
const sp<IServiceManager> sm(defaultServiceManager());
if (sm != NULL) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index f13f49f..add5e74 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -16,8 +16,6 @@
#define LOG_TAG "ProcessState"
-#include <cutils/process_name.h>
-
#include <binder/ProcessState.h>
#include <utils/Atomic.h>
@@ -42,7 +40,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
+#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 15
// -------------------------------------------------------------------------
@@ -52,7 +50,7 @@
class PoolThread : public Thread
{
public:
- PoolThread(bool isMain)
+ explicit PoolThread(bool isMain)
: mIsMain(isMain)
{
}
@@ -73,7 +71,22 @@
if (gProcess != NULL) {
return gProcess;
}
- gProcess = new ProcessState;
+ gProcess = new ProcessState("/dev/binder");
+ return gProcess;
+}
+
+sp<ProcessState> ProcessState::initWithDriver(const char* driver)
+{
+ Mutex::Autolock _l(gProcessMutex);
+ if (gProcess != NULL) {
+ // Allow for initWithDriver to be called repeatedly with the same
+ // driver.
+ if (!strcmp(gProcess->getDriverName().c_str(), driver)) {
+ return gProcess;
+ }
+ LOG_ALWAYS_FATAL("ProcessState was already initialized.");
+ }
+ gProcess = new ProcessState(driver);
return gProcess;
}
@@ -309,9 +322,13 @@
androidSetThreadName( makeBinderThreadName().string() );
}
-static int open_driver()
+String8 ProcessState::getDriverName() {
+ return mDriverName;
+}
+
+static int open_driver(const char *driver)
{
- int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
+ int fd = open(driver, O_RDWR | O_CLOEXEC);
if (fd >= 0) {
int vers = 0;
status_t result = ioctl(fd, BINDER_VERSION, &vers);
@@ -321,7 +338,8 @@
fd = -1;
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
- ALOGE("Binder driver protocol does not match user space protocol!");
+ ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
+ vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
close(fd);
fd = -1;
}
@@ -331,13 +349,14 @@
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
} else {
- ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
+ ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
}
return fd;
}
-ProcessState::ProcessState()
- : mDriverFD(open_driver())
+ProcessState::ProcessState(const char *driver)
+ : mDriverName(String8(driver))
+ , mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
@@ -358,6 +377,7 @@
ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
+ mDriverName.clear();
}
}
@@ -366,6 +386,13 @@
ProcessState::~ProcessState()
{
+ if (mDriverFD >= 0) {
+ if (mVMStart != MAP_FAILED) {
+ munmap(mVMStart, BINDER_VM_SIZE);
+ }
+ close(mDriverFD);
+ }
+ mDriverFD = -1;
}
}; // namespace android
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index cd9509f..f0613d1 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -48,7 +48,7 @@
class FdTextOutput : public BufferedTextOutput
{
public:
- FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { }
+ explicit FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { }
virtual ~FdTextOutput() { };
protected:
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index d3520d6..006f7f9 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -32,6 +32,11 @@
return Status(exceptionCode, OK, message);
}
+Status Status::fromExceptionCode(int32_t exceptionCode,
+ const char* message) {
+ return fromExceptionCode(exceptionCode, String8(message));
+}
+
Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode) {
return Status(EX_SERVICE_SPECIFIC, serviceSpecificErrorCode);
}
@@ -41,6 +46,11 @@
return Status(EX_SERVICE_SPECIFIC, serviceSpecificErrorCode, message);
}
+Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode,
+ const char* message) {
+ return fromServiceSpecificError(serviceSpecificErrorCode, String8(message));
+}
+
Status Status::fromStatusT(status_t status) {
Status ret;
ret.setFromStatusT(status);
@@ -94,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);
@@ -117,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;
}
@@ -158,5 +179,10 @@
return ret;
}
+std::stringstream& operator<< (std::stringstream& stream, const Status& s) {
+ stream << s.toString8().string();
+ return stream;
+}
+
} // namespace binder
} // namespace android
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index 2ed5188..101eba3 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -29,111 +29,14 @@
// ---------------------------------------------------------------------------
-TextOutput::TextOutput() {
+TextOutput::TextOutput() {
}
-TextOutput::~TextOutput() {
+TextOutput::~TextOutput() {
}
// ---------------------------------------------------------------------------
-TextOutput& operator<<(TextOutput& to, bool val)
-{
- if (val) to.print("true", 4);
- else to.print("false", 5);
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, int val)
-{
- char buf[16];
- sprintf(buf, "%d", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, long val)
-{
- char buf[16];
- sprintf(buf, "%ld", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned int val)
-{
- char buf[16];
- sprintf(buf, "%u", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned long val)
-{
- char buf[16];
- sprintf(buf, "%lu", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, long long val)
-{
- char buf[32];
- sprintf(buf, "%Ld", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned long long val)
-{
- char buf[32];
- sprintf(buf, "%Lu", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-static TextOutput& print_float(TextOutput& to, double value)
-{
- char buf[64];
- sprintf(buf, "%g", value);
- if( !strchr(buf, '.') && !strchr(buf, 'e') &&
- !strchr(buf, 'E') ) {
- strncat(buf, ".0", sizeof(buf)-1);
- }
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, float val)
-{
- return print_float(to,val);
-}
-
-TextOutput& operator<<(TextOutput& to, double val)
-{
- return print_float(to,val);
-}
-
-TextOutput& operator<<(TextOutput& to, const void* val)
-{
- char buf[32];
- snprintf(buf, sizeof(buf), "%p", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, const String8& val)
-{
- to << val.string();
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, const String16& val)
-{
- to << String8(val).string();
- return to;
-}
-
static void textOutputPrinter(void* cookie, const char* txt)
{
((TextOutput*)cookie)->print(txt, strlen(txt));
diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp
new file mode 100644
index 0000000..fd1dfd5
--- /dev/null
+++ b/libs/binder/Value.cpp
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2015 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 "Value"
+
+#include <binder/Value.h>
+
+#include <limits>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <binder/Map.h>
+#include <private/binder/ParcelValTypes.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+using android::BAD_TYPE;
+using android::BAD_VALUE;
+using android::NO_ERROR;
+using android::UNEXPECTED_NULL;
+using android::Parcel;
+using android::sp;
+using android::status_t;
+using std::map;
+using std::set;
+using std::vector;
+using android::binder::Value;
+using android::IBinder;
+using android::os::PersistableBundle;
+using namespace android::binder;
+
+// ====================================================================
+
+#define RETURN_IF_FAILED(calledOnce) \
+ do { \
+ status_t returnStatus = calledOnce; \
+ if (returnStatus) { \
+ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+ return returnStatus; \
+ } \
+ } while(false)
+
+// ====================================================================
+
+/* These `internal_type_ptr()` functions allow this
+ * class to work without C++ RTTI support. This technique
+ * only works properly when called directly from this file,
+ * but that is OK because that is the only place we will
+ * be calling them from. */
+template<class T> const void* internal_type_ptr()
+{
+ static const T *marker;
+ return (void*)▮
+}
+
+/* Allows the type to be specified by the argument
+ * instead of inside angle brackets. */
+template<class T> const void* internal_type_ptr(const T&)
+{
+ return internal_type_ptr<T>();
+}
+
+// ====================================================================
+
+namespace android {
+
+namespace binder {
+
+class Value::ContentBase {
+public:
+ virtual ~ContentBase() = default;
+ virtual const void* type_ptr() const = 0;
+ virtual ContentBase * clone() const = 0;
+ virtual bool operator==(const ContentBase& rhs) const = 0;
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+ virtual const std::type_info &type() const = 0;
+#endif
+
+ template<typename T> bool get(T* out) const;
+};
+
+/* This is the actual class that holds the value. */
+template<typename T> class Value::Content : public Value::ContentBase {
+public:
+ Content() = default;
+ Content(const T & value) : mValue(value) { }
+
+ virtual ~Content() = default;
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+ virtual const std::type_info &type() const override
+ {
+ return typeid(T);
+ }
+#endif
+
+ virtual const void* type_ptr() const override
+ {
+ return internal_type_ptr<T>();
+ }
+
+ virtual ContentBase * clone() const override
+ {
+ return new Content(mValue);
+ };
+
+ virtual bool operator==(const ContentBase& rhs) const override
+ {
+ if (type_ptr() != rhs.type_ptr()) {
+ return false;
+ }
+ return mValue == static_cast<const Content<T>* >(&rhs)->mValue;
+ }
+
+ T mValue;
+};
+
+template<typename T> bool Value::ContentBase::get(T* out) const
+{
+ if (internal_type_ptr(*out) != type_ptr())
+ {
+ return false;
+ }
+
+ *out = static_cast<const Content<T>*>(this)->mValue;
+
+ return true;
+}
+
+// ====================================================================
+
+Value::Value() : mContent(NULL)
+{
+}
+
+Value::Value(const Value& value)
+ : mContent(value.mContent ? value.mContent->clone() : NULL)
+{
+}
+
+Value::~Value()
+{
+ delete mContent;
+}
+
+bool Value::operator==(const Value& rhs) const
+{
+ const Value& lhs(*this);
+
+ if (lhs.empty() && rhs.empty()) {
+ return true;
+ }
+
+ if ( (lhs.mContent == NULL)
+ || (rhs.mContent == NULL)
+ ) {
+ return false;
+ }
+
+ return *lhs.mContent == *rhs.mContent;
+}
+
+Value& Value::swap(Value &rhs)
+{
+ std::swap(mContent, rhs.mContent);
+ return *this;
+}
+
+Value& Value::operator=(const Value& rhs)
+{
+ delete mContent;
+ mContent = rhs.mContent
+ ? rhs.mContent->clone()
+ : NULL;
+ return *this;
+}
+
+bool Value::empty() const
+{
+ return mContent == NULL;
+}
+
+void Value::clear()
+{
+ delete mContent;
+ mContent = NULL;
+}
+
+int32_t Value::parcelType() const
+{
+ const void* t_info(mContent ? mContent->type_ptr() : NULL);
+
+ if (t_info == internal_type_ptr<bool>()) return VAL_BOOLEAN;
+ if (t_info == internal_type_ptr<uint8_t>()) return VAL_BYTE;
+ if (t_info == internal_type_ptr<int32_t>()) return VAL_INTEGER;
+ if (t_info == internal_type_ptr<int64_t>()) return VAL_LONG;
+ if (t_info == internal_type_ptr<double>()) return VAL_DOUBLE;
+ if (t_info == internal_type_ptr<String16>()) return VAL_STRING;
+
+ if (t_info == internal_type_ptr<vector<bool>>()) return VAL_BOOLEANARRAY;
+ if (t_info == internal_type_ptr<vector<uint8_t>>()) return VAL_BYTEARRAY;
+ if (t_info == internal_type_ptr<vector<int32_t>>()) return VAL_INTARRAY;
+ if (t_info == internal_type_ptr<vector<int64_t>>()) return VAL_LONGARRAY;
+ if (t_info == internal_type_ptr<vector<double>>()) return VAL_DOUBLEARRAY;
+ if (t_info == internal_type_ptr<vector<String16>>()) return VAL_STRINGARRAY;
+
+ if (t_info == internal_type_ptr<Map>()) return VAL_MAP;
+ if (t_info == internal_type_ptr<PersistableBundle>()) return VAL_PERSISTABLEBUNDLE;
+
+ return VAL_NULL;
+}
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+const std::type_info& Value::type() const
+{
+ return mContent != NULL
+ ? mContent->type()
+ : typeid(void);
+}
+#endif // ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+
+#define DEF_TYPE_ACCESSORS(T, TYPENAME) \
+ bool Value::is ## TYPENAME() const \
+ { \
+ return mContent \
+ ? internal_type_ptr<T>() == mContent->type_ptr() \
+ : false; \
+ } \
+ bool Value::get ## TYPENAME(T* out) const \
+ { \
+ return mContent \
+ ? mContent->get(out) \
+ : false; \
+ } \
+ void Value::put ## TYPENAME(const T& in) \
+ { \
+ *this = in; \
+ } \
+ Value& Value::operator=(const T& rhs) \
+ { \
+ delete mContent; \
+ mContent = new Content< T >(rhs); \
+ return *this; \
+ } \
+ Value::Value(const T& value) \
+ : mContent(new Content< T >(value)) \
+ { }
+
+DEF_TYPE_ACCESSORS(bool, Boolean)
+DEF_TYPE_ACCESSORS(int8_t, Byte)
+DEF_TYPE_ACCESSORS(int32_t, Int)
+DEF_TYPE_ACCESSORS(int64_t, Long)
+DEF_TYPE_ACCESSORS(double, Double)
+DEF_TYPE_ACCESSORS(String16, String)
+
+DEF_TYPE_ACCESSORS(std::vector<bool>, BooleanVector)
+DEF_TYPE_ACCESSORS(std::vector<uint8_t>, ByteVector)
+DEF_TYPE_ACCESSORS(std::vector<int32_t>, IntVector)
+DEF_TYPE_ACCESSORS(std::vector<int64_t>, LongVector)
+DEF_TYPE_ACCESSORS(std::vector<double>, DoubleVector)
+DEF_TYPE_ACCESSORS(std::vector<String16>, StringVector)
+
+DEF_TYPE_ACCESSORS(::android::binder::Map, Map)
+DEF_TYPE_ACCESSORS(PersistableBundle, PersistableBundle)
+
+bool Value::getString(String8* out) const
+{
+ String16 val;
+ bool ret = getString(&val);
+ if (ret) {
+ *out = String8(val);
+ }
+ return ret;
+}
+
+bool Value::getString(::std::string* out) const
+{
+ String8 val;
+ bool ret = getString(&val);
+ if (ret) {
+ *out = val.string();
+ }
+ return ret;
+}
+
+status_t Value::writeToParcel(Parcel* parcel) const
+{
+ // This implementation needs to be kept in sync with the writeValue
+ // implementation in frameworks/base/core/java/android/os/Parcel.java
+
+#define BEGIN_HANDLE_WRITE() \
+ do { \
+ const void* t_info(mContent?mContent->type_ptr():NULL); \
+ if (false) { }
+#define HANDLE_WRITE_TYPE(T, TYPEVAL, TYPEMETHOD) \
+ else if (t_info == internal_type_ptr<T>()) { \
+ RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \
+ RETURN_IF_FAILED(parcel->TYPEMETHOD(static_cast<const Content<T>*>(mContent)->mValue)); \
+ }
+#define HANDLE_WRITE_PARCELABLE(T, TYPEVAL) \
+ else if (t_info == internal_type_ptr<T>()) { \
+ RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \
+ RETURN_IF_FAILED(static_cast<const Content<T>*>(mContent)->mValue.writeToParcel(parcel)); \
+ }
+#define END_HANDLE_WRITE() \
+ else { \
+ ALOGE("writeToParcel: Type not supported"); \
+ return BAD_TYPE; \
+ } \
+ } while (false);
+
+ BEGIN_HANDLE_WRITE()
+
+ HANDLE_WRITE_TYPE(bool, VAL_BOOLEAN, writeBool)
+ HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte)
+ HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte)
+ HANDLE_WRITE_TYPE(int32_t, VAL_INTEGER, writeInt32)
+ HANDLE_WRITE_TYPE(int64_t, VAL_LONG, writeInt64)
+ HANDLE_WRITE_TYPE(double, VAL_DOUBLE, writeDouble)
+ HANDLE_WRITE_TYPE(String16, VAL_STRING, writeString16)
+
+ HANDLE_WRITE_TYPE(vector<bool>, VAL_BOOLEANARRAY, writeBoolVector)
+ HANDLE_WRITE_TYPE(vector<uint8_t>, VAL_BYTEARRAY, writeByteVector)
+ HANDLE_WRITE_TYPE(vector<int8_t>, VAL_BYTEARRAY, writeByteVector)
+ HANDLE_WRITE_TYPE(vector<int32_t>, VAL_INTARRAY, writeInt32Vector)
+ HANDLE_WRITE_TYPE(vector<int64_t>, VAL_LONGARRAY, writeInt64Vector)
+ HANDLE_WRITE_TYPE(vector<double>, VAL_DOUBLEARRAY, writeDoubleVector)
+ HANDLE_WRITE_TYPE(vector<String16>, VAL_STRINGARRAY, writeString16Vector)
+
+ HANDLE_WRITE_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE)
+
+ END_HANDLE_WRITE()
+
+ return NO_ERROR;
+
+#undef BEGIN_HANDLE_WRITE
+#undef HANDLE_WRITE_TYPE
+#undef HANDLE_WRITE_PARCELABLE
+#undef END_HANDLE_WRITE
+}
+
+status_t Value::readFromParcel(const Parcel* parcel)
+{
+ // This implementation needs to be kept in sync with the readValue
+ // implementation in frameworks/base/core/java/android/os/Parcel.javai
+
+#define BEGIN_HANDLE_READ() \
+ switch(value_type) { \
+ default: \
+ ALOGE("readFromParcel: Parcel type %d is not supported", value_type); \
+ return BAD_TYPE;
+#define HANDLE_READ_TYPE(T, TYPEVAL, TYPEMETHOD) \
+ case TYPEVAL: \
+ mContent = new Content<T>(); \
+ RETURN_IF_FAILED(parcel->TYPEMETHOD(&static_cast<Content<T>*>(mContent)->mValue)); \
+ break;
+#define HANDLE_READ_PARCELABLE(T, TYPEVAL) \
+ case TYPEVAL: \
+ mContent = new Content<T>(); \
+ RETURN_IF_FAILED(static_cast<Content<T>*>(mContent)->mValue.readFromParcel(parcel)); \
+ break;
+#define END_HANDLE_READ() \
+ }
+
+ int32_t value_type = VAL_NULL;
+
+ delete mContent;
+ mContent = NULL;
+
+ RETURN_IF_FAILED(parcel->readInt32(&value_type));
+
+ BEGIN_HANDLE_READ()
+
+ HANDLE_READ_TYPE(bool, VAL_BOOLEAN, readBool)
+ HANDLE_READ_TYPE(int8_t, VAL_BYTE, readByte)
+ HANDLE_READ_TYPE(int32_t, VAL_INTEGER, readInt32)
+ HANDLE_READ_TYPE(int64_t, VAL_LONG, readInt64)
+ HANDLE_READ_TYPE(double, VAL_DOUBLE, readDouble)
+ HANDLE_READ_TYPE(String16, VAL_STRING, readString16)
+
+ HANDLE_READ_TYPE(vector<bool>, VAL_BOOLEANARRAY, readBoolVector)
+ HANDLE_READ_TYPE(vector<uint8_t>, VAL_BYTEARRAY, readByteVector)
+ HANDLE_READ_TYPE(vector<int32_t>, VAL_INTARRAY, readInt32Vector)
+ HANDLE_READ_TYPE(vector<int64_t>, VAL_LONGARRAY, readInt64Vector)
+ HANDLE_READ_TYPE(vector<double>, VAL_DOUBLEARRAY, readDoubleVector)
+ HANDLE_READ_TYPE(vector<String16>, VAL_STRINGARRAY, readString16Vector)
+
+ HANDLE_READ_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE)
+
+ END_HANDLE_READ()
+
+ return NO_ERROR;
+
+#undef BEGIN_HANDLE_READ
+#undef HANDLE_READ_TYPE
+#undef HANDLE_READ_PARCELABLE
+#undef END_HANDLE_READ
+}
+
+} // namespace binder
+
+} // namespace android
+
+/* vim: set ts=4 sw=4 tw=0 et :*/
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
new file mode 100644
index 0000000..4212776
--- /dev/null
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_APP_OPS_MANAGER_H
+#define ANDROID_APP_OPS_MANAGER_H
+
+#include <binder/IAppOpsService.h>
+
+#include <utils/threads.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class AppOpsManager
+{
+public:
+ enum {
+ MODE_ALLOWED = IAppOpsService::MODE_ALLOWED,
+ MODE_IGNORED = IAppOpsService::MODE_IGNORED,
+ MODE_ERRORED = IAppOpsService::MODE_ERRORED
+ };
+
+ enum {
+ OP_NONE = -1,
+ OP_COARSE_LOCATION = 0,
+ OP_FINE_LOCATION = 1,
+ OP_GPS = 2,
+ OP_VIBRATE = 3,
+ OP_READ_CONTACTS = 4,
+ OP_WRITE_CONTACTS = 5,
+ OP_READ_CALL_LOG = 6,
+ OP_WRITE_CALL_LOG = 7,
+ OP_READ_CALENDAR = 8,
+ OP_WRITE_CALENDAR = 9,
+ OP_WIFI_SCAN = 10,
+ OP_POST_NOTIFICATION = 11,
+ OP_NEIGHBORING_CELLS = 12,
+ OP_CALL_PHONE = 13,
+ OP_READ_SMS = 14,
+ OP_WRITE_SMS = 15,
+ OP_RECEIVE_SMS = 16,
+ OP_RECEIVE_EMERGECY_SMS = 17,
+ OP_RECEIVE_MMS = 18,
+ OP_RECEIVE_WAP_PUSH = 19,
+ OP_SEND_SMS = 20,
+ OP_READ_ICC_SMS = 21,
+ OP_WRITE_ICC_SMS = 22,
+ OP_WRITE_SETTINGS = 23,
+ OP_SYSTEM_ALERT_WINDOW = 24,
+ OP_ACCESS_NOTIFICATIONS = 25,
+ OP_CAMERA = 26,
+ OP_RECORD_AUDIO = 27,
+ OP_PLAY_AUDIO = 28,
+ OP_READ_CLIPBOARD = 29,
+ OP_WRITE_CLIPBOARD = 30,
+ OP_TAKE_MEDIA_BUTTONS = 31,
+ OP_TAKE_AUDIO_FOCUS = 32,
+ OP_AUDIO_MASTER_VOLUME = 33,
+ OP_AUDIO_VOICE_VOLUME = 34,
+ OP_AUDIO_RING_VOLUME = 35,
+ OP_AUDIO_MEDIA_VOLUME = 36,
+ OP_AUDIO_ALARM_VOLUME = 37,
+ OP_AUDIO_NOTIFICATION_VOLUME = 38,
+ OP_AUDIO_BLUETOOTH_VOLUME = 39,
+ OP_WAKE_LOCK = 40,
+ OP_MONITOR_LOCATION = 41,
+ OP_MONITOR_HIGH_POWER_LOCATION = 42,
+ OP_GET_USAGE_STATS = 43,
+ OP_MUTE_MICROPHONE = 44,
+ OP_TOAST_WINDOW = 45,
+ OP_PROJECT_MEDIA = 46,
+ OP_ACTIVATE_VPN = 47,
+ OP_WRITE_WALLPAPER = 48,
+ OP_ASSIST_STRUCTURE = 49,
+ OP_ASSIST_SCREENSHOT = 50,
+ OP_READ_PHONE_STATE = 51,
+ OP_ADD_VOICEMAIL = 52,
+ OP_USE_SIP = 53,
+ OP_PROCESS_OUTGOING_CALLS = 54,
+ OP_USE_FINGERPRINT = 55,
+ OP_BODY_SENSORS = 56,
+ OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
+ };
+
+ AppOpsManager();
+
+ int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
+ int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
+ int32_t startOp(int32_t op, int32_t uid, const String16& callingPackage);
+ void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
+ void startWatchingMode(int32_t op, const String16& packageName,
+ const sp<IAppOpsCallback>& callback);
+ void stopWatchingMode(const sp<IAppOpsCallback>& callback);
+ int32_t permissionToOpCode(const String16& permission);
+
+private:
+ Mutex mLock;
+ sp<IAppOpsService> mService;
+
+ sp<IAppOpsService> getService();
+};
+
+
+}; // namespace android
+// ---------------------------------------------------------------------------
+#endif // ANDROID_APP_OPS_MANAGER_H
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
new file mode 100644
index 0000000..3404881
--- /dev/null
+++ b/libs/binder/include/binder/Binder.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 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_BINDER_H
+#define ANDROID_BINDER_H
+
+#include <atomic>
+#include <stdint.h>
+#include <binder/IBinder.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class BBinder : public IBinder
+{
+public:
+ BBinder();
+
+ virtual const String16& getInterfaceDescriptor() const;
+ virtual bool isBinderAlive() const;
+ virtual status_t pingBinder();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ virtual status_t transact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+ virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0);
+
+ virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0,
+ wp<DeathRecipient>* outRecipient = NULL);
+
+ virtual void attachObject( const void* objectID,
+ void* object,
+ void* cleanupCookie,
+ object_cleanup_func func);
+ virtual void* findObject(const void* objectID) const;
+ virtual void detachObject(const void* objectID);
+
+ virtual BBinder* localBinder();
+
+protected:
+ virtual ~BBinder();
+
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+private:
+ BBinder(const BBinder& o);
+ BBinder& operator=(const BBinder& o);
+
+ class Extras;
+
+ std::atomic<Extras*> mExtras;
+ void* mReserved0;
+};
+
+// ---------------------------------------------------------------------------
+
+class BpRefBase : public virtual RefBase
+{
+protected:
+ explicit BpRefBase(const sp<IBinder>& o);
+ virtual ~BpRefBase();
+ virtual void onFirstRef();
+ virtual void onLastStrongRef(const void* id);
+ virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
+
+ inline IBinder* remote() { return mRemote; }
+ inline IBinder* remote() const { return mRemote; }
+
+private:
+ BpRefBase(const BpRefBase& o);
+ BpRefBase& operator=(const BpRefBase& o);
+
+ IBinder* const mRemote;
+ RefBase::weakref_type* mRefs;
+ std::atomic<int32_t> mState;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_BINDER_H
diff --git a/include/binder/BinderService.h b/libs/binder/include/binder/BinderService.h
similarity index 100%
rename from include/binder/BinderService.h
rename to libs/binder/include/binder/BinderService.h
diff --git a/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
similarity index 100%
rename from include/binder/BpBinder.h
rename to libs/binder/include/binder/BpBinder.h
diff --git a/include/binder/BufferedTextOutput.h b/libs/binder/include/binder/BufferedTextOutput.h
similarity index 100%
rename from include/binder/BufferedTextOutput.h
rename to libs/binder/include/binder/BufferedTextOutput.h
diff --git a/include/binder/Debug.h b/libs/binder/include/binder/Debug.h
similarity index 100%
rename from include/binder/Debug.h
rename to libs/binder/include/binder/Debug.h
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
new file mode 100644
index 0000000..5ad2180
--- /dev/null
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -0,0 +1,42 @@
+/*
+ * 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_IACTIVITY_MANAGER_H
+#define ANDROID_IACTIVITY_MANAGER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class IActivityManager : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ActivityManager)
+
+ virtual int openContentUri(const String16& /* stringUri */) = 0;
+
+ enum {
+ OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ------------------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IACTIVITY_MANAGER_H
\ No newline at end of file
diff --git a/libs/binder/include/binder/IAppOpsCallback.h b/libs/binder/include/binder/IAppOpsCallback.h
new file mode 100644
index 0000000..b62e9e2
--- /dev/null
+++ b/libs/binder/include/binder/IAppOpsCallback.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+//
+#ifndef ANDROID_IAPP_OPS_CALLBACK_H
+#define ANDROID_IAPP_OPS_CALLBACK_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IAppOpsCallback : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AppOpsCallback)
+
+ virtual void opChanged(int32_t op, const String16& packageName) = 0;
+
+ enum {
+ OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnAppOpsCallback : public BnInterface<IAppOpsCallback>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAPP_OPS_CALLBACK_H
+
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
new file mode 100644
index 0000000..dc18045
--- /dev/null
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+//
+#ifndef ANDROID_IAPP_OPS_SERVICE_H
+#define ANDROID_IAPP_OPS_SERVICE_H
+
+#include <binder/IAppOpsCallback.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IAppOpsService : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AppOpsService)
+
+ virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
+ const String16& packageName) = 0;
+ virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
+ const String16& packageName) = 0;
+ virtual void startWatchingMode(int32_t op, const String16& packageName,
+ const sp<IAppOpsCallback>& callback) = 0;
+ virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
+ virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
+ virtual int32_t permissionToOpCode(const String16& permission) = 0;
+
+ enum {
+ CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1,
+ START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2,
+ FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
+ START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
+ STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
+ GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
+ PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+ };
+
+ enum {
+ MODE_ALLOWED = 0,
+ MODE_IGNORED = 1,
+ MODE_ERRORED = 2
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnAppOpsService : public BnInterface<IAppOpsService>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include/binder/IBatteryStats.h
new file mode 100644
index 0000000..e15d6f0
--- /dev/null
+++ b/libs/binder/include/binder/IBatteryStats.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_IBATTERYSTATS_H
+#define ANDROID_IBATTERYSTATS_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IBatteryStats : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(BatteryStats)
+
+ virtual void noteStartSensor(int uid, int sensor) = 0;
+ virtual void noteStopSensor(int uid, int sensor) = 0;
+ virtual void noteStartVideo(int uid) = 0;
+ virtual void noteStopVideo(int uid) = 0;
+ virtual void noteStartAudio(int uid) = 0;
+ virtual void noteStopAudio(int uid) = 0;
+ virtual void noteResetVideo() = 0;
+ virtual void noteResetAudio() = 0;
+ virtual void noteFlashlightOn(int uid) = 0;
+ virtual void noteFlashlightOff(int uid) = 0;
+ virtual void noteStartCamera(int uid) = 0;
+ virtual void noteStopCamera(int uid) = 0;
+ virtual void noteResetCamera() = 0;
+ virtual void noteResetFlashlight() = 0;
+
+ enum {
+ NOTE_START_SENSOR_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ NOTE_STOP_SENSOR_TRANSACTION,
+ NOTE_START_VIDEO_TRANSACTION,
+ NOTE_STOP_VIDEO_TRANSACTION,
+ NOTE_START_AUDIO_TRANSACTION,
+ NOTE_STOP_AUDIO_TRANSACTION,
+ NOTE_RESET_VIDEO_TRANSACTION,
+ NOTE_RESET_AUDIO_TRANSACTION,
+ NOTE_FLASHLIGHT_ON_TRANSACTION,
+ NOTE_FLASHLIGHT_OFF_TRANSACTION,
+ NOTE_START_CAMERA_TRANSACTION,
+ NOTE_STOP_CAMERA_TRANSACTION,
+ NOTE_RESET_CAMERA_TRANSACTION,
+ NOTE_RESET_FLASHLIGHT_TRANSACTION
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnBatteryStats : public BnInterface<IBatteryStats>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IBATTERYSTATS_H
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
new file mode 100644
index 0000000..2e62957
--- /dev/null
+++ b/libs/binder/include/binder/IBinder.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 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_IBINDER_H
+#define ANDROID_IBINDER_H
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+
+// linux/binder.h already defines this, but we can't just include it from there
+// because there are host builds that include this file.
+#ifndef B_PACK_CHARS
+#define B_PACK_CHARS(c1, c2, c3, c4) \
+ ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
+#endif // B_PACK_CHARS
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class BBinder;
+class BpBinder;
+class IInterface;
+class Parcel;
+class IResultReceiver;
+class IShellCallback;
+
+/**
+ * Base class and low-level protocol for a remotable object.
+ * You can derive from this class to create an object for which other
+ * processes can hold references to it. Communication between processes
+ * (method calls, property get and set) is down through a low-level
+ * protocol implemented on top of the transact() API.
+ */
+class IBinder : public virtual RefBase
+{
+public:
+ enum {
+ FIRST_CALL_TRANSACTION = 0x00000001,
+ LAST_CALL_TRANSACTION = 0x00ffffff,
+
+ PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'),
+ DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'),
+ SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
+ INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
+ SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
+
+ // Corresponds to TF_ONE_WAY -- an asynchronous call.
+ FLAG_ONEWAY = 0x00000001
+ };
+
+ IBinder();
+
+ /**
+ * Check if this IBinder implements the interface named by
+ * @a descriptor. If it does, the base pointer to it is returned,
+ * which you can safely static_cast<> to the concrete C++ interface.
+ */
+ virtual sp<IInterface> queryLocalInterface(const String16& descriptor);
+
+ /**
+ * Return the canonical name of the interface provided by this IBinder
+ * object.
+ */
+ virtual const String16& getInterfaceDescriptor() const = 0;
+
+ virtual bool isBinderAlive() const = 0;
+ virtual status_t pingBinder() = 0;
+ virtual status_t dump(int fd, const Vector<String16>& args) = 0;
+ static status_t shellCommand(const sp<IBinder>& target, int in, int out, int err,
+ Vector<String16>& args, const sp<IShellCallback>& callback,
+ const sp<IResultReceiver>& resultReceiver);
+
+ virtual status_t transact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0) = 0;
+
+ // DeathRecipient is pure abstract, there is no virtual method
+ // implementation to put in a translation unit in order to silence the
+ // weak vtables warning.
+ #if defined(__clang__)
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wweak-vtables"
+ #endif
+
+ class DeathRecipient : public virtual RefBase
+ {
+ public:
+ virtual void binderDied(const wp<IBinder>& who) = 0;
+ };
+
+ #if defined(__clang__)
+ #pragma clang diagnostic pop
+ #endif
+
+ /**
+ * Register the @a recipient for a notification if this binder
+ * goes away. If this binder object unexpectedly goes away
+ * (typically because its hosting process has been killed),
+ * then DeathRecipient::binderDied() will be called with a reference
+ * to this.
+ *
+ * The @a cookie is optional -- if non-NULL, it should be a
+ * memory address that you own (that is, you know it is unique).
+ *
+ * @note You will only receive death notifications for remote binders,
+ * as local binders by definition can't die without you dying as well.
+ * Trying to use this function on a local binder will result in an
+ * INVALID_OPERATION code being returned and nothing happening.
+ *
+ * @note This link always holds a weak reference to its recipient.
+ *
+ * @note You will only receive a weak reference to the dead
+ * binder. You should not try to promote this to a strong reference.
+ * (Nor should you need to, as there is nothing useful you can
+ * directly do with it now that it has passed on.)
+ */
+ virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0) = 0;
+
+ /**
+ * Remove a previously registered death notification.
+ * The @a recipient will no longer be called if this object
+ * dies. The @a cookie is optional. If non-NULL, you can
+ * supply a NULL @a recipient, and the recipient previously
+ * added with that cookie will be unlinked.
+ */
+ virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0,
+ wp<DeathRecipient>* outRecipient = NULL) = 0;
+
+ virtual bool checkSubclass(const void* subclassID) const;
+
+ typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
+
+ virtual void attachObject( const void* objectID,
+ void* object,
+ void* cleanupCookie,
+ object_cleanup_func func) = 0;
+ virtual void* findObject(const void* objectID) const = 0;
+ virtual void detachObject(const void* objectID) = 0;
+
+ virtual BBinder* localBinder();
+ virtual BpBinder* remoteBinder();
+
+protected:
+ virtual ~IBinder();
+
+private:
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_IBINDER_H
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
new file mode 100644
index 0000000..0f1fe5b
--- /dev/null
+++ b/libs/binder/include/binder/IInterface.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2005 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_IINTERFACE_H
+#define ANDROID_IINTERFACE_H
+
+#include <binder/Binder.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IInterface : public virtual RefBase
+{
+public:
+ IInterface();
+ static sp<IBinder> asBinder(const IInterface*);
+ static sp<IBinder> asBinder(const sp<IInterface>&);
+
+protected:
+ virtual ~IInterface();
+ virtual IBinder* onAsBinder() = 0;
+};
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
+{
+ return INTERFACE::asInterface(obj);
+}
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+class BnInterface : public INTERFACE, public BBinder
+{
+public:
+ virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
+ virtual const String16& getInterfaceDescriptor() const;
+
+protected:
+ virtual IBinder* onAsBinder();
+};
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+class BpInterface : public INTERFACE, public BpRefBase
+{
+public:
+ explicit BpInterface(const sp<IBinder>& remote);
+
+protected:
+ virtual IBinder* onAsBinder();
+};
+
+// ----------------------------------------------------------------------
+
+#define DECLARE_META_INTERFACE(INTERFACE) \
+ static const ::android::String16 descriptor; \
+ static ::android::sp<I##INTERFACE> asInterface( \
+ const ::android::sp<::android::IBinder>& obj); \
+ virtual const ::android::String16& getInterfaceDescriptor() const; \
+ I##INTERFACE(); \
+ virtual ~I##INTERFACE(); \
+
+
+#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ const ::android::String16 I##INTERFACE::descriptor(NAME); \
+ const ::android::String16& \
+ I##INTERFACE::getInterfaceDescriptor() const { \
+ return I##INTERFACE::descriptor; \
+ } \
+ ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
+ const ::android::sp<::android::IBinder>& obj) \
+ { \
+ ::android::sp<I##INTERFACE> intr; \
+ if (obj != NULL) { \
+ intr = static_cast<I##INTERFACE*>( \
+ obj->queryLocalInterface( \
+ I##INTERFACE::descriptor).get()); \
+ if (intr == NULL) { \
+ intr = new Bp##INTERFACE(obj); \
+ } \
+ } \
+ return intr; \
+ } \
+ I##INTERFACE::I##INTERFACE() { } \
+ I##INTERFACE::~I##INTERFACE() { } \
+
+
+#define CHECK_INTERFACE(interface, data, reply) \
+ if (!(data).checkInterface(this)) { return PERMISSION_DENIED; } \
+
+
+// ----------------------------------------------------------------------
+// No user-serviceable parts after this...
+
+template<typename INTERFACE>
+inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
+ const String16& _descriptor)
+{
+ if (_descriptor == INTERFACE::descriptor) return this;
+ return NULL;
+}
+
+template<typename INTERFACE>
+inline const String16& BnInterface<INTERFACE>::getInterfaceDescriptor() const
+{
+ return INTERFACE::getInterfaceDescriptor();
+}
+
+template<typename INTERFACE>
+IBinder* BnInterface<INTERFACE>::onAsBinder()
+{
+ return this;
+}
+
+template<typename INTERFACE>
+inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
+ : BpRefBase(remote)
+{
+}
+
+template<typename INTERFACE>
+inline IBinder* BpInterface<INTERFACE>::onAsBinder()
+{
+ return remote();
+}
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IINTERFACE_H
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
new file mode 100644
index 0000000..b21047f
--- /dev/null
+++ b/libs/binder/include/binder/IMediaResourceMonitor.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_I_MEDIA_RESOURCE_MONITOR_H
+#define ANDROID_I_MEDIA_RESOURCE_MONITOR_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IMediaResourceMonitor : public IInterface {
+public:
+ DECLARE_META_INTERFACE(MediaResourceMonitor)
+
+ // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX.
+ enum {
+ TYPE_VIDEO_CODEC = 0,
+ TYPE_AUDIO_CODEC = 1,
+ };
+
+ virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0;
+
+ enum {
+ NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION,
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
+public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_I_MEDIA_RESOURCE_MONITOR_H
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
new file mode 100644
index 0000000..15a104f
--- /dev/null
+++ b/libs/binder/include/binder/IMemory.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007 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_IMEMORY_H
+#define ANDROID_IMEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IMemoryHeap : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(MemoryHeap)
+
+ // flags returned by getFlags()
+ enum {
+ READ_ONLY = 0x00000001
+ };
+
+ virtual int getHeapID() const = 0;
+ virtual void* getBase() const = 0;
+ virtual size_t getSize() const = 0;
+ virtual uint32_t getFlags() const = 0;
+ virtual uint32_t getOffset() const = 0;
+
+ // these are there just for backward source compatibility
+ int32_t heapID() const { return getHeapID(); }
+ void* base() const { return getBase(); }
+ size_t virtualSize() const { return getSize(); }
+};
+
+class BnMemoryHeap : public BnInterface<IMemoryHeap>
+{
+public:
+ virtual status_t onTransact(
+ uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+ BnMemoryHeap();
+protected:
+ virtual ~BnMemoryHeap();
+};
+
+// ----------------------------------------------------------------------------
+
+class IMemory : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(Memory)
+
+ virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
+
+ // helpers
+ void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
+ void* pointer() const;
+ size_t size() const;
+ ssize_t offset() const;
+};
+
+class BnMemory : public BnInterface<IMemory>
+{
+public:
+ virtual status_t onTransact(
+ uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+ BnMemory();
+protected:
+ virtual ~BnMemory();
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IMEMORY_H
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
new file mode 100644
index 0000000..245607e
--- /dev/null
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2005 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_IPC_THREAD_STATE_H
+#define ANDROID_IPC_THREAD_STATE_H
+
+#include <utils/Errors.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <utils/Vector.h>
+
+#if defined(_WIN32)
+typedef int uid_t;
+#endif
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class IPCThreadState
+{
+public:
+ static IPCThreadState* self();
+ static IPCThreadState* selfOrNull(); // self(), but won't instantiate
+
+ sp<ProcessState> process();
+
+ status_t clearLastError();
+
+ pid_t getCallingPid() const;
+ uid_t getCallingUid() const;
+
+ void setStrictModePolicy(int32_t policy);
+ int32_t getStrictModePolicy() const;
+
+ void setLastTransactionBinderFlags(int32_t flags);
+ int32_t getLastTransactionBinderFlags() const;
+
+ int64_t clearCallingIdentity();
+ void restoreCallingIdentity(int64_t token);
+
+ int setupPolling(int* fd);
+ status_t handlePolledCommands();
+ void flushCommands();
+
+ void joinThreadPool(bool isMain = true);
+
+ // Stop the local process.
+ void stopProcess(bool immediate = true);
+
+ status_t transact(int32_t handle,
+ uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+ void incStrongHandle(int32_t handle);
+ void decStrongHandle(int32_t handle);
+ void incWeakHandle(int32_t handle);
+ void decWeakHandle(int32_t handle);
+ status_t attemptIncStrongHandle(int32_t handle);
+ static void expungeHandle(int32_t handle, IBinder* binder);
+ status_t requestDeathNotification( int32_t handle,
+ BpBinder* proxy);
+ status_t clearDeathNotification( int32_t handle,
+ BpBinder* proxy);
+
+ static void shutdown();
+
+ // Call this to disable switching threads to background scheduling when
+ // receiving incoming IPC calls. This is specifically here for the
+ // Android system process, since it expects to have background apps calling
+ // in to it but doesn't want to acquire locks in its services while in
+ // the background.
+ static void disableBackgroundScheduling(bool disable);
+ bool backgroundSchedulingDisabled();
+
+ // Call blocks until the number of executing binder threads is less than
+ // the maximum number of binder threads threads allowed for this process.
+ void blockUntilThreadAvailable();
+
+private:
+ IPCThreadState();
+ ~IPCThreadState();
+
+ status_t sendReply(const Parcel& reply, uint32_t flags);
+ status_t waitForResponse(Parcel *reply,
+ status_t *acquireResult=NULL);
+ status_t talkWithDriver(bool doReceive=true);
+ status_t writeTransactionData(int32_t cmd,
+ uint32_t binderFlags,
+ int32_t handle,
+ uint32_t code,
+ const Parcel& data,
+ status_t* statusBuffer);
+ status_t getAndExecuteCommand();
+ status_t executeCommand(int32_t command);
+ void processPendingDerefs();
+
+ void clearCaller();
+
+ static void threadDestructor(void *st);
+ static void freeBuffer(Parcel* parcel,
+ const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsSize,
+ void* cookie);
+
+ const sp<ProcessState> mProcess;
+ Vector<BBinder*> mPendingStrongDerefs;
+ Vector<RefBase::weakref_type*> mPendingWeakDerefs;
+
+ Parcel mIn;
+ Parcel mOut;
+ status_t mLastError;
+ pid_t mCallingPid;
+ uid_t mCallingUid;
+ int32_t mStrictModePolicy;
+ int32_t mLastTransactionBinderFlags;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/libs/binder/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h
new file mode 100644
index 0000000..25f3431
--- /dev/null
+++ b/libs/binder/include/binder/IPermissionController.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2005 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_IPERMISSION_CONTROLLER_H
+#define ANDROID_IPERMISSION_CONTROLLER_H
+
+#include <binder/IInterface.h>
+#include <stdlib.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IPermissionController : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(PermissionController)
+
+ virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) = 0;
+
+ virtual void getPackagesForUid(const uid_t uid, Vector<String16> &packages) = 0;
+
+ virtual bool isRuntimePermission(const String16& permission) = 0;
+
+ enum {
+ CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ GET_PACKAGES_FOR_UID_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1,
+ IS_RUNTIME_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 2
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnPermissionController : public BnInterface<IPermissionController>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IPERMISSION_CONTROLLER_H
+
diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include/binder/IProcessInfoService.h
new file mode 100644
index 0000000..2669f91
--- /dev/null
+++ b/libs/binder/include/binder/IProcessInfoService.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 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_I_PROCESS_INFO_SERVICE_H
+#define ANDROID_I_PROCESS_INFO_SERVICE_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IProcessInfoService : public IInterface {
+public:
+ DECLARE_META_INTERFACE(ProcessInfoService)
+
+ virtual status_t getProcessStatesFromPids( size_t length,
+ /*in*/ int32_t* pids,
+ /*out*/ int32_t* states) = 0;
+
+ virtual status_t getProcessStatesAndOomScoresFromPids( size_t length,
+ /*in*/ int32_t* pids,
+ /*out*/ int32_t* states,
+ /*out*/ int32_t* scores) = 0;
+
+ enum {
+ GET_PROCESS_STATES_FROM_PIDS = IBinder::FIRST_CALL_TRANSACTION,
+ GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS,
+ };
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/libs/binder/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h
new file mode 100644
index 0000000..e494fba
--- /dev/null
+++ b/libs/binder/include/binder/IResultReceiver.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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_IRESULT_RECEIVER_H
+#define ANDROID_IRESULT_RECEIVER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IResultReceiver : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ResultReceiver)
+
+ virtual void send(int32_t resultCode) = 0;
+
+ enum {
+ OP_SEND = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnResultReceiver : public BnInterface<IResultReceiver>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IRESULT_RECEIVER_H
+
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
new file mode 100644
index 0000000..3b23f81
--- /dev/null
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2005 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_ISERVICE_MANAGER_H
+#define ANDROID_ISERVICE_MANAGER_H
+
+#include <binder/IInterface.h>
+#include <binder/IPermissionController.h>
+#include <utils/Vector.h>
+#include <utils/String16.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IServiceManager : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ServiceManager)
+
+ /**
+ * Retrieve an existing service, blocking for a few seconds
+ * if it doesn't yet exist.
+ */
+ virtual sp<IBinder> getService( const String16& name) const = 0;
+
+ /**
+ * Retrieve an existing service, non-blocking.
+ */
+ virtual sp<IBinder> checkService( const String16& name) const = 0;
+
+ /**
+ * Register a service.
+ */
+ virtual status_t addService( const String16& name,
+ const sp<IBinder>& service,
+ bool allowIsolated = false) = 0;
+
+ /**
+ * Return list of all existing services.
+ */
+ virtual Vector<String16> listServices() = 0;
+
+ enum {
+ GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ CHECK_SERVICE_TRANSACTION,
+ ADD_SERVICE_TRANSACTION,
+ LIST_SERVICES_TRANSACTION,
+ };
+};
+
+sp<IServiceManager> defaultServiceManager();
+
+template<typename INTERFACE>
+status_t getService(const String16& name, sp<INTERFACE>* outService)
+{
+ const sp<IServiceManager> sm = defaultServiceManager();
+ if (sm != NULL) {
+ *outService = interface_cast<INTERFACE>(sm->getService(name));
+ if ((*outService) != NULL) return NO_ERROR;
+ }
+ return NAME_NOT_FOUND;
+}
+
+bool checkCallingPermission(const String16& permission);
+bool checkCallingPermission(const String16& permission,
+ int32_t* outPid, int32_t* outUid);
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
+
+}; // namespace android
+
+#endif // ANDROID_ISERVICE_MANAGER_H
+
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h
new file mode 100644
index 0000000..fda9ee6
--- /dev/null
+++ b/libs/binder/include/binder/IShellCallback.h
@@ -0,0 +1,55 @@
+/*
+ * 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_ISHELL_CALLBACK_H
+#define ANDROID_ISHELL_CALLBACK_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IShellCallback : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ShellCallback);
+
+ virtual int openOutputFile(const String16& path, const String16& seLinuxContext) = 0;
+
+ enum {
+ OP_OPEN_OUTPUT_FILE = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnShellCallback : public BnInterface<IShellCallback>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_ISHELL_CALLBACK_H
+
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
new file mode 100644
index 0000000..96ebaac
--- /dev/null
+++ b/libs/binder/include/binder/IpPrefix.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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_IP_PREFIX_H
+#define ANDROID_IP_PREFIX_H
+
+#include <netinet/in.h>
+
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace net {
+
+/*
+ * C++ implementation of the Java class android.net.IpPrefix
+ */
+class IpPrefix : public Parcelable {
+public:
+ IpPrefix() = default;
+ virtual ~IpPrefix() = default;
+ IpPrefix(const IpPrefix& prefix) = default;
+
+ IpPrefix(const struct in6_addr& addr, int32_t plen):
+ mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { }
+
+ IpPrefix(const struct in_addr& addr, int32_t plen):
+ mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { }
+
+ bool getAddressAsIn6Addr(struct in6_addr* addr) const;
+ bool getAddressAsInAddr(struct in_addr* addr) const;
+
+ const struct in6_addr& getAddressAsIn6Addr() const;
+ const struct in_addr& getAddressAsInAddr() const;
+
+ bool isIpv6() const;
+ bool isIpv4() const;
+
+ int32_t getPrefixLength() const;
+
+ void setAddress(const struct in6_addr& addr);
+ void setAddress(const struct in_addr& addr);
+
+ void setPrefixLength(int32_t prefix);
+
+ friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs);
+
+ friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) {
+ return !(lhs == rhs);
+ }
+
+public:
+ // Overrides
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+private:
+ union InternalUnion {
+ InternalUnion() = default;
+ InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { };
+ InternalUnion(const struct in_addr &addr):mInAddr(addr) { };
+ struct in6_addr mIn6Addr;
+ struct in_addr mInAddr;
+ } mUnion;
+ int32_t mPrefixLength;
+ bool mIsIpv6;
+};
+
+} // namespace net
+
+} // namespace android
+
+#endif // ANDROID_IP_PREFIX_H
diff --git a/libs/binder/include/binder/Map.h b/libs/binder/include/binder/Map.h
new file mode 100644
index 0000000..96a4f8a
--- /dev/null
+++ b/libs/binder/include/binder/Map.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005 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_MAP_H
+#define ANDROID_MAP_H
+
+#include <map>
+#include <string>
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace binder {
+
+class Value;
+
+/**
+ * Convenience typedef for ::std::map<::std::string,::android::binder::Value>
+ */
+typedef ::std::map<::std::string, Value> Map;
+
+} // namespace binder
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_MAP_H
diff --git a/include/binder/MemoryBase.h b/libs/binder/include/binder/MemoryBase.h
similarity index 100%
rename from include/binder/MemoryBase.h
rename to libs/binder/include/binder/MemoryBase.h
diff --git a/include/binder/MemoryDealer.h b/libs/binder/include/binder/MemoryDealer.h
similarity index 100%
rename from include/binder/MemoryDealer.h
rename to libs/binder/include/binder/MemoryDealer.h
diff --git a/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
similarity index 100%
rename from include/binder/MemoryHeapBase.h
rename to libs/binder/include/binder/MemoryHeapBase.h
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
new file mode 100644
index 0000000..5d36526
--- /dev/null
+++ b/libs/binder/include/binder/Parcel.h
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2005 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_PARCEL_H
+#define ANDROID_PARCEL_H
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <cutils/native_handle.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/Flattenable.h>
+#include <linux/android/binder.h>
+
+#include <binder/IInterface.h>
+#include <binder/Parcelable.h>
+#include <binder/Map.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+template <typename T> class Flattenable;
+template <typename T> class LightFlattenable;
+class IBinder;
+class IPCThreadState;
+class ProcessState;
+class String8;
+class TextOutput;
+
+namespace binder {
+class Value;
+};
+
+class Parcel {
+ friend class IPCThreadState;
+public:
+ class ReadableBlob;
+ class WritableBlob;
+
+ Parcel();
+ ~Parcel();
+
+ const uint8_t* data() const;
+ size_t dataSize() const;
+ size_t dataAvail() const;
+ size_t dataPosition() const;
+ size_t dataCapacity() const;
+
+ status_t setDataSize(size_t size);
+ void setDataPosition(size_t pos) const;
+ status_t setDataCapacity(size_t size);
+
+ status_t setData(const uint8_t* buffer, size_t len);
+
+ status_t appendFrom(const Parcel *parcel,
+ size_t start, size_t len);
+
+ int compareData(const Parcel& other);
+
+ bool allowFds() const;
+ bool pushAllowFds(bool allowFds);
+ void restoreAllowFds(bool lastValue);
+
+ bool hasFileDescriptors() const;
+
+ // Writes the RPC header.
+ status_t writeInterfaceToken(const String16& interface);
+
+ // Parses the RPC header, returning true if the interface name
+ // in the header matches the expected interface from the caller.
+ //
+ // Additionally, enforceInterface does part of the work of
+ // propagating the StrictMode policy mask, populating the current
+ // IPCThreadState, which as an optimization may optionally be
+ // passed in.
+ bool enforceInterface(const String16& interface,
+ IPCThreadState* threadState = NULL) const;
+ bool checkInterface(IBinder*) const;
+
+ void freeData();
+
+private:
+ const binder_size_t* objects() const;
+
+public:
+ size_t objectsCount() const;
+
+ status_t errorCheck() const;
+ void setError(status_t err);
+
+ status_t write(const void* data, size_t len);
+ void* writeInplace(size_t len);
+ status_t writeUnpadded(const void* data, size_t len);
+ status_t writeInt32(int32_t val);
+ status_t writeUint32(uint32_t val);
+ status_t writeInt64(int64_t val);
+ status_t writeUint64(uint64_t val);
+ status_t writeFloat(float val);
+ status_t writeDouble(double val);
+ status_t writeCString(const char* str);
+ status_t writeString8(const String8& str);
+ status_t writeString16(const String16& str);
+ status_t writeString16(const std::unique_ptr<String16>& str);
+ status_t writeString16(const char16_t* str, size_t len);
+ status_t writeStrongBinder(const sp<IBinder>& val);
+ status_t writeWeakBinder(const wp<IBinder>& val);
+ status_t writeInt32Array(size_t len, const int32_t *val);
+ status_t writeByteArray(size_t len, const uint8_t *val);
+ status_t writeBool(bool val);
+ status_t writeChar(char16_t val);
+ status_t writeByte(int8_t val);
+
+ // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
+ status_t writeUtf8AsUtf16(const std::string& str);
+ status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
+
+ status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
+ status_t writeByteVector(const std::vector<int8_t>& val);
+ status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
+ status_t writeByteVector(const std::vector<uint8_t>& val);
+ status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
+ status_t writeInt32Vector(const std::vector<int32_t>& val);
+ status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
+ status_t writeInt64Vector(const std::vector<int64_t>& val);
+ status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
+ status_t writeFloatVector(const std::vector<float>& val);
+ status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
+ status_t writeDoubleVector(const std::vector<double>& val);
+ status_t writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
+ status_t writeBoolVector(const std::vector<bool>& val);
+ status_t writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
+ status_t writeCharVector(const std::vector<char16_t>& val);
+ status_t writeString16Vector(
+ const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
+ status_t writeString16Vector(const std::vector<String16>& val);
+ status_t writeUtf8VectorAsUtf16Vector(
+ const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
+ status_t writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
+
+ status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
+ status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
+
+ template<typename T>
+ status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
+ template<typename T>
+ status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
+ template<typename T>
+ status_t writeParcelableVector(const std::vector<T>& val);
+
+ template<typename T>
+ status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable);
+
+ status_t writeParcelable(const Parcelable& parcelable);
+
+ status_t writeValue(const binder::Value& value);
+
+ template<typename T>
+ status_t write(const Flattenable<T>& val);
+
+ template<typename T>
+ status_t write(const LightFlattenable<T>& val);
+
+ template<typename T>
+ status_t writeVectorSize(const std::vector<T>& val);
+ template<typename T>
+ status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val);
+
+ status_t writeMap(const binder::Map& map);
+ status_t writeNullableMap(const std::unique_ptr<binder::Map>& map);
+
+ // Place a native_handle into the parcel (the native_handle's file-
+ // descriptors are dup'ed, so it is safe to delete the native_handle
+ // when this function returns).
+ // Doesn't take ownership of the native_handle.
+ status_t writeNativeHandle(const native_handle* handle);
+
+ // Place a file descriptor into the parcel. The given fd must remain
+ // valid for the lifetime of the parcel.
+ // The Parcel does not take ownership of the given fd unless you ask it to.
+ status_t writeFileDescriptor(int fd, bool takeOwnership = false);
+
+ // Place a file descriptor into the parcel. A dup of the fd is made, which
+ // will be closed once the parcel is destroyed.
+ status_t writeDupFileDescriptor(int fd);
+
+ // Place a Java "parcel file descriptor" into the parcel. The given fd must remain
+ // valid for the lifetime of the parcel.
+ // The Parcel does not take ownership of the given fd unless you ask it to.
+ status_t writeParcelFileDescriptor(int fd, bool takeOwnership = false);
+
+ // Place a file descriptor into the parcel. This will not affect the
+ // semantics of the smart file descriptor. A new descriptor will be
+ // created, and will be closed when the parcel is destroyed.
+ status_t writeUniqueFileDescriptor(
+ const base::unique_fd& fd);
+
+ // Place a vector of file desciptors into the parcel. Each descriptor is
+ // dup'd as in writeDupFileDescriptor
+ status_t writeUniqueFileDescriptorVector(
+ const std::unique_ptr<std::vector<base::unique_fd>>& val);
+ status_t writeUniqueFileDescriptorVector(
+ const std::vector<base::unique_fd>& val);
+
+ // Writes a blob to the parcel.
+ // If the blob is small, then it is stored in-place, otherwise it is
+ // transferred by way of an anonymous shared memory region. Prefer sending
+ // immutable blobs if possible since they may be subsequently transferred between
+ // processes without further copying whereas mutable blobs always need to be copied.
+ // The caller should call release() on the blob after writing its contents.
+ status_t writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob);
+
+ // Write an existing immutable blob file descriptor to the parcel.
+ // This allows the client to send the same blob to multiple processes
+ // as long as it keeps a dup of the blob file descriptor handy for later.
+ status_t writeDupImmutableBlobFileDescriptor(int fd);
+
+ status_t writeObject(const flat_binder_object& val, bool nullMetaData);
+
+ // Like Parcel.java's writeNoException(). Just writes a zero int32.
+ // Currently the native implementation doesn't do any of the StrictMode
+ // stack gathering and serialization that the Java implementation does.
+ status_t writeNoException();
+
+ void remove(size_t start, size_t amt);
+
+ status_t read(void* outData, size_t len) const;
+ const void* readInplace(size_t len) const;
+ int32_t readInt32() const;
+ status_t readInt32(int32_t *pArg) const;
+ uint32_t readUint32() const;
+ status_t readUint32(uint32_t *pArg) const;
+ int64_t readInt64() const;
+ status_t readInt64(int64_t *pArg) const;
+ uint64_t readUint64() const;
+ status_t readUint64(uint64_t *pArg) const;
+ float readFloat() const;
+ status_t readFloat(float *pArg) const;
+ double readDouble() const;
+ status_t readDouble(double *pArg) const;
+ intptr_t readIntPtr() const;
+ status_t readIntPtr(intptr_t *pArg) const;
+ bool readBool() const;
+ status_t readBool(bool *pArg) const;
+ char16_t readChar() const;
+ status_t readChar(char16_t *pArg) const;
+ int8_t readByte() const;
+ status_t readByte(int8_t *pArg) const;
+
+ // Read a UTF16 encoded string, convert to UTF8
+ status_t readUtf8FromUtf16(std::string* str) const;
+ status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
+
+ const char* readCString() const;
+ String8 readString8() const;
+ status_t readString8(String8* pArg) const;
+ String16 readString16() const;
+ status_t readString16(String16* pArg) const;
+ status_t readString16(std::unique_ptr<String16>* pArg) const;
+ const char16_t* readString16Inplace(size_t* outLen) const;
+ sp<IBinder> readStrongBinder() const;
+ status_t readStrongBinder(sp<IBinder>* val) const;
+ status_t readNullableStrongBinder(sp<IBinder>* val) const;
+ wp<IBinder> readWeakBinder() const;
+
+ template<typename T>
+ status_t readParcelableVector(
+ std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
+ template<typename T>
+ status_t readParcelableVector(std::vector<T>* val) const;
+
+ status_t readParcelable(Parcelable* parcelable) const;
+
+ template<typename T>
+ status_t readParcelable(std::unique_ptr<T>* parcelable) const;
+
+ status_t readValue(binder::Value* value) const;
+
+ template<typename T>
+ status_t readStrongBinder(sp<T>* val) const;
+
+ template<typename T>
+ status_t readNullableStrongBinder(sp<T>* val) const;
+
+ status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
+ status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
+
+ status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
+ status_t readByteVector(std::vector<int8_t>* val) const;
+ status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
+ status_t readByteVector(std::vector<uint8_t>* val) const;
+ status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
+ status_t readInt32Vector(std::vector<int32_t>* val) const;
+ status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
+ status_t readInt64Vector(std::vector<int64_t>* val) const;
+ status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
+ status_t readFloatVector(std::vector<float>* val) const;
+ status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
+ status_t readDoubleVector(std::vector<double>* val) const;
+ status_t readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
+ status_t readBoolVector(std::vector<bool>* val) const;
+ status_t readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
+ status_t readCharVector(std::vector<char16_t>* val) const;
+ status_t readString16Vector(
+ std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
+ status_t readString16Vector(std::vector<String16>* val) const;
+ status_t readUtf8VectorFromUtf16Vector(
+ std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
+ status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
+
+ template<typename T>
+ status_t read(Flattenable<T>& val) const;
+
+ template<typename T>
+ status_t read(LightFlattenable<T>& val) const;
+
+ template<typename T>
+ status_t resizeOutVector(std::vector<T>* val) const;
+ template<typename T>
+ status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
+
+ status_t readMap(binder::Map* map)const;
+ status_t readNullableMap(std::unique_ptr<binder::Map>* map) const;
+
+ // Like Parcel.java's readExceptionCode(). Reads the first int32
+ // off of a Parcel's header, returning 0 or the negative error
+ // code on exceptions, but also deals with skipping over rich
+ // response headers. Callers should use this to read & parse the
+ // response headers rather than doing it by hand.
+ int32_t readExceptionCode() const;
+
+ // Retrieve native_handle from the parcel. This returns a copy of the
+ // parcel's native_handle (the caller takes ownership). The caller
+ // must free the native_handle with native_handle_close() and
+ // native_handle_delete().
+ native_handle* readNativeHandle() const;
+
+
+ // Retrieve a file descriptor from the parcel. This returns the raw fd
+ // in the parcel, which you do not own -- use dup() to get your own copy.
+ int readFileDescriptor() const;
+
+ // Retrieve a Java "parcel file descriptor" from the parcel. This returns the raw fd
+ // in the parcel, which you do not own -- use dup() to get your own copy.
+ int readParcelFileDescriptor() const;
+
+ // Retrieve a smart file descriptor from the parcel.
+ status_t readUniqueFileDescriptor(
+ base::unique_fd* val) const;
+
+
+ // Retrieve a vector of smart file descriptors from the parcel.
+ status_t readUniqueFileDescriptorVector(
+ std::unique_ptr<std::vector<base::unique_fd>>* val) const;
+ status_t readUniqueFileDescriptorVector(
+ std::vector<base::unique_fd>* val) const;
+
+ // Reads a blob from the parcel.
+ // The caller should call release() on the blob after reading its contents.
+ status_t readBlob(size_t len, ReadableBlob* outBlob) const;
+
+ const flat_binder_object* readObject(bool nullMetaData) const;
+
+ // Explicitly close all file descriptors in the parcel.
+ void closeFileDescriptors();
+
+ // Debugging: get metrics on current allocations.
+ static size_t getGlobalAllocSize();
+ static size_t getGlobalAllocCount();
+
+private:
+ typedef void (*release_func)(Parcel* parcel,
+ const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsSize,
+ void* cookie);
+
+ uintptr_t ipcData() const;
+ size_t ipcDataSize() const;
+ uintptr_t ipcObjects() const;
+ size_t ipcObjectsCount() const;
+ void ipcSetDataReference(const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsCount,
+ release_func relFunc, void* relCookie);
+
+public:
+ void print(TextOutput& to, uint32_t flags = 0) const;
+
+private:
+ Parcel(const Parcel& o);
+ Parcel& operator=(const Parcel& o);
+
+ status_t finishWrite(size_t len);
+ void releaseObjects();
+ void acquireObjects();
+ status_t growData(size_t len);
+ status_t restartWrite(size_t desired);
+ status_t continueWrite(size_t desired);
+ status_t writePointer(uintptr_t val);
+ status_t readPointer(uintptr_t *pArg) const;
+ uintptr_t readPointer() const;
+ void freeDataNoInit();
+ void initState();
+ void scanForFds() const;
+
+ template<class T>
+ status_t readAligned(T *pArg) const;
+
+ template<class T> T readAligned() const;
+
+ template<class T>
+ status_t writeAligned(T val);
+
+ status_t writeRawNullableParcelable(const Parcelable*
+ parcelable);
+
+ template<typename T, typename U>
+ status_t unsafeReadTypedVector(std::vector<T>* val,
+ status_t(Parcel::*read_func)(U*) const) const;
+ template<typename T>
+ status_t readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
+ status_t(Parcel::*read_func)(T*) const) const;
+ template<typename T>
+ status_t readTypedVector(std::vector<T>* val,
+ status_t(Parcel::*read_func)(T*) const) const;
+ template<typename T, typename U>
+ status_t unsafeWriteTypedVector(const std::vector<T>& val,
+ status_t(Parcel::*write_func)(U));
+ template<typename T>
+ status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(const T&));
+ template<typename T>
+ status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(T));
+ template<typename T>
+ status_t writeTypedVector(const std::vector<T>& val,
+ status_t(Parcel::*write_func)(const T&));
+ template<typename T>
+ status_t writeTypedVector(const std::vector<T>& val,
+ status_t(Parcel::*write_func)(T));
+
+ status_t mError;
+ uint8_t* mData;
+ size_t mDataSize;
+ size_t mDataCapacity;
+ mutable size_t mDataPos;
+ binder_size_t* mObjects;
+ size_t mObjectsSize;
+ size_t mObjectsCapacity;
+ mutable size_t mNextObjectHint;
+
+ mutable bool mFdsKnown;
+ mutable bool mHasFds;
+ bool mAllowFds;
+
+ release_func mOwner;
+ void* mOwnerCookie;
+
+ class Blob {
+ public:
+ Blob();
+ ~Blob();
+
+ void clear();
+ void release();
+ inline size_t size() const { return mSize; }
+ inline int fd() const { return mFd; }
+ inline bool isMutable() const { return mMutable; }
+
+ protected:
+ void init(int fd, void* data, size_t size, bool isMutable);
+
+ int mFd; // owned by parcel so not closed when released
+ void* mData;
+ size_t mSize;
+ bool mMutable;
+ };
+
+ #if defined(__clang__)
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wweak-vtables"
+ #endif
+
+ // FlattenableHelperInterface and FlattenableHelper avoid generating a vtable entry in objects
+ // following Flattenable template/protocol.
+ class FlattenableHelperInterface {
+ protected:
+ ~FlattenableHelperInterface() { }
+ public:
+ virtual size_t getFlattenedSize() const = 0;
+ virtual size_t getFdCount() const = 0;
+ virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const = 0;
+ virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) = 0;
+ };
+
+ #if defined(__clang__)
+ #pragma clang diagnostic pop
+ #endif
+
+ // Concrete implementation of FlattenableHelperInterface that delegates virtual calls to the
+ // specified class T implementing the Flattenable protocol. It "virtualizes" a compile-time
+ // protocol.
+ template<typename T>
+ class FlattenableHelper : public FlattenableHelperInterface {
+ friend class Parcel;
+ const Flattenable<T>& val;
+ explicit FlattenableHelper(const Flattenable<T>& _val) : val(_val) { }
+
+ protected:
+ ~FlattenableHelper() = default;
+ public:
+ virtual size_t getFlattenedSize() const {
+ return val.getFlattenedSize();
+ }
+ virtual size_t getFdCount() const {
+ return val.getFdCount();
+ }
+ virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const {
+ return val.flatten(buffer, size, fds, count);
+ }
+ virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) {
+ return const_cast<Flattenable<T>&>(val).unflatten(buffer, size, fds, count);
+ }
+ };
+ status_t write(const FlattenableHelperInterface& val);
+ status_t read(FlattenableHelperInterface& val) const;
+
+public:
+ class ReadableBlob : public Blob {
+ friend class Parcel;
+ public:
+ inline const void* data() const { return mData; }
+ inline void* mutableData() { return isMutable() ? mData : NULL; }
+ };
+
+ class WritableBlob : public Blob {
+ friend class Parcel;
+ public:
+ inline void* data() { return mData; }
+ };
+
+private:
+ size_t mOpenAshmemSize;
+
+public:
+ // TODO: Remove once ABI can be changed.
+ size_t getBlobAshmemSize() const;
+ size_t getOpenAshmemSize() const;
+};
+
+// ---------------------------------------------------------------------------
+
+template<typename T>
+status_t Parcel::write(const Flattenable<T>& val) {
+ const FlattenableHelper<T> helper(val);
+ return write(helper);
+}
+
+template<typename T>
+status_t Parcel::write(const LightFlattenable<T>& val) {
+ size_t size(val.getFlattenedSize());
+ if (!val.isFixedSize()) {
+ if (size > INT32_MAX) {
+ return BAD_VALUE;
+ }
+ status_t err = writeInt32(static_cast<int32_t>(size));
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+ if (size) {
+ void* buffer = writeInplace(size);
+ if (buffer == NULL)
+ return NO_MEMORY;
+ return val.flatten(buffer, size);
+ }
+ return NO_ERROR;
+}
+
+template<typename T>
+status_t Parcel::read(Flattenable<T>& val) const {
+ FlattenableHelper<T> helper(val);
+ return read(helper);
+}
+
+template<typename T>
+status_t Parcel::read(LightFlattenable<T>& val) const {
+ size_t size;
+ if (val.isFixedSize()) {
+ size = val.getFlattenedSize();
+ } else {
+ int32_t s;
+ status_t err = readInt32(&s);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ size = static_cast<size_t>(s);
+ }
+ if (size) {
+ void const* buffer = readInplace(size);
+ return buffer == NULL ? NO_MEMORY :
+ val.unflatten(buffer, size);
+ }
+ return NO_ERROR;
+}
+
+template<typename T>
+status_t Parcel::writeVectorSize(const std::vector<T>& val) {
+ if (val.size() > INT32_MAX) {
+ return BAD_VALUE;
+ }
+ return writeInt32(static_cast<int32_t>(val.size()));
+}
+
+template<typename T>
+status_t Parcel::writeVectorSize(const std::unique_ptr<std::vector<T>>& val) {
+ if (!val) {
+ return writeInt32(-1);
+ }
+
+ return writeVectorSize(*val);
+}
+
+template<typename T>
+status_t Parcel::resizeOutVector(std::vector<T>* val) const {
+ int32_t size;
+ status_t err = readInt32(&size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (size < 0) {
+ return UNEXPECTED_NULL;
+ }
+ val->resize(size_t(size));
+ return OK;
+}
+
+template<typename T>
+status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
+ int32_t size;
+ status_t err = readInt32(&size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ val->reset();
+ if (size >= 0) {
+ val->reset(new std::vector<T>(size_t(size)));
+ }
+
+ return OK;
+}
+
+template<typename T>
+status_t Parcel::readStrongBinder(sp<T>* val) const {
+ sp<IBinder> tmp;
+ status_t ret = readStrongBinder(&tmp);
+
+ if (ret == OK) {
+ *val = interface_cast<T>(tmp);
+
+ if (val->get() == nullptr) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ return ret;
+}
+
+template<typename T>
+status_t Parcel::readNullableStrongBinder(sp<T>* val) const {
+ sp<IBinder> tmp;
+ status_t ret = readNullableStrongBinder(&tmp);
+
+ if (ret == OK) {
+ *val = interface_cast<T>(tmp);
+
+ if (val->get() == nullptr && tmp.get() != nullptr) {
+ ret = UNKNOWN_ERROR;
+ }
+ }
+
+ return ret;
+}
+
+template<typename T, typename U>
+status_t Parcel::unsafeReadTypedVector(
+ std::vector<T>* val,
+ status_t(Parcel::*read_func)(U*) const) const {
+ int32_t size;
+ status_t status = this->readInt32(&size);
+
+ if (status != OK) {
+ return status;
+ }
+
+ if (size < 0) {
+ return UNEXPECTED_NULL;
+ }
+
+ if (val->max_size() < static_cast<size_t>(size)) {
+ return NO_MEMORY;
+ }
+
+ val->resize(static_cast<size_t>(size));
+
+ if (val->size() < static_cast<size_t>(size)) {
+ return NO_MEMORY;
+ }
+
+ for (auto& v: *val) {
+ status = (this->*read_func)(&v);
+
+ if (status != OK) {
+ return status;
+ }
+ }
+
+ return OK;
+}
+
+template<typename T>
+status_t Parcel::readTypedVector(std::vector<T>* val,
+ status_t(Parcel::*read_func)(T*) const) const {
+ return unsafeReadTypedVector(val, read_func);
+}
+
+template<typename T>
+status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
+ status_t(Parcel::*read_func)(T*) const) const {
+ const size_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ val->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ val->reset(new std::vector<T>());
+
+ status = unsafeReadTypedVector(val->get(), read_func);
+
+ if (status != OK) {
+ val->reset();
+ }
+
+ return status;
+}
+
+template<typename T, typename U>
+status_t Parcel::unsafeWriteTypedVector(const std::vector<T>& val,
+ status_t(Parcel::*write_func)(U)) {
+ if (val.size() > std::numeric_limits<int32_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ status_t status = this->writeInt32(static_cast<int32_t>(val.size()));
+
+ if (status != OK) {
+ return status;
+ }
+
+ for (const auto& item : val) {
+ status = (this->*write_func)(item);
+
+ if (status != OK) {
+ return status;
+ }
+ }
+
+ return OK;
+}
+
+template<typename T>
+status_t Parcel::writeTypedVector(const std::vector<T>& val,
+ status_t(Parcel::*write_func)(const T&)) {
+ return unsafeWriteTypedVector(val, write_func);
+}
+
+template<typename T>
+status_t Parcel::writeTypedVector(const std::vector<T>& val,
+ status_t(Parcel::*write_func)(T)) {
+ return unsafeWriteTypedVector(val, write_func);
+}
+
+template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(const T&)) {
+ if (val.get() == nullptr) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(T)) {
+ if (val.get() == nullptr) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
+status_t Parcel::readParcelableVector(std::vector<T>* val) const {
+ return unsafeReadTypedVector<T, Parcelable>(val, &Parcel::readParcelable);
+}
+
+template<typename T>
+status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
+ const size_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ val->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ val->reset(new std::vector<std::unique_ptr<T>>());
+
+ status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
+
+ if (status != OK) {
+ val->reset();
+ }
+
+ return status;
+}
+
+template<typename T>
+status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const {
+ const size_t start = dataPosition();
+ int32_t present;
+ status_t status = readInt32(&present);
+ parcelable->reset();
+
+ if (status != OK || !present) {
+ return status;
+ }
+
+ setDataPosition(start);
+ parcelable->reset(new T());
+
+ status = readParcelable(parcelable->get());
+
+ if (status != OK) {
+ parcelable->reset();
+ }
+
+ return status;
+}
+
+template<typename T>
+status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
+ return writeRawNullableParcelable(parcelable.get());
+}
+
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::vector<T>& val) {
+ return unsafeWriteTypedVector<T,const Parcelable&>(val, &Parcel::writeParcelable);
+}
+
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) {
+ if (val.get() == nullptr) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
+}
+
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) {
+ if (val.get() == nullptr) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
+}
+
+// ---------------------------------------------------------------------------
+
+inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
+{
+ parcel.print(to);
+ return to;
+}
+
+// ---------------------------------------------------------------------------
+
+// Generic acquire and release of objects.
+void acquire_object(const sp<ProcessState>& proc,
+ const flat_binder_object& obj, const void* who);
+void release_object(const sp<ProcessState>& proc,
+ const flat_binder_object& obj, const void* who);
+
+void flatten_binder(const sp<ProcessState>& proc,
+ const sp<IBinder>& binder, flat_binder_object* out);
+void flatten_binder(const sp<ProcessState>& proc,
+ const wp<IBinder>& binder, flat_binder_object* out);
+status_t unflatten_binder(const sp<ProcessState>& proc,
+ const flat_binder_object& flat, sp<IBinder>* out);
+status_t unflatten_binder(const sp<ProcessState>& proc,
+ const flat_binder_object& flat, wp<IBinder>* out);
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PARCEL_H
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
new file mode 100644
index 0000000..d5b57ac
--- /dev/null
+++ b/libs/binder/include/binder/Parcelable.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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_PARCELABLE_H
+#define ANDROID_PARCELABLE_H
+
+#include <vector>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class Parcel;
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// Abstract interface of all parcelables.
+class Parcelable {
+public:
+ virtual ~Parcelable() = default;
+
+ // Write |this| parcelable to the given |parcel|. Keep in mind that
+ // implementations of writeToParcel must be manually kept in sync
+ // with readFromParcel and the Java equivalent versions of these methods.
+ //
+ // Returns android::OK on success and an appropriate error otherwise.
+ virtual status_t writeToParcel(Parcel* parcel) const = 0;
+
+ // Read data from the given |parcel| into |this|. After readFromParcel
+ // completes, |this| should have equivalent state to the object that
+ // wrote itself to the parcel.
+ //
+ // Returns android::OK on success and an appropriate error otherwise.
+ virtual status_t readFromParcel(const Parcel* parcel) = 0;
+}; // class Parcelable
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+} // namespace android
+
+#endif // ANDROID_PARCELABLE_H
diff --git a/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
similarity index 100%
rename from include/binder/PermissionCache.h
rename to libs/binder/include/binder/PermissionCache.h
diff --git a/libs/binder/include/binder/PersistableBundle.h b/libs/binder/include/binder/PersistableBundle.h
new file mode 100644
index 0000000..322fef9
--- /dev/null
+++ b/libs/binder/include/binder/PersistableBundle.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 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_PERSISTABLE_BUNDLE_H
+#define ANDROID_PERSISTABLE_BUNDLE_H
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace os {
+
+/*
+ * C++ implementation of PersistableBundle, a mapping from String values to
+ * various types that can be saved to persistent and later restored.
+ */
+class PersistableBundle : public Parcelable {
+public:
+ PersistableBundle() = default;
+ virtual ~PersistableBundle() = default;
+ PersistableBundle(const PersistableBundle& bundle) = default;
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ bool empty() const;
+ size_t size() const;
+ size_t erase(const String16& key);
+
+ /*
+ * Setters for PersistableBundle. Adds a a key-value pair instantiated with
+ * |key| and |value| into the member map appropriate for the type of |value|.
+ * If there is already an existing value for |key|, |value| will replace it.
+ */
+ void putBoolean(const String16& key, bool value);
+ void putInt(const String16& key, int32_t value);
+ void putLong(const String16& key, int64_t value);
+ void putDouble(const String16& key, double value);
+ void putString(const String16& key, const String16& value);
+ void putBooleanVector(const String16& key, const std::vector<bool>& value);
+ void putIntVector(const String16& key, const std::vector<int32_t>& value);
+ void putLongVector(const String16& key, const std::vector<int64_t>& value);
+ void putDoubleVector(const String16& key, const std::vector<double>& value);
+ void putStringVector(const String16& key, const std::vector<String16>& value);
+ void putPersistableBundle(const String16& key, const PersistableBundle& value);
+
+ /*
+ * Getters for PersistableBundle. If |key| exists, these methods write the
+ * value associated with |key| into |out|, and return true. Otherwise, these
+ * methods return false.
+ */
+ bool getBoolean(const String16& key, bool* out) const;
+ bool getInt(const String16& key, int32_t* out) const;
+ bool getLong(const String16& key, int64_t* out) const;
+ bool getDouble(const String16& key, double* out) const;
+ bool getString(const String16& key, String16* out) const;
+ bool getBooleanVector(const String16& key, std::vector<bool>* out) const;
+ bool getIntVector(const String16& key, std::vector<int32_t>* out) const;
+ bool getLongVector(const String16& key, std::vector<int64_t>* out) const;
+ bool getDoubleVector(const String16& key, std::vector<double>* out) const;
+ bool getStringVector(const String16& key, std::vector<String16>* out) const;
+ bool getPersistableBundle(const String16& key, PersistableBundle* out) const;
+
+ /* Getters for all keys for each value type */
+ std::set<String16> getBooleanKeys() const;
+ std::set<String16> getIntKeys() const;
+ std::set<String16> getLongKeys() const;
+ std::set<String16> getDoubleKeys() const;
+ std::set<String16> getStringKeys() const;
+ std::set<String16> getBooleanVectorKeys() const;
+ std::set<String16> getIntVectorKeys() const;
+ std::set<String16> getLongVectorKeys() const;
+ std::set<String16> getDoubleVectorKeys() const;
+ std::set<String16> getStringVectorKeys() const;
+ std::set<String16> getPersistableBundleKeys() const;
+
+ friend bool operator==(const PersistableBundle& lhs, const PersistableBundle& rhs) {
+ return (lhs.mBoolMap == rhs.mBoolMap && lhs.mIntMap == rhs.mIntMap &&
+ lhs.mLongMap == rhs.mLongMap && lhs.mDoubleMap == rhs.mDoubleMap &&
+ lhs.mStringMap == rhs.mStringMap && lhs.mBoolVectorMap == rhs.mBoolVectorMap &&
+ lhs.mIntVectorMap == rhs.mIntVectorMap &&
+ lhs.mLongVectorMap == rhs.mLongVectorMap &&
+ lhs.mDoubleVectorMap == rhs.mDoubleVectorMap &&
+ lhs.mStringVectorMap == rhs.mStringVectorMap &&
+ lhs.mPersistableBundleMap == rhs.mPersistableBundleMap);
+ }
+
+ friend bool operator!=(const PersistableBundle& lhs, const PersistableBundle& rhs) {
+ return !(lhs == rhs);
+ }
+
+private:
+ status_t writeToParcelInner(Parcel* parcel) const;
+ status_t readFromParcelInner(const Parcel* parcel, size_t length);
+
+ std::map<String16, bool> mBoolMap;
+ std::map<String16, int32_t> mIntMap;
+ std::map<String16, int64_t> mLongMap;
+ std::map<String16, double> mDoubleMap;
+ std::map<String16, String16> mStringMap;
+ std::map<String16, std::vector<bool>> mBoolVectorMap;
+ std::map<String16, std::vector<int32_t>> mIntVectorMap;
+ std::map<String16, std::vector<int64_t>> mLongVectorMap;
+ std::map<String16, std::vector<double>> mDoubleVectorMap;
+ std::map<String16, std::vector<String16>> mStringVectorMap;
+ std::map<String16, PersistableBundle> mPersistableBundleMap;
+};
+
+} // namespace os
+
+} // namespace android
+
+#endif // ANDROID_PERSISTABLE_BUNDLE_H
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include/binder/ProcessInfoService.h
new file mode 100644
index 0000000..0da61ee
--- /dev/null
+++ b/libs/binder/include/binder/ProcessInfoService.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 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_PROCESS_INFO_SERVICE_H
+#define ANDROID_PROCESS_INFO_SERVICE_H
+
+#include <binder/IProcessInfoService.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+#include <sys/types.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class ProcessInfoService : public Singleton<ProcessInfoService> {
+
+ friend class Singleton<ProcessInfoService>;
+ sp<IProcessInfoService> mProcessInfoService;
+ Mutex mProcessInfoLock;
+
+ ProcessInfoService();
+
+ status_t getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states);
+ status_t getProcessStatesScoresImpl(size_t length, /*in*/ int32_t* pids,
+ /*out*/ int32_t* states, /*out*/ int32_t *scores);
+ void updateBinderLocked();
+
+ static const int BINDER_ATTEMPT_LIMIT = 5;
+
+public:
+
+ /**
+ * For each PID in the given "pids" input array, write the current process state
+ * for that process into the "states" output array, or
+ * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID
+ * exists.
+ *
+ * Returns NO_ERROR if this operation was successful, or a negative error code otherwise.
+ */
+ static status_t getProcessStatesFromPids(size_t length, /*in*/ int32_t* pids,
+ /*out*/ int32_t* states) {
+ return ProcessInfoService::getInstance().getProcessStatesImpl(length, /*in*/ pids,
+ /*out*/ states);
+ }
+
+ /**
+ * For each PID in the given "pids" input array, write the current process state
+ * for that process into the "states" output array, or
+ * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID
+ * exists. OoM scores will also be written in the "scores" output array.
+ * Please also note that clients calling this method need to have
+ * "GET_PROCESS_STATE_AND_OOM_SCORE" permission.
+ *
+ * Returns NO_ERROR if this operation was successful, or a negative error code otherwise.
+ */
+ static status_t getProcessStatesScoresFromPids(size_t length, /*in*/ int32_t* pids,
+ /*out*/ int32_t* states, /*out*/ int32_t *scores) {
+ return ProcessInfoService::getInstance().getProcessStatesScoresImpl(
+ length, /*in*/ pids, /*out*/ states, /*out*/ scores);
+ }
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_PROCESS_INFO_SERVICE_H
+
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
new file mode 100644
index 0000000..1ef045d
--- /dev/null
+++ b/libs/binder/include/binder/ProcessState.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2005 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_PROCESS_STATE_H
+#define ANDROID_PROCESS_STATE_H
+
+#include <binder/IBinder.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+#include <utils/threads.h>
+
+#include <pthread.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class IPCThreadState;
+
+class ProcessState : public virtual RefBase
+{
+public:
+ static sp<ProcessState> self();
+ /* initWithDriver() can be used to configure libbinder to use
+ * a different binder driver dev node. It must be called *before*
+ * any call to ProcessState::self(). /dev/binder remains the default.
+ */
+ static sp<ProcessState> initWithDriver(const char *driver);
+
+ void setContextObject(const sp<IBinder>& object);
+ sp<IBinder> getContextObject(const sp<IBinder>& caller);
+
+ void setContextObject(const sp<IBinder>& object,
+ const String16& name);
+ sp<IBinder> getContextObject(const String16& name,
+ const sp<IBinder>& caller);
+
+ void startThreadPool();
+
+ typedef bool (*context_check_func)(const String16& name,
+ const sp<IBinder>& caller,
+ void* userData);
+
+ bool isContextManager(void) const;
+ bool becomeContextManager(
+ context_check_func checkFunc,
+ void* userData);
+
+ sp<IBinder> getStrongProxyForHandle(int32_t handle);
+ wp<IBinder> getWeakProxyForHandle(int32_t handle);
+ void expungeHandle(int32_t handle, IBinder* binder);
+
+ void spawnPooledThread(bool isMain);
+
+ status_t setThreadPoolMaxThreadCount(size_t maxThreads);
+ void giveThreadPoolName();
+
+ String8 getDriverName();
+
+private:
+ friend class IPCThreadState;
+
+ ProcessState(const char* driver);
+ ~ProcessState();
+
+ ProcessState(const ProcessState& o);
+ ProcessState& operator=(const ProcessState& o);
+ String8 makeBinderThreadName();
+
+ struct handle_entry {
+ IBinder* binder;
+ RefBase::weakref_type* refs;
+ };
+
+ handle_entry* lookupHandleLocked(int32_t handle);
+
+ String8 mDriverName;
+ int mDriverFD;
+ void* mVMStart;
+
+ // Protects thread count variable below.
+ pthread_mutex_t mThreadCountLock;
+ pthread_cond_t mThreadCountDecrement;
+ // Number of binder threads current executing a command.
+ size_t mExecutingThreadsCount;
+ // Maximum number for binder threads allowed for this process.
+ size_t mMaxThreads;
+ // Time when thread pool was emptied
+ int64_t mStarvationStartTimeMs;
+
+ mutable Mutex mLock; // protects everything below.
+
+ Vector<handle_entry>mHandleToObject;
+
+ bool mManagesContexts;
+ context_check_func mBinderContextCheckFunc;
+ void* mBinderContextUserData;
+
+ KeyedVector<String16, sp<IBinder> >
+ mContexts;
+
+
+ String8 mRootDir;
+ bool mThreadPoolStarted;
+ volatile int32_t mThreadPoolSeq;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PROCESS_STATE_H
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
new file mode 100644
index 0000000..3bfd462
--- /dev/null
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -0,0 +1,705 @@
+/*
+ * 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
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <cutils/compiler.h>
+
+// Set to 1 to enable CallStacks when logging errors
+#define SI_DUMP_CALLSTACKS 0
+#if SI_DUMP_CALLSTACKS
+#include <utils/CallStack.h>
+#endif
+
+#include <utils/NativeHandle.h>
+
+#include <functional>
+#include <type_traits>
+
+namespace android {
+namespace SafeInterface {
+
+// ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way
+class ParcelHandler {
+public:
+ explicit ParcelHandler(const char* logTag) : mLogTag(logTag) {}
+
+ // Specializations for types with dedicated handling in Parcel
+ status_t read(const Parcel& parcel, bool* b) const {
+ return callParcel("readBool", [&]() { return parcel.readBool(b); });
+ }
+ status_t write(Parcel* parcel, bool b) const {
+ return callParcel("writeBool", [&]() { return parcel->writeBool(b); });
+ }
+ template <typename E>
+ typename std::enable_if<std::is_enum<E>::value, status_t>::type read(const Parcel& parcel,
+ E* e) const {
+ typename std::underlying_type<E>::type u{};
+ status_t result = read(parcel, &u);
+ *e = static_cast<E>(u);
+ return result;
+ }
+ template <typename E>
+ typename std::enable_if<std::is_enum<E>::value, status_t>::type write(Parcel* parcel,
+ E e) const {
+ return write(parcel, static_cast<typename std::underlying_type<E>::type>(e));
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
+ const Parcel& parcel, T* t) const {
+ return callParcel("read(Flattenable)", [&]() { return parcel.read(*t); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write(
+ Parcel* parcel, const T& t) const {
+ return callParcel("write(Flattenable)", [&]() { return parcel->write(t); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
+ const Parcel& parcel, sp<T>* t) const {
+ *t = new T{};
+ return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write(
+ Parcel* parcel, const sp<T>& t) const {
+ return callParcel("write(sp<Flattenable>)", [&]() { return parcel->write(*(t.get())); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type read(
+ const Parcel& parcel, T* t) const {
+ return callParcel("read(LightFlattenable)", [&]() { return parcel.read(*t); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type write(
+ Parcel* parcel, const T& t) const {
+ return callParcel("write(LightFlattenable)", [&]() { return parcel->write(t); });
+ }
+ template <typename NH>
+ typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type read(
+ const Parcel& parcel, NH* nh) {
+ *nh = NativeHandle::create(parcel.readNativeHandle(), true);
+ return NO_ERROR;
+ }
+ template <typename NH>
+ typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type write(
+ Parcel* parcel, const NH& nh) {
+ return callParcel("write(sp<NativeHandle>)",
+ [&]() { return parcel->writeNativeHandle(nh->handle()); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read(
+ const Parcel& parcel, T* t) const {
+ return callParcel("readParcelable", [&]() { return parcel.readParcelable(t); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write(
+ Parcel* parcel, const T& t) const {
+ return callParcel("writeParcelable", [&]() { return parcel->writeParcelable(t); });
+ }
+ status_t read(const Parcel& parcel, String8* str) const {
+ return callParcel("readString8", [&]() { return parcel.readString8(str); });
+ }
+ status_t write(Parcel* parcel, const String8& str) const {
+ return callParcel("writeString8", [&]() { return parcel->writeString8(str); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type read(
+ const Parcel& parcel, sp<T>* pointer) const {
+ return callParcel("readNullableStrongBinder",
+ [&]() { return parcel.readNullableStrongBinder(pointer); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type write(
+ Parcel* parcel, const sp<T>& pointer) const {
+ return callParcel("writeStrongBinder",
+ [&]() { return parcel->writeStrongBinder(pointer); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type read(
+ const Parcel& parcel, sp<T>* pointer) const {
+ return callParcel("readNullableStrongBinder[IInterface]",
+ [&]() { return parcel.readNullableStrongBinder(pointer); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type write(
+ Parcel* parcel, const sp<T>& interface) const {
+ return write(parcel, IInterface::asBinder(interface));
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read(
+ const Parcel& parcel, std::vector<T>* v) const {
+ return callParcel("readParcelableVector", [&]() { return parcel.readParcelableVector(v); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write(
+ Parcel* parcel, const std::vector<T>& v) const {
+ return callParcel("writeParcelableVector",
+ [&]() { return parcel->writeParcelableVector(v); });
+ }
+
+ // Templates to handle integral types. We use a struct template to require that the called
+ // function exactly matches the signedness and size of the argument (e.g., the argument isn't
+ // silently widened).
+ template <bool isSigned, size_t size, typename I>
+ struct HandleInt;
+ template <typename I>
+ struct HandleInt<true, 4, I> {
+ static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+ return handler.callParcel("readInt32", [&]() { return parcel.readInt32(i); });
+ }
+ static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+ return handler.callParcel("writeInt32", [&]() { return parcel->writeInt32(i); });
+ }
+ };
+ template <typename I>
+ struct HandleInt<false, 4, I> {
+ static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+ return handler.callParcel("readUint32", [&]() { return parcel.readUint32(i); });
+ }
+ static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+ return handler.callParcel("writeUint32", [&]() { return parcel->writeUint32(i); });
+ }
+ };
+ template <typename I>
+ struct HandleInt<true, 8, I> {
+ static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+ return handler.callParcel("readInt64", [&]() { return parcel.readInt64(i); });
+ }
+ static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+ return handler.callParcel("writeInt64", [&]() { return parcel->writeInt64(i); });
+ }
+ };
+ template <typename I>
+ struct HandleInt<false, 8, I> {
+ static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+ return handler.callParcel("readUint64", [&]() { return parcel.readUint64(i); });
+ }
+ static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+ return handler.callParcel("writeUint64", [&]() { return parcel->writeUint64(i); });
+ }
+ };
+ template <typename I>
+ typename std::enable_if<std::is_integral<I>::value, status_t>::type read(const Parcel& parcel,
+ I* i) const {
+ return HandleInt<std::is_signed<I>::value, sizeof(I), I>::read(*this, parcel, i);
+ }
+ template <typename I>
+ typename std::enable_if<std::is_integral<I>::value, status_t>::type write(Parcel* parcel,
+ I i) const {
+ return HandleInt<std::is_signed<I>::value, sizeof(I), I>::write(*this, parcel, i);
+ }
+
+private:
+ const char* const mLogTag;
+
+ // Helper to encapsulate error handling while calling the various Parcel methods
+ template <typename Function>
+ status_t callParcel(const char* name, Function f) const {
+ status_t error = f();
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to %s, (%d: %s)", name, error, strerror(-error));
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ }
+ return error;
+ }
+};
+
+// Utility struct template which allows us to retrieve the types of the parameters of a member
+// function pointer
+template <typename T>
+struct ParamExtractor;
+template <typename Class, typename Return, typename... Params>
+struct ParamExtractor<Return (Class::*)(Params...)> {
+ using ParamTuple = std::tuple<Params...>;
+};
+template <typename Class, typename Return, typename... Params>
+struct ParamExtractor<Return (Class::*)(Params...) const> {
+ using ParamTuple = std::tuple<Params...>;
+};
+
+} // namespace SafeInterface
+
+template <typename Interface>
+class SafeBpInterface : public BpInterface<Interface> {
+protected:
+ SafeBpInterface(const sp<IBinder>& impl, const char* logTag)
+ : BpInterface<Interface>(impl), mLogTag(logTag) {}
+ ~SafeBpInterface() override = default;
+
+ // callRemote is used to invoke a synchronous procedure call over Binder
+ template <typename Method, typename TagType, typename... Args>
+ status_t callRemote(TagType tag, Args&&... args) const {
+ static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t");
+
+ // Verify that the arguments are compatible with the parameters
+ using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+ static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value,
+ "Invalid argument type");
+
+ // Write the input arguments to the data Parcel
+ Parcel data;
+ data.writeInterfaceToken(this->getInterfaceDescriptor());
+
+ status_t error = writeInputs(&data, std::forward<Args>(args)...);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by writeInputs
+ return error;
+ }
+
+ // Send the data Parcel to the remote and retrieve the reply parcel
+ Parcel reply;
+ error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ return error;
+ }
+
+ // Read the outputs from the reply Parcel into the output arguments
+ error = readOutputs(reply, std::forward<Args>(args)...);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by readOutputs
+ return error;
+ }
+
+ // Retrieve the result code from the reply Parcel
+ status_t result = NO_ERROR;
+ error = reply.readInt32(&result);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to obtain result");
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ return error;
+ }
+ return result;
+ }
+
+ // callRemoteAsync is used to invoke an asynchronous procedure call over Binder
+ template <typename Method, typename TagType, typename... Args>
+ void callRemoteAsync(TagType tag, Args&&... args) const {
+ static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t");
+
+ // Verify that the arguments are compatible with the parameters
+ using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+ static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value,
+ "Invalid argument type");
+
+ // Write the input arguments to the data Parcel
+ Parcel data;
+ data.writeInterfaceToken(this->getInterfaceDescriptor());
+ status_t error = writeInputs(&data, std::forward<Args>(args)...);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by writeInputs
+ return;
+ }
+
+ // There will be no data in the reply Parcel since the call is one-way
+ Parcel reply;
+ error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply,
+ IBinder::FLAG_ONEWAY);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ }
+ }
+
+private:
+ const char* const mLogTag;
+
+ // This struct provides information on whether the decayed types of the elements at Index in the
+ // tuple types T and U (that is, the types after stripping cv-qualifiers, removing references,
+ // and a few other less common operations) are the same
+ template <size_t Index, typename T, typename U>
+ struct DecayedElementsMatch {
+ private:
+ using FirstT = typename std::tuple_element<Index, T>::type;
+ using DecayedT = typename std::decay<FirstT>::type;
+ using FirstU = typename std::tuple_element<Index, U>::type;
+ using DecayedU = typename std::decay<FirstU>::type;
+
+ public:
+ static constexpr bool value = std::is_same<DecayedT, DecayedU>::value;
+ };
+
+ // When comparing whether the argument types match the parameter types, we first decay them (see
+ // DecayedElementsMatch) to avoid falsely flagging, say, T&& against T even though they are
+ // equivalent enough for our purposes
+ template <typename T, typename U>
+ struct ArgsMatchParams {};
+ template <typename... Args, typename... Params>
+ struct ArgsMatchParams<std::tuple<Args...>, std::tuple<Params...>> {
+ static_assert(sizeof...(Args) <= sizeof...(Params), "Too many arguments");
+ static_assert(sizeof...(Args) >= sizeof...(Params), "Not enough arguments");
+
+ private:
+ template <size_t Index>
+ static constexpr typename std::enable_if<(Index < sizeof...(Args)), bool>::type
+ elementsMatch() {
+ if (!DecayedElementsMatch<Index, std::tuple<Args...>, std::tuple<Params...>>::value) {
+ return false;
+ }
+ return elementsMatch<Index + 1>();
+ }
+ template <size_t Index>
+ static constexpr typename std::enable_if<(Index >= sizeof...(Args)), bool>::type
+ elementsMatch() {
+ return true;
+ }
+
+ public:
+ static constexpr bool value = elementsMatch<0>();
+ };
+
+ // Since we assume that pointer arguments are outputs, we can use this template struct to
+ // determine whether or not a given argument is fundamentally a pointer type and thus an output
+ template <typename T>
+ struct IsPointerIfDecayed {
+ private:
+ using Decayed = typename std::decay<T>::type;
+
+ public:
+ static constexpr bool value = std::is_pointer<Decayed>::value;
+ };
+
+ template <typename T>
+ typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type writeIfInput(
+ Parcel* data, T&& t) const {
+ return SafeInterface::ParcelHandler{mLogTag}.write(data, std::forward<T>(t));
+ }
+ template <typename T>
+ typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type writeIfInput(
+ Parcel* /*data*/, T&& /*t*/) const {
+ return NO_ERROR;
+ }
+
+ // This method iterates through all of the arguments, writing them to the data Parcel if they
+ // are an input (i.e., if they are not a pointer type)
+ template <typename T, typename... Remaining>
+ status_t writeInputs(Parcel* data, T&& t, Remaining&&... remaining) const {
+ status_t error = writeIfInput(data, std::forward<T>(t));
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by writeIfInput
+ return error;
+ }
+ return writeInputs(data, std::forward<Remaining>(remaining)...);
+ }
+ static status_t writeInputs(Parcel* /*data*/) { return NO_ERROR; }
+
+ template <typename T>
+ typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type readIfOutput(
+ const Parcel& reply, T&& t) const {
+ return SafeInterface::ParcelHandler{mLogTag}.read(reply, std::forward<T>(t));
+ }
+ template <typename T>
+ static typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type readIfOutput(
+ const Parcel& /*reply*/, T&& /*t*/) {
+ return NO_ERROR;
+ }
+
+ // Similar to writeInputs except that it reads output arguments from the reply Parcel
+ template <typename T, typename... Remaining>
+ status_t readOutputs(const Parcel& reply, T&& t, Remaining&&... remaining) const {
+ status_t error = readIfOutput(reply, std::forward<T>(t));
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by readIfOutput
+ return error;
+ }
+ return readOutputs(reply, std::forward<Remaining>(remaining)...);
+ }
+ static status_t readOutputs(const Parcel& /*data*/) { return NO_ERROR; }
+};
+
+template <typename Interface>
+class SafeBnInterface : public BnInterface<Interface> {
+public:
+ explicit SafeBnInterface(const char* logTag) : mLogTag(logTag) {}
+
+protected:
+ template <typename Method>
+ status_t callLocal(const Parcel& data, Parcel* reply, Method method) {
+ CHECK_INTERFACE(this, data, reply);
+
+ // Since we need to both pass inputs into the call as well as retrieve outputs, we create a
+ // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the
+ // outputs. When we ultimately call into the method, we will pass the addresses of the
+ // output arguments instead of their tuple members directly, but the storage will live in
+ // the tuple.
+ using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+ typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{};
+
+ // Read the inputs from the data Parcel into the argument tuple
+ status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by read
+ return error;
+ }
+
+ // Call the local method
+ status_t result = MethodCaller<ParamTuple>::call(this, method, &rawArgs);
+
+ // Extract the outputs from the argument tuple and write them into the reply Parcel
+ error = OutputWriter<ParamTuple>{mLogTag}.writeOutputs(reply, &rawArgs);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by write
+ return error;
+ }
+
+ // Return the result code in the reply Parcel
+ error = reply->writeInt32(result);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to write result");
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ return error;
+ }
+ return NO_ERROR;
+ }
+
+ template <typename Method>
+ status_t callLocalAsync(const Parcel& data, Parcel* /*reply*/, Method method) {
+ // reply is not actually used by CHECK_INTERFACE
+ CHECK_INTERFACE(this, data, reply);
+
+ // Since we need to both pass inputs into the call as well as retrieve outputs, we create a
+ // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the
+ // outputs. When we ultimately call into the method, we will pass the addresses of the
+ // output arguments instead of their tuple members directly, but the storage will live in
+ // the tuple.
+ using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+ typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{};
+
+ // Read the inputs from the data Parcel into the argument tuple
+ status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by read
+ return error;
+ }
+
+ // Call the local method
+ MethodCaller<ParamTuple>::callVoid(this, method, &rawArgs);
+
+ // After calling, there is nothing more to do since asynchronous calls do not return a value
+ // to the caller
+ return NO_ERROR;
+ }
+
+private:
+ const char* const mLogTag;
+
+ // RemoveFirst strips the first element from a tuple.
+ // For example, given T = std::tuple<A, B, C>, RemoveFirst<T>::type = std::tuple<B, C>
+ template <typename T, typename... Args>
+ struct RemoveFirst;
+ template <typename T, typename... Args>
+ struct RemoveFirst<std::tuple<T, Args...>> {
+ using type = std::tuple<Args...>;
+ };
+
+ // RawConverter strips a tuple down to its fundamental types, discarding both pointers and
+ // references. This allows us to allocate storage for both input (non-pointer) arguments and
+ // output (pointer) arguments in one tuple.
+ // For example, given T = std::tuple<const A&, B*>, RawConverter<T>::type = std::tuple<A, B>
+ template <typename Unconverted, typename... Converted>
+ struct RawConverter;
+ template <typename Unconverted, typename... Converted>
+ struct RawConverter<std::tuple<Converted...>, Unconverted> {
+ private:
+ using ElementType = typename std::tuple_element<0, Unconverted>::type;
+ using Decayed = typename std::decay<ElementType>::type;
+ using WithoutPointer = typename std::remove_pointer<Decayed>::type;
+
+ public:
+ using type = typename RawConverter<std::tuple<Converted..., WithoutPointer>,
+ typename RemoveFirst<Unconverted>::type>::type;
+ };
+ template <typename... Converted>
+ struct RawConverter<std::tuple<Converted...>, std::tuple<>> {
+ using type = std::tuple<Converted...>;
+ };
+
+ // This provides a simple way to determine whether the indexed element of Args... is a pointer
+ template <size_t I, typename... Args>
+ struct ElementIsPointer {
+ private:
+ using ElementType = typename std::tuple_element<I, std::tuple<Args...>>::type;
+
+ public:
+ static constexpr bool value = std::is_pointer<ElementType>::value;
+ };
+
+ // This class iterates over the parameter types, and if a given parameter is an input
+ // (i.e., is not a pointer), reads the corresponding argument tuple element from the data Parcel
+ template <typename... Params>
+ class InputReader;
+ template <typename... Params>
+ class InputReader<std::tuple<Params...>> {
+ public:
+ explicit InputReader(const char* logTag) : mLogTag(logTag) {}
+
+ // Note that in this case (as opposed to in SafeBpInterface), we iterate using an explicit
+ // index (starting with 0 here) instead of using recursion and stripping the first element.
+ // This is because in SafeBpInterface we aren't actually operating on a real tuple, but are
+ // instead just using a tuple as a convenient container for variadic types, whereas here we
+ // can't modify the argument tuple without causing unnecessary copies or moves of the data
+ // contained therein.
+ template <typename RawTuple>
+ status_t readInputs(const Parcel& data, RawTuple* args) {
+ return dispatchArg<0>(data, args);
+ }
+
+ private:
+ const char* const mLogTag;
+
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type readIfInput(
+ const Parcel& data, RawTuple* args) {
+ return SafeInterface::ParcelHandler{mLogTag}.read(data, &std::get<I>(*args));
+ }
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type readIfInput(
+ const Parcel& /*data*/, RawTuple* /*args*/) {
+ return NO_ERROR;
+ }
+
+ // Recursively iterate through the arguments
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
+ const Parcel& data, RawTuple* args) {
+ status_t error = readIfInput<I>(data, args);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged in read
+ return error;
+ }
+ return dispatchArg<I + 1>(data, args);
+ }
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg(
+ const Parcel& /*data*/, RawTuple* /*args*/) {
+ return NO_ERROR;
+ }
+ };
+
+ // getForCall uses the types of the parameters to determine whether a given element of the
+ // argument tuple is an input, which should be passed directly into the call, or an output, for
+ // which its address should be passed into the call
+ template <size_t I, typename RawTuple, typename... Params>
+ static typename std::enable_if<
+ ElementIsPointer<I, Params...>::value,
+ typename std::tuple_element<I, std::tuple<Params...>>::type>::type
+ getForCall(RawTuple* args) {
+ return &std::get<I>(*args);
+ }
+ template <size_t I, typename RawTuple, typename... Params>
+ static typename std::enable_if<
+ !ElementIsPointer<I, Params...>::value,
+ typename std::tuple_element<I, std::tuple<Params...>>::type>::type&
+ getForCall(RawTuple* args) {
+ return std::get<I>(*args);
+ }
+
+ // This template class uses std::index_sequence and parameter pack expansion to call the given
+ // method using the elements of the argument tuple (after those arguments are passed through
+ // getForCall to get addresses instead of values for output arguments)
+ template <typename... Params>
+ struct MethodCaller;
+ template <typename... Params>
+ struct MethodCaller<std::tuple<Params...>> {
+ public:
+ // The calls through these to the helper methods are necessary to generate the
+ // std::index_sequences used to unpack the argument tuple into the method call
+ template <typename Class, typename MemberFunction, typename RawTuple>
+ static status_t call(Class* instance, MemberFunction function, RawTuple* args) {
+ return callHelper(instance, function, args, std::index_sequence_for<Params...>{});
+ }
+ template <typename Class, typename MemberFunction, typename RawTuple>
+ static void callVoid(Class* instance, MemberFunction function, RawTuple* args) {
+ callVoidHelper(instance, function, args, std::index_sequence_for<Params...>{});
+ }
+
+ private:
+ template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I>
+ static status_t callHelper(Class* instance, MemberFunction function, RawTuple* args,
+ std::index_sequence<I...> /*unused*/) {
+ return (instance->*function)(getForCall<I, RawTuple, Params...>(args)...);
+ }
+ template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I>
+ static void callVoidHelper(Class* instance, MemberFunction function, RawTuple* args,
+ std::index_sequence<I...> /*unused*/) {
+ (instance->*function)(getForCall<I, RawTuple, Params...>(args)...);
+ }
+ };
+
+ // This class iterates over the parameter types, and if a given parameter is an output
+ // (i.e., is a pointer), writes the corresponding argument tuple element into the reply Parcel
+ template <typename... Params>
+ struct OutputWriter;
+ template <typename... Params>
+ struct OutputWriter<std::tuple<Params...>> {
+ public:
+ explicit OutputWriter(const char* logTag) : mLogTag(logTag) {}
+
+ // See the note on InputReader::readInputs for why this differs from the arguably simpler
+ // RemoveFirst approach in SafeBpInterface
+ template <typename RawTuple>
+ status_t writeOutputs(Parcel* reply, RawTuple* args) {
+ return dispatchArg<0>(reply, args);
+ }
+
+ private:
+ const char* const mLogTag;
+
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type
+ writeIfOutput(Parcel* reply, RawTuple* args) {
+ return SafeInterface::ParcelHandler{mLogTag}.write(reply, std::get<I>(*args));
+ }
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type
+ writeIfOutput(Parcel* /*reply*/, RawTuple* /*args*/) {
+ return NO_ERROR;
+ }
+
+ // Recursively iterate through the arguments
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
+ Parcel* reply, RawTuple* args) {
+ status_t error = writeIfOutput<I>(reply, args);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged in read
+ return error;
+ }
+ return dispatchArg<I + 1>(reply, args);
+ }
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg(
+ Parcel* /*reply*/, RawTuple* /*args*/) {
+ return NO_ERROR;
+ }
+ };
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
new file mode 100644
index 0000000..c3738f8
--- /dev/null
+++ b/libs/binder/include/binder/Status.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 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_BINDER_STATUS_H
+#define ANDROID_BINDER_STATUS_H
+
+#include <cstdint>
+#include <sstream>
+
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace binder {
+
+// An object similar in function to a status_t except that it understands
+// how exceptions are encoded in the prefix of a Parcel. Used like:
+//
+// Parcel data;
+// Parcel reply;
+// status_t status;
+// binder::Status remote_exception;
+// if ((status = data.writeInterfaceToken(interface_descriptor)) != OK ||
+// (status = data.writeInt32(function_input)) != OK) {
+// // We failed to write into the memory of our local parcel?
+// }
+// if ((status = remote()->transact(transaction, data, &reply)) != OK) {
+// // Something has gone wrong in the binder driver or libbinder.
+// }
+// if ((status = remote_exception.readFromParcel(reply)) != OK) {
+// // The remote didn't correctly write the exception header to the
+// // reply.
+// }
+// if (!remote_exception.isOk()) {
+// // The transaction went through correctly, but the remote reported an
+// // exception during handling.
+// }
+//
+class Status final {
+public:
+ // Keep the exception codes in sync with android/os/Parcel.java.
+ enum Exception {
+ EX_NONE = 0,
+ EX_SECURITY = -1,
+ EX_BAD_PARCELABLE = -2,
+ EX_ILLEGAL_ARGUMENT = -3,
+ EX_NULL_POINTER = -4,
+ EX_ILLEGAL_STATE = -5,
+ 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,
+ // This is special, and indicates to C++ binder proxies that the
+ // transaction has failed at a low level.
+ EX_TRANSACTION_FAILED = -129,
+ };
+
+ // A more readable alias for the default constructor.
+ static Status ok();
+
+ // Authors should explicitly pick whether their integer is:
+ // - an exception code (EX_* above)
+ // - service specific error code
+ // - status_t
+ //
+ // Prefer a generic exception code when possible, then a service specific
+ // code, and finally a status_t for low level failures or legacy support.
+ // Exception codes and service specific errors map to nicer exceptions for
+ // Java clients.
+ static Status fromExceptionCode(int32_t exceptionCode);
+ static Status fromExceptionCode(int32_t exceptionCode,
+ const String8& message);
+ static Status fromExceptionCode(int32_t exceptionCode,
+ const char* message);
+
+ static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode);
+ static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode,
+ const String8& message);
+ static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode,
+ const char* message);
+
+ static Status fromStatusT(status_t status);
+
+ Status() = default;
+ ~Status() = default;
+
+ // Status objects are copyable and contain just simple data.
+ Status(const Status& status) = default;
+ Status(Status&& status) = default;
+ Status& operator=(const Status& status) = default;
+
+ // Bear in mind that if the client or service is a Java endpoint, this
+ // is not the logic which will provide/interpret the data here.
+ status_t readFromParcel(const Parcel& parcel);
+ status_t writeToParcel(Parcel* parcel) const;
+
+ // Set one of the pre-defined exception types defined above.
+ void setException(int32_t ex, const String8& message);
+ // Set a service specific exception with error code.
+ void setServiceSpecificError(int32_t errorCode, const String8& message);
+ // Setting a |status| != OK causes generated code to return |status|
+ // from Binder transactions, rather than writing an exception into the
+ // reply Parcel. This is the least preferable way of reporting errors.
+ void setFromStatusT(status_t status);
+
+ // Get information about an exception.
+ int32_t exceptionCode() const { return mException; }
+ const String8& exceptionMessage() const { return mMessage; }
+ status_t transactionError() const {
+ return mException == EX_TRANSACTION_FAILED ? mErrorCode : OK;
+ }
+ int32_t serviceSpecificErrorCode() const {
+ return mException == EX_SERVICE_SPECIFIC ? mErrorCode : 0;
+ }
+
+ bool isOk() const { return mException == EX_NONE; }
+
+ // For logging.
+ String8 toString8() const;
+
+private:
+ Status(int32_t exceptionCode, int32_t errorCode);
+ Status(int32_t exceptionCode, int32_t errorCode, const String8& message);
+
+ // If |mException| == EX_TRANSACTION_FAILED, generated code will return
+ // |mErrorCode| as the result of the transaction rather than write an
+ // exception to the reply parcel.
+ //
+ // Otherwise, we always write |mException| to the parcel.
+ // If |mException| != EX_NONE, we write |mMessage| as well.
+ // If |mException| == EX_SERVICE_SPECIFIC we write |mErrorCode| as well.
+ int32_t mException = EX_NONE;
+ int32_t mErrorCode = 0;
+ String8 mMessage;
+}; // class Status
+
+// For gtest output logging
+std::stringstream& operator<< (std::stringstream& stream, const Status& s);
+
+} // namespace binder
+} // namespace android
+
+#endif // ANDROID_BINDER_STATUS_H
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
new file mode 100644
index 0000000..851e01f
--- /dev/null
+++ b/libs/binder/include/binder/TextOutput.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2006 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_TEXTOUTPUT_H
+#define ANDROID_TEXTOUTPUT_H
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <sstream>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class TextOutput
+{
+public:
+ TextOutput();
+ virtual ~TextOutput();
+
+ virtual status_t print(const char* txt, size_t len) = 0;
+ virtual void moveIndent(int delta) = 0;
+
+ class Bundle {
+ public:
+ inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); }
+ inline ~Bundle() { mTO.popBundle(); }
+ private:
+ TextOutput& mTO;
+ };
+
+ virtual void pushBundle() = 0;
+ virtual void popBundle() = 0;
+};
+
+// ---------------------------------------------------------------------------
+
+// Text output stream for printing to the log (via utils/Log.h).
+extern TextOutput& alog;
+
+// Text output stream for printing to stdout.
+extern TextOutput& aout;
+
+// Text output stream for printing to stderr.
+extern TextOutput& aerr;
+
+typedef TextOutput& (*TextOutputManipFunc)(TextOutput&);
+
+TextOutput& endl(TextOutput& to);
+TextOutput& indent(TextOutput& to);
+TextOutput& dedent(TextOutput& to);
+
+template<typename T>
+TextOutput& operator<<(TextOutput& to, const T& val)
+{
+ std::stringstream strbuf;
+ strbuf << val;
+ std::string str = strbuf.str();
+ to.print(str.c_str(), str.size());
+ return to;
+}
+
+TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
+
+class TypeCode
+{
+public:
+ inline TypeCode(uint32_t code);
+ inline ~TypeCode();
+
+ inline uint32_t typeCode() const;
+
+private:
+ uint32_t mCode;
+};
+
+TextOutput& operator<<(TextOutput& to, const TypeCode& val);
+
+class HexDump
+{
+public:
+ HexDump(const void *buf, size_t size, size_t bytesPerLine=16);
+ inline ~HexDump();
+
+ inline HexDump& setBytesPerLine(size_t bytesPerLine);
+ inline HexDump& setSingleLineCutoff(int32_t bytes);
+ inline HexDump& setAlignment(size_t alignment);
+ inline HexDump& setCArrayStyle(bool enabled);
+
+ inline const void* buffer() const;
+ inline size_t size() const;
+ inline size_t bytesPerLine() const;
+ inline int32_t singleLineCutoff() const;
+ inline size_t alignment() const;
+ inline bool carrayStyle() const;
+
+private:
+ const void* mBuffer;
+ size_t mSize;
+ size_t mBytesPerLine;
+ int32_t mSingleLineCutoff;
+ size_t mAlignment;
+ bool mCArrayStyle;
+};
+
+TextOutput& operator<<(TextOutput& to, const HexDump& val);
+inline TextOutput& operator<<(TextOutput& to,
+ decltype(std::endl<char,
+ std::char_traits<char>>)
+ /*val*/) {
+ endl(to);
+ return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const char &c)
+{
+ to.print(&c, 1);
+ return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const bool &val)
+{
+ if (val) to.print("true", 4);
+ else to.print("false", 5);
+ return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const String16& val)
+{
+ to << String8(val).string();
+ return to;
+}
+
+// ---------------------------------------------------------------------------
+// No user servicable parts below.
+
+inline TextOutput& endl(TextOutput& to)
+{
+ to.print("\n", 1);
+ return to;
+}
+
+inline TextOutput& indent(TextOutput& to)
+{
+ to.moveIndent(1);
+ return to;
+}
+
+inline TextOutput& dedent(TextOutput& to)
+{
+ to.moveIndent(-1);
+ return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func)
+{
+ return (*func)(to);
+}
+
+inline TypeCode::TypeCode(uint32_t code) : mCode(code) { }
+inline TypeCode::~TypeCode() { }
+inline uint32_t TypeCode::typeCode() const { return mCode; }
+
+inline HexDump::~HexDump() { }
+
+inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) {
+ mBytesPerLine = bytesPerLine; return *this;
+}
+inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) {
+ mSingleLineCutoff = bytes; return *this;
+}
+inline HexDump& HexDump::setAlignment(size_t alignment) {
+ mAlignment = alignment; return *this;
+}
+inline HexDump& HexDump::setCArrayStyle(bool enabled) {
+ mCArrayStyle = enabled; return *this;
+}
+
+inline const void* HexDump::buffer() const { return mBuffer; }
+inline size_t HexDump::size() const { return mSize; }
+inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; }
+inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; }
+inline size_t HexDump::alignment() const { return mAlignment; }
+inline bool HexDump::carrayStyle() const { return mCArrayStyle; }
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_TEXTOUTPUT_H
diff --git a/libs/binder/include/binder/Value.h b/libs/binder/include/binder/Value.h
new file mode 100644
index 0000000..4dee3d8
--- /dev/null
+++ b/libs/binder/include/binder/Value.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015 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_VALUE_H
+#define ANDROID_VALUE_H
+
+#include <stdint.h>
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <binder/Map.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Parcel;
+
+namespace binder {
+
+/**
+ * A limited C++ generic type. The purpose of this class is to allow C++
+ * programs to make use of (or implement) Binder interfaces which make use
+ * the Java "Object" generic type (either via the use of the Map type or
+ * some other mechanism).
+ *
+ * This class only supports a limited set of types, but additional types
+ * may be easily added to this class in the future as needed---without
+ * breaking binary compatability.
+ *
+ * This class was written in such a way as to help avoid type errors by
+ * giving each type their own explicity-named accessor methods (rather than
+ * overloaded methods).
+ *
+ * When reading or writing this class to a Parcel, use the `writeValue()`
+ * and `readValue()` methods.
+ */
+class Value {
+public:
+ Value();
+ virtual ~Value();
+
+ Value& swap(Value &);
+
+ bool empty() const;
+
+ void clear();
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+ const std::type_info& type() const;
+#endif
+
+ int32_t parcelType() const;
+
+ bool operator==(const Value& rhs) const;
+ bool operator!=(const Value& rhs) const { return !this->operator==(rhs); }
+
+ Value(const Value& value);
+ Value(const bool& value);
+ Value(const int8_t& value);
+ Value(const int32_t& value);
+ Value(const int64_t& value);
+ Value(const double& value);
+ Value(const String16& value);
+ Value(const std::vector<bool>& value);
+ Value(const std::vector<uint8_t>& value);
+ Value(const std::vector<int32_t>& value);
+ Value(const std::vector<int64_t>& value);
+ Value(const std::vector<double>& value);
+ Value(const std::vector<String16>& value);
+ Value(const os::PersistableBundle& value);
+ Value(const binder::Map& value);
+
+ Value& operator=(const Value& rhs);
+ Value& operator=(const int8_t& rhs);
+ Value& operator=(const bool& rhs);
+ Value& operator=(const int32_t& rhs);
+ Value& operator=(const int64_t& rhs);
+ Value& operator=(const double& rhs);
+ Value& operator=(const String16& rhs);
+ Value& operator=(const std::vector<bool>& rhs);
+ Value& operator=(const std::vector<uint8_t>& rhs);
+ Value& operator=(const std::vector<int32_t>& rhs);
+ Value& operator=(const std::vector<int64_t>& rhs);
+ Value& operator=(const std::vector<double>& rhs);
+ Value& operator=(const std::vector<String16>& rhs);
+ Value& operator=(const os::PersistableBundle& rhs);
+ Value& operator=(const binder::Map& rhs);
+
+ void putBoolean(const bool& value);
+ void putByte(const int8_t& value);
+ void putInt(const int32_t& value);
+ void putLong(const int64_t& value);
+ void putDouble(const double& value);
+ void putString(const String16& value);
+ void putBooleanVector(const std::vector<bool>& value);
+ void putByteVector(const std::vector<uint8_t>& value);
+ void putIntVector(const std::vector<int32_t>& value);
+ void putLongVector(const std::vector<int64_t>& value);
+ void putDoubleVector(const std::vector<double>& value);
+ void putStringVector(const std::vector<String16>& value);
+ void putPersistableBundle(const os::PersistableBundle& value);
+ void putMap(const binder::Map& value);
+
+ bool getBoolean(bool* out) const;
+ bool getByte(int8_t* out) const;
+ bool getInt(int32_t* out) const;
+ bool getLong(int64_t* out) const;
+ bool getDouble(double* out) const;
+ bool getString(String16* out) const;
+ bool getBooleanVector(std::vector<bool>* out) const;
+ bool getByteVector(std::vector<uint8_t>* out) const;
+ bool getIntVector(std::vector<int32_t>* out) const;
+ bool getLongVector(std::vector<int64_t>* out) const;
+ bool getDoubleVector(std::vector<double>* out) const;
+ bool getStringVector(std::vector<String16>* out) const;
+ bool getPersistableBundle(os::PersistableBundle* out) const;
+ bool getMap(binder::Map* out) const;
+
+ bool isBoolean() const;
+ bool isByte() const;
+ bool isInt() const;
+ bool isLong() const;
+ bool isDouble() const;
+ bool isString() const;
+ bool isBooleanVector() const;
+ bool isByteVector() const;
+ bool isIntVector() const;
+ bool isLongVector() const;
+ bool isDoubleVector() const;
+ bool isStringVector() const;
+ bool isPersistableBundle() const;
+ bool isMap() const;
+
+ // String Convenience Adapters
+ // ---------------------------
+
+ Value(const String8& value): Value(String16(value)) { }
+ Value(const ::std::string& value): Value(String8(value.c_str())) { }
+ void putString(const String8& value) { return putString(String16(value)); }
+ void putString(const ::std::string& value) { return putString(String8(value.c_str())); }
+ Value& operator=(const String8& rhs) { return *this = String16(rhs); }
+ Value& operator=(const ::std::string& rhs) { return *this = String8(rhs.c_str()); }
+ bool getString(String8* out) const;
+ bool getString(::std::string* out) const;
+
+private:
+
+ // This allows ::android::Parcel to call the two methods below.
+ friend class ::android::Parcel;
+
+ // This is called by ::android::Parcel::writeValue()
+ status_t writeToParcel(Parcel* parcel) const;
+
+ // This is called by ::android::Parcel::readValue()
+ status_t readFromParcel(const Parcel* parcel);
+
+ template<typename T> class Content;
+ class ContentBase;
+
+ ContentBase* mContent;
+};
+
+} // namespace binder
+
+} // namespace android
+
+#endif // ANDROID_VALUE_H
diff --git a/libs/binder/include/private/binder/ParcelValTypes.h b/libs/binder/include/private/binder/ParcelValTypes.h
new file mode 100644
index 0000000..666d22a
--- /dev/null
+++ b/libs/binder/include/private/binder/ParcelValTypes.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+namespace android {
+namespace binder {
+
+// Keep in sync with frameworks/base/core/java/android/os/Parcel.java.
+enum {
+ VAL_NULL = -1,
+ VAL_STRING = 0,
+ VAL_INTEGER = 1,
+ VAL_MAP = 2,
+ VAL_BUNDLE = 3,
+ VAL_PARCELABLE = 4,
+ VAL_SHORT = 5,
+ VAL_LONG = 6,
+ VAL_DOUBLE = 8,
+ VAL_BOOLEAN = 9,
+ VAL_BYTEARRAY = 13,
+ VAL_STRINGARRAY = 14,
+ VAL_IBINDER = 15,
+ VAL_INTARRAY = 18,
+ VAL_LONGARRAY = 19,
+ VAL_BYTE = 20,
+ VAL_SERIALIZABLE = 21,
+ VAL_BOOLEANARRAY = 23,
+ VAL_PERSISTABLEBUNDLE = 25,
+ VAL_DOUBLEARRAY = 28,
+};
+
+} // namespace binder
+} // namespace android
diff --git a/libs/binder/include/private/binder/Static.h b/libs/binder/include/private/binder/Static.h
new file mode 100644
index 0000000..3d10456
--- /dev/null
+++ b/libs/binder/include/private/binder/Static.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+// All static variables go here, to control initialization and
+// destruction order in the library.
+
+#include <utils/threads.h>
+
+#include <binder/IBinder.h>
+#include <binder/ProcessState.h>
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// For TextStream.cpp
+extern Vector<int32_t> gTextBuffers;
+
+// For ProcessState.cpp
+extern Mutex gProcessMutex;
+extern sp<ProcessState> gProcess;
+
+// For IServiceManager.cpp
+extern Mutex gDefaultServiceManagerLock;
+extern sp<IServiceManager> gDefaultServiceManager;
+extern sp<IPermissionController> gPermissionController;
+
+} // namespace android
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
new file mode 100644
index 0000000..2f11622
--- /dev/null
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 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 _BINDER_MODULE_H_
+#define _BINDER_MODULE_H_
+
+#ifdef __cplusplus
+namespace android {
+#endif
+
+/* obtain structures and constants from the kernel header */
+
+#include <sys/ioctl.h>
+#include <linux/android/binder.h>
+
+#ifdef __cplusplus
+} // namespace android
+#endif
+
+#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
new file mode 100644
index 0000000..3071408
--- /dev/null
+++ b/libs/binder/tests/Android.bp
@@ -0,0 +1,131 @@
+//
+// Copyright (C) 2014 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.
+//
+
+cc_test {
+ name: "binderDriverInterfaceTest_IPC_32",
+ srcs: ["binderDriverInterfaceTest.cpp"],
+ compile_multilib: "32",
+ cflags: ["-DBINDER_IPC_32BIT=1"],
+}
+
+cc_test {
+ product_variables: {
+ binder32bit: {
+ cflags: ["-DBINDER_IPC_32BIT=1"],
+ },
+ },
+
+ name: "binderDriverInterfaceTest",
+ srcs: ["binderDriverInterfaceTest.cpp"],
+}
+
+cc_test {
+ name: "binderValueTypeTest",
+ srcs: ["binderValueTypeTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+}
+
+cc_test {
+ name: "binderLibTest_IPC_32",
+ srcs: ["binderLibTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ compile_multilib: "32",
+ cflags: ["-DBINDER_IPC_32BIT=1"],
+}
+
+cc_test {
+ product_variables: {
+ binder32bit: {
+ cflags: ["-DBINDER_IPC_32BIT=1"],
+ },
+ },
+
+ name: "binderLibTest",
+ srcs: ["binderLibTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+}
+
+cc_test {
+ name: "binderThroughputTest",
+ srcs: ["binderThroughputTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ clang: true,
+ cflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-sign-compare",
+ "-O3",
+ ],
+}
+
+cc_test {
+ name: "binderTextOutputTest",
+ srcs: ["binderTextOutputTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libbase",
+ ],
+}
+
+cc_test {
+ name: "schd-dbg",
+ srcs: ["schd-dbg.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libbase",
+ ],
+}
+
+cc_test {
+ name: "binderSafeInterfaceTest",
+ srcs: ["binderSafeInterfaceTest.cpp"],
+
+ cppflags: [
+ "-Werror",
+ "-Weverything",
+ "-Wno-c++98-compat",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-global-constructors",
+ "-Wno-padded",
+ "-Wno-weak-vtables",
+ ],
+
+ cpp_std: "experimental",
+ gnu_extensions: false,
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/libs/binder/tests/Android.mk b/libs/binder/tests/Android.mk
deleted file mode 100644
index a40523d..0000000
--- a/libs/binder/tests/Android.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-LOCAL_CFLAGS += -DBINDER_IPC_32BIT=1
-endif
-endif
-
-LOCAL_MODULE := binderDriverInterfaceTest
-LOCAL_SRC_FILES := binderDriverInterfaceTest.cpp
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := binderLibTest
-LOCAL_SRC_FILES := binderLibTest.cpp
-LOCAL_SHARED_LIBRARIES := libbinder libutils
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := binderThroughputTest
-LOCAL_SRC_FILES := binderThroughputTest.cpp
-LOCAL_SHARED_LIBRARIES := libbinder libutils
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -g -Wall -Werror -std=c++11 -Wno-missing-field-initializers -Wno-sign-compare -O3
-include $(BUILD_NATIVE_TEST)
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index 0277550..ff5912f 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -20,7 +20,7 @@
#include <stdlib.h>
#include <gtest/gtest.h>
-#include <linux/binder.h>
+#include <linux/android/binder.h>
#include <binder/IBinder.h>
#include <sys/mman.h>
#include <poll.h>
@@ -229,7 +229,9 @@
.sender_euid = 0,
.data_size = 0,
.offsets_size = 0,
- .data = {0, 0},
+ .data = {
+ .ptr = {0, 0},
+ },
},
};
struct {
@@ -350,4 +352,3 @@
return RUN_ALL_TESTS();
}
-
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 3df3acf..a04869a 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -34,6 +34,7 @@
static testing::Environment* binder_env;
static char *binderservername;
+static char *binderserversuffix;
static char binderserverarg[] = "--binderserver";
static String16 binderLibTestServiceName = String16("test.binderLib");
@@ -44,6 +45,7 @@
BINDER_LIB_TEST_ADD_SERVER,
BINDER_LIB_TEST_CALL_BACK,
BINDER_LIB_TEST_NOP_CALL_BACK,
+ BINDER_LIB_TEST_GET_SELF_TRANSACTION,
BINDER_LIB_TEST_GET_ID_TRANSACTION,
BINDER_LIB_TEST_INDIRECT_TRANSACTION,
BINDER_LIB_TEST_SET_ERROR_TRANSACTION,
@@ -55,6 +57,7 @@
BINDER_LIB_TEST_EXIT_TRANSACTION,
BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
+ BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
};
pid_t start_server_process(int arg2)
@@ -70,6 +73,7 @@
binderserverarg,
stri,
strpipefd1,
+ binderserversuffix,
NULL
};
@@ -252,14 +256,10 @@
int ret;
pthread_mutex_lock(&m_waitMutex);
if (!m_eventTriggered) {
-#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
- pthread_cond_timeout_np(&m_waitCond, &m_waitMutex, timeout_s * 1000);
-#else
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += timeout_s;
pthread_cond_timedwait(&m_waitCond, &m_waitMutex, &ts);
-#endif
}
ret = m_eventTriggered ? NO_ERROR : TIMED_OUT;
pthread_mutex_unlock(&m_waitMutex);
@@ -391,7 +391,7 @@
ret = reply.readInt32(&count);
ASSERT_EQ(NO_ERROR, ret);
- EXPECT_EQ(ARRAY_SIZE(serverId), count);
+ EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
BinderLibTestBundle replyi(&reply);
@@ -441,7 +441,7 @@
ret = reply.readInt32(&count);
ASSERT_EQ(NO_ERROR, ret);
- EXPECT_EQ(ARRAY_SIZE(serverId), count);
+ EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
int32_t counti;
@@ -633,7 +633,7 @@
}
ret = read(pipefd[0], buf, sizeof(buf));
- EXPECT_EQ(sizeof(buf), ret);
+ EXPECT_EQ(sizeof(buf), (size_t)ret);
EXPECT_EQ(write_value, buf[0]);
waitForReadData(pipefd[0], 5000); /* wait for other proccess to close pipe */
@@ -672,6 +672,62 @@
EXPECT_GE(ret, 0);
}
+TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
+ status_t ret;
+ Parcel data, reply;
+
+ ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ const flat_binder_object *fb = reply.readObject(false);
+ ASSERT_TRUE(fb != NULL);
+ EXPECT_EQ(BINDER_TYPE_HANDLE, fb->type);
+ EXPECT_EQ(m_server, ProcessState::self()->getStrongProxyForHandle(fb->handle));
+ EXPECT_EQ((binder_uintptr_t)0, fb->cookie);
+ EXPECT_EQ((uint64_t)0, (uint64_t)fb->binder >> 32);
+}
+
+TEST_F(BinderLibTest, FreedBinder) {
+ status_t ret;
+
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != NULL);
+
+ __u32 freedHandle;
+ wp<IBinder> keepFreedBinder;
+ {
+ Parcel data, reply;
+ data.writeBool(false); /* request weak reference */
+ ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply);
+ ASSERT_EQ(NO_ERROR, ret);
+ struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data());
+ freedHandle = freed->handle;
+ /* Add a weak ref to the freed binder so the driver does not
+ * delete its reference to it - otherwise the transaction
+ * fails regardless of whether the driver is fixed.
+ */
+ keepFreedBinder = reply.readWeakBinder();
+ }
+ {
+ Parcel data, reply;
+ data.writeStrongBinder(server);
+ /* Replace original handle with handle to the freed binder */
+ struct flat_binder_object *strong = (struct flat_binder_object *)(data.data());
+ __u32 oldHandle = strong->handle;
+ strong->handle = freedHandle;
+ ret = server->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply);
+ /* Returns DEAD_OBJECT (-32) if target crashes and
+ * FAILED_TRANSACTION if the driver rejects the invalid
+ * object.
+ */
+ EXPECT_EQ((status_t)FAILED_TRANSACTION, ret);
+ /* Restore original handle so parcel destructor does not use
+ * the wrong handle.
+ */
+ strong->handle = oldHandle;
+ }
+}
+
class BinderLibTestService : public BBinder
{
public:
@@ -739,14 +795,10 @@
}
if (ret > 0) {
if (m_serverStartRequested) {
-#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
- ret = pthread_cond_timeout_np(&m_serverWaitCond, &m_serverWaitMutex, 5000);
-#else
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
ret = pthread_cond_timedwait(&m_serverWaitCond, &m_serverWaitMutex, &ts);
-#endif
}
if (m_serverStartRequested) {
m_serverStartRequested = false;
@@ -777,6 +829,9 @@
binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_SELF_TRANSACTION:
+ reply->writeStrongBinder(this);
+ return NO_ERROR;
case BINDER_LIB_TEST_GET_ID_TRANSACTION:
reply->writeInt32(m_id);
return NO_ERROR;
@@ -890,6 +945,16 @@
while (wait(NULL) != -1 || errno != ECHILD)
;
exit(EXIT_SUCCESS);
+ case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: {
+ bool strongRef = data.readBool();
+ sp<IBinder> binder = new BBinder();
+ if (strongRef) {
+ reply->writeStrongBinder(binder);
+ } else {
+ reply->writeWeakBinder(binder);
+ }
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
};
@@ -906,6 +971,8 @@
int run_server(int index, int readypipefd)
{
+ binderLibTestServiceName += String16(binderserversuffix);
+
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
{
@@ -936,15 +1003,19 @@
int main(int argc, char **argv) {
int ret;
- if (argc == 3 && !strcmp(argv[1], "--servername")) {
+ if (argc == 4 && !strcmp(argv[1], "--servername")) {
binderservername = argv[2];
} else {
binderservername = argv[0];
}
- if (argc == 4 && !strcmp(argv[1], binderserverarg)) {
+ if (argc == 5 && !strcmp(argv[1], binderserverarg)) {
+ binderserversuffix = argv[4];
return run_server(atoi(argv[2]), atoi(argv[3]));
}
+ binderserversuffix = new char[16];
+ snprintf(binderserversuffix, 16, "%d", getpid());
+ binderLibTestServiceName += String16(binderserversuffix);
::testing::InitGoogleTest(&argc, argv);
binder_env = AddGlobalTestEnvironment(new BinderLibTestEnv());
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
new file mode 100644
index 0000000..6a16e24
--- /dev/null
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -0,0 +1,819 @@
+/*
+ * 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.
+ */
+
+#include <binder/SafeInterface.h>
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/ProcessState.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#include <gtest/gtest.h>
+#pragma clang diagnostic pop
+
+#include <utils/LightRefBase.h>
+#include <utils/NativeHandle.h>
+
+#include <cutils/native_handle.h>
+
+#include <optional>
+
+#include <sys/eventfd.h>
+
+using namespace std::chrono_literals; // NOLINT - google-build-using-namespace
+
+namespace android {
+namespace tests {
+
+enum class TestEnum : uint32_t {
+ INVALID = 0,
+ INITIAL = 1,
+ FINAL = 2,
+};
+
+// This class serves two purposes:
+// 1) It ensures that the implementation doesn't require copying or moving the data (for
+// efficiency purposes)
+// 2) It tests that Parcelables can be passed correctly
+class NoCopyNoMove : public Parcelable {
+public:
+ NoCopyNoMove() = default;
+ explicit NoCopyNoMove(int32_t value) : mValue(value) {}
+ ~NoCopyNoMove() override = default;
+
+ // Not copyable
+ NoCopyNoMove(const NoCopyNoMove&) = delete;
+ NoCopyNoMove& operator=(const NoCopyNoMove&) = delete;
+
+ // Not movable
+ NoCopyNoMove(NoCopyNoMove&&) = delete;
+ NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;
+
+ // Parcelable interface
+ status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); }
+ status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); }
+
+ int32_t getValue() const { return mValue; }
+ void setValue(int32_t value) { mValue = value; }
+
+private:
+ int32_t mValue = 0;
+ uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded
+};
+
+struct TestFlattenable : Flattenable<TestFlattenable> {
+ TestFlattenable() = default;
+ explicit TestFlattenable(int32_t v) : value(v) {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const { return sizeof(value); }
+ size_t getFdCount() const { return 0; }
+ status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const {
+ FlattenableUtils::write(buffer, size, value);
+ return NO_ERROR;
+ }
+ status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+ FlattenableUtils::read(buffer, size, value);
+ return NO_ERROR;
+ }
+
+ int32_t value = 0;
+};
+
+struct TestLightFlattenable : LightFlattenablePod<TestLightFlattenable> {
+ TestLightFlattenable() = default;
+ explicit TestLightFlattenable(int32_t v) : value(v) {}
+ int32_t value = 0;
+};
+
+// It seems like this should be able to inherit from TestFlattenable (to avoid duplicating code),
+// but the SafeInterface logic can't easily be extended to find an indirect Flattenable<T>
+// base class
+class TestLightRefBaseFlattenable : public Flattenable<TestLightRefBaseFlattenable>,
+ public LightRefBase<TestLightRefBaseFlattenable> {
+public:
+ TestLightRefBaseFlattenable() = default;
+ explicit TestLightRefBaseFlattenable(int32_t v) : value(v) {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const { return sizeof(value); }
+ size_t getFdCount() const { return 0; }
+ status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const {
+ FlattenableUtils::write(buffer, size, value);
+ return NO_ERROR;
+ }
+ status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+ FlattenableUtils::read(buffer, size, value);
+ return NO_ERROR;
+ }
+
+ int32_t value = 0;
+};
+
+class TestParcelable : public Parcelable {
+public:
+ TestParcelable() = default;
+ explicit TestParcelable(int32_t value) : mValue(value) {}
+ TestParcelable(const TestParcelable& other) : TestParcelable(other.mValue) {}
+ TestParcelable(TestParcelable&& other) : TestParcelable(other.mValue) {}
+
+ // Parcelable interface
+ status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); }
+ status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); }
+
+ int32_t getValue() const { return mValue; }
+ void setValue(int32_t value) { mValue = value; }
+
+private:
+ int32_t mValue = 0;
+};
+
+class ExitOnDeath : public IBinder::DeathRecipient {
+public:
+ ~ExitOnDeath() override = default;
+
+ void binderDied(const wp<IBinder>& /*who*/) override {
+ ALOG(LOG_INFO, "ExitOnDeath", "Exiting");
+ exit(0);
+ }
+};
+
+// This callback class is used to test both one-way transactions and that sp<IInterface> can be
+// passed correctly
+class ICallback : public IInterface {
+public:
+ DECLARE_META_INTERFACE(Callback)
+
+ enum class Tag : uint32_t {
+ OnCallback = IBinder::FIRST_CALL_TRANSACTION,
+ Last,
+ };
+
+ virtual void onCallback(int32_t aPlusOne) = 0;
+};
+
+class BpCallback : public SafeBpInterface<ICallback> {
+public:
+ explicit BpCallback(const sp<IBinder>& impl) : SafeBpInterface<ICallback>(impl, getLogTag()) {}
+
+ void onCallback(int32_t aPlusOne) override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemoteAsync<decltype(&ICallback::onCallback)>(Tag::OnCallback, aPlusOne);
+ }
+
+private:
+ static constexpr const char* getLogTag() { return "BpCallback"; }
+};
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback");
+#pragma clang diagnostic pop
+
+class BnCallback : public SafeBnInterface<ICallback> {
+public:
+ BnCallback() : SafeBnInterface("BnCallback") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t /*flags*/) override {
+ EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+ EXPECT_LT(code, static_cast<uint32_t>(ICallback::Tag::Last));
+ ICallback::Tag tag = static_cast<ICallback::Tag>(code);
+ switch (tag) {
+ case ICallback::Tag::OnCallback: {
+ return callLocalAsync(data, reply, &ICallback::onCallback);
+ }
+ case ICallback::Tag::Last:
+ // Should not be possible because of the asserts at the beginning of the method
+ [&]() { FAIL(); }();
+ return UNKNOWN_ERROR;
+ }
+ }
+};
+
+class ISafeInterfaceTest : public IInterface {
+public:
+ DECLARE_META_INTERFACE(SafeInterfaceTest)
+
+ enum class Tag : uint32_t {
+ SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
+ ReturnsNoMemory,
+ LogicalNot,
+ ModifyEnum,
+ IncrementFlattenable,
+ IncrementLightFlattenable,
+ IncrementLightRefBaseFlattenable,
+ IncrementNativeHandle,
+ IncrementNoCopyNoMove,
+ IncrementParcelableVector,
+ ToUpper,
+ CallMeBack,
+ IncrementInt32,
+ IncrementUint32,
+ IncrementInt64,
+ IncrementUint64,
+ IncrementTwo,
+ Last,
+ };
+
+ // This is primarily so that the remote service dies when the test does, but it also serves to
+ // test the handling of sp<IBinder> and non-const methods
+ virtual status_t setDeathToken(const sp<IBinder>& token) = 0;
+
+ // This is the most basic test since it doesn't require parceling any arguments
+ virtual status_t returnsNoMemory() const = 0;
+
+ // These are ordered according to their corresponding methods in SafeInterface::ParcelHandler
+ virtual status_t logicalNot(bool a, bool* notA) const = 0;
+ virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0;
+ virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0;
+ virtual status_t increment(const TestLightFlattenable& a,
+ TestLightFlattenable* aPlusOne) const = 0;
+ virtual status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+ sp<TestLightRefBaseFlattenable>* aPlusOne) const = 0;
+ virtual status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const = 0;
+ virtual status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const = 0;
+ virtual status_t increment(const std::vector<TestParcelable>& a,
+ std::vector<TestParcelable>* aPlusOne) const = 0;
+ virtual status_t toUpper(const String8& str, String8* upperStr) const = 0;
+ // As mentioned above, sp<IBinder> is already tested by setDeathToken
+ virtual void callMeBack(const sp<ICallback>& callback, int32_t a) const = 0;
+ virtual status_t increment(int32_t a, int32_t* aPlusOne) const = 0;
+ virtual status_t increment(uint32_t a, uint32_t* aPlusOne) const = 0;
+ virtual status_t increment(int64_t a, int64_t* aPlusOne) const = 0;
+ virtual status_t increment(uint64_t a, uint64_t* aPlusOne) const = 0;
+
+ // This tests that input/output parameter interleaving works correctly
+ virtual status_t increment(int32_t a, int32_t* aPlusOne, int32_t b,
+ int32_t* bPlusOne) const = 0;
+};
+
+class BpSafeInterfaceTest : public SafeBpInterface<ISafeInterfaceTest> {
+public:
+ explicit BpSafeInterfaceTest(const sp<IBinder>& impl)
+ : SafeBpInterface<ISafeInterfaceTest>(impl, getLogTag()) {}
+
+ status_t setDeathToken(const sp<IBinder>& token) override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::setDeathToken)>(Tag::SetDeathToken, token);
+ }
+ status_t returnsNoMemory() const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::returnsNoMemory)>(Tag::ReturnsNoMemory);
+ }
+ status_t logicalNot(bool a, bool* notA) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::logicalNot)>(Tag::LogicalNot, a, notA);
+ }
+ status_t modifyEnum(TestEnum a, TestEnum* b) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::modifyEnum)>(Tag::ModifyEnum, a, b);
+ }
+ status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override {
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const TestFlattenable&, TestFlattenable*) const;
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<Signature>(Tag::IncrementFlattenable, a, aPlusOne);
+ }
+ status_t increment(const TestLightFlattenable& a,
+ TestLightFlattenable* aPlusOne) const override {
+ using Signature = status_t (ISafeInterfaceTest::*)(const TestLightFlattenable&,
+ TestLightFlattenable*) const;
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<Signature>(Tag::IncrementLightFlattenable, a, aPlusOne);
+ }
+ status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+ sp<TestLightRefBaseFlattenable>* aPlusOne) const override {
+ using Signature = status_t (ISafeInterfaceTest::*)(const sp<TestLightRefBaseFlattenable>&,
+ sp<TestLightRefBaseFlattenable>*) const;
+ return callRemote<Signature>(Tag::IncrementLightRefBaseFlattenable, a, aPlusOne);
+ }
+ status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const sp<NativeHandle>&, sp<NativeHandle>*) const;
+ return callRemote<Signature>(Tag::IncrementNativeHandle, a, aPlusOne);
+ }
+ status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a,
+ NoCopyNoMove* aPlusOne) const;
+ return callRemote<Signature>(Tag::IncrementNoCopyNoMove, a, aPlusOne);
+ }
+ status_t increment(const std::vector<TestParcelable>& a,
+ std::vector<TestParcelable>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<TestParcelable>&,
+ std::vector<TestParcelable>*);
+ return callRemote<Signature>(Tag::IncrementParcelableVector, a, aPlusOne);
+ }
+ status_t toUpper(const String8& str, String8* upperStr) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::toUpper)>(Tag::ToUpper, str, upperStr);
+ }
+ void callMeBack(const sp<ICallback>& callback, int32_t a) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemoteAsync<decltype(&ISafeInterfaceTest::callMeBack)>(Tag::CallMeBack, callback,
+ a);
+ }
+ status_t increment(int32_t a, int32_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const;
+ return callRemote<Signature>(Tag::IncrementInt32, a, aPlusOne);
+ }
+ status_t increment(uint32_t a, uint32_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const;
+ return callRemote<Signature>(Tag::IncrementUint32, a, aPlusOne);
+ }
+ status_t increment(int64_t a, int64_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const;
+ return callRemote<Signature>(Tag::IncrementInt64, a, aPlusOne);
+ }
+ status_t increment(uint64_t a, uint64_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
+ return callRemote<Signature>(Tag::IncrementUint64, a, aPlusOne);
+ }
+ status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t, int32_t*) const;
+ return callRemote<Signature>(Tag::IncrementTwo, a, aPlusOne, b, bPlusOne);
+ }
+
+private:
+ static constexpr const char* getLogTag() { return "BpSafeInterfaceTest"; }
+};
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest");
+
+static sp<IBinder::DeathRecipient> getDeathRecipient() {
+ static sp<IBinder::DeathRecipient> recipient = new ExitOnDeath;
+ return recipient;
+}
+#pragma clang diagnostic pop
+
+class BnSafeInterfaceTest : public SafeBnInterface<ISafeInterfaceTest> {
+public:
+ BnSafeInterfaceTest() : SafeBnInterface(getLogTag()) {}
+
+ status_t setDeathToken(const sp<IBinder>& token) override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ token->linkToDeath(getDeathRecipient());
+ return NO_ERROR;
+ }
+ status_t returnsNoMemory() const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return NO_MEMORY;
+ }
+ status_t logicalNot(bool a, bool* notA) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *notA = !a;
+ return NO_ERROR;
+ }
+ status_t modifyEnum(TestEnum a, TestEnum* b) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID;
+ return NO_ERROR;
+ }
+ status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ aPlusOne->value = a.value + 1;
+ return NO_ERROR;
+ }
+ status_t increment(const TestLightFlattenable& a,
+ TestLightFlattenable* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ aPlusOne->value = a.value + 1;
+ return NO_ERROR;
+ }
+ status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+ sp<TestLightRefBaseFlattenable>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = new TestLightRefBaseFlattenable(a->value + 1);
+ return NO_ERROR;
+ }
+ status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ native_handle* rawHandle = native_handle_create(1 /*numFds*/, 1 /*numInts*/);
+ if (rawHandle == nullptr) return NO_MEMORY;
+
+ // Copy the fd over directly
+ rawHandle->data[0] = dup(a->handle()->data[0]);
+
+ // Increment the int
+ rawHandle->data[1] = a->handle()->data[1] + 1;
+
+ // This cannot fail, as it is just the sp<NativeHandle> taking responsibility for closing
+ // the native_handle when it goes out of scope
+ *aPlusOne = NativeHandle::create(rawHandle, true);
+ return NO_ERROR;
+ }
+ status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ aPlusOne->setValue(a.getValue() + 1);
+ return NO_ERROR;
+ }
+ status_t increment(const std::vector<TestParcelable>& a,
+ std::vector<TestParcelable>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ aPlusOne->resize(a.size());
+ for (size_t i = 0; i < a.size(); ++i) {
+ (*aPlusOne)[i].setValue(a[i].getValue() + 1);
+ }
+ return NO_ERROR;
+ }
+ status_t toUpper(const String8& str, String8* upperStr) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *upperStr = str;
+ upperStr->toUpper();
+ return NO_ERROR;
+ }
+ void callMeBack(const sp<ICallback>& callback, int32_t a) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ callback->onCallback(a + 1);
+ }
+ status_t increment(int32_t a, int32_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ return NO_ERROR;
+ }
+ status_t increment(uint32_t a, uint32_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ return NO_ERROR;
+ }
+ status_t increment(int64_t a, int64_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ return NO_ERROR;
+ }
+ status_t increment(uint64_t a, uint64_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ return NO_ERROR;
+ }
+ status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ *bPlusOne = b + 1;
+ return NO_ERROR;
+ }
+
+ // BnInterface
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t /*flags*/) override {
+ EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+ EXPECT_LT(code, static_cast<uint32_t>(Tag::Last));
+ ISafeInterfaceTest::Tag tag = static_cast<ISafeInterfaceTest::Tag>(code);
+ switch (tag) {
+ case ISafeInterfaceTest::Tag::SetDeathToken: {
+ return callLocal(data, reply, &ISafeInterfaceTest::setDeathToken);
+ }
+ case ISafeInterfaceTest::Tag::ReturnsNoMemory: {
+ return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory);
+ }
+ case ISafeInterfaceTest::Tag::LogicalNot: {
+ return callLocal(data, reply, &ISafeInterfaceTest::logicalNot);
+ }
+ case ISafeInterfaceTest::Tag::ModifyEnum: {
+ return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum);
+ }
+ case ISafeInterfaceTest::Tag::IncrementFlattenable: {
+ using Signature = status_t (ISafeInterfaceTest::*)(const TestFlattenable& a,
+ TestFlattenable* aPlusOne) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementLightFlattenable: {
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const TestLightFlattenable& a,
+ TestLightFlattenable* aPlusOne) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementLightRefBaseFlattenable: {
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const sp<TestLightRefBaseFlattenable>&,
+ sp<TestLightRefBaseFlattenable>*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementNativeHandle: {
+ using Signature = status_t (ISafeInterfaceTest::*)(const sp<NativeHandle>&,
+ sp<NativeHandle>*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementNoCopyNoMove: {
+ using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a,
+ NoCopyNoMove* aPlusOne) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementParcelableVector: {
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const std::vector<TestParcelable>&,
+ std::vector<TestParcelable>*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::ToUpper: {
+ return callLocal(data, reply, &ISafeInterfaceTest::toUpper);
+ }
+ case ISafeInterfaceTest::Tag::CallMeBack: {
+ return callLocalAsync(data, reply, &ISafeInterfaceTest::callMeBack);
+ }
+ case ISafeInterfaceTest::Tag::IncrementInt32: {
+ using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementUint32: {
+ using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementInt64: {
+ using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementUint64: {
+ using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementTwo: {
+ using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t,
+ int32_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::Last:
+ // Should not be possible because of the asserts at the beginning of the method
+ [&]() { FAIL(); }();
+ return UNKNOWN_ERROR;
+ }
+ }
+
+private:
+ static constexpr const char* getLogTag() { return "BnSafeInterfaceTest"; }
+};
+
+class SafeInterfaceTest : public ::testing::Test {
+public:
+ SafeInterfaceTest() : mSafeInterfaceTest(getRemoteService()) {
+ ProcessState::self()->startThreadPool();
+ }
+ ~SafeInterfaceTest() override = default;
+
+protected:
+ sp<ISafeInterfaceTest> mSafeInterfaceTest;
+
+private:
+ static constexpr const char* getLogTag() { return "SafeInterfaceTest"; }
+
+ sp<ISafeInterfaceTest> getRemoteService() {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+ static std::mutex sMutex;
+ static sp<ISafeInterfaceTest> sService;
+ static sp<IBinder> sDeathToken = new BBinder;
+#pragma clang diagnostic pop
+
+ std::unique_lock<decltype(sMutex)> lock;
+ if (sService == nullptr) {
+ ALOG(LOG_INFO, getLogTag(), "Forking remote process");
+ pid_t forkPid = fork();
+ EXPECT_NE(forkPid, -1);
+
+ const String16 serviceName("SafeInterfaceTest");
+
+ if (forkPid == 0) {
+ ALOG(LOG_INFO, getLogTag(), "Remote process checking in");
+ sp<ISafeInterfaceTest> nativeService = new BnSafeInterfaceTest;
+ defaultServiceManager()->addService(serviceName,
+ IInterface::asBinder(nativeService));
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ // We shouldn't get to this point
+ [&]() { FAIL(); }();
+ }
+
+ sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+ sService = interface_cast<ISafeInterfaceTest>(binder);
+ EXPECT_TRUE(sService != nullptr);
+
+ sService->setDeathToken(sDeathToken);
+ }
+
+ return sService;
+ }
+};
+
+TEST_F(SafeInterfaceTest, TestReturnsNoMemory) {
+ status_t result = mSafeInterfaceTest->returnsNoMemory();
+ ASSERT_EQ(NO_MEMORY, result);
+}
+
+TEST_F(SafeInterfaceTest, TestLogicalNot) {
+ const bool a = true;
+ bool notA = true;
+ status_t result = mSafeInterfaceTest->logicalNot(a, ¬A);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(!a, notA);
+ // Test both since we don't want to accidentally catch a default false somewhere
+ const bool b = false;
+ bool notB = false;
+ result = mSafeInterfaceTest->logicalNot(b, ¬B);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(!b, notB);
+}
+
+TEST_F(SafeInterfaceTest, TestModifyEnum) {
+ const TestEnum a = TestEnum::INITIAL;
+ TestEnum b = TestEnum::INVALID;
+ status_t result = mSafeInterfaceTest->modifyEnum(a, &b);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(TestEnum::FINAL, b);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementFlattenable) {
+ const TestFlattenable a{1};
+ TestFlattenable aPlusOne{0};
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a.value + 1, aPlusOne.value);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementLightFlattenable) {
+ const TestLightFlattenable a{1};
+ TestLightFlattenable aPlusOne{0};
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a.value + 1, aPlusOne.value);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementLightRefBaseFlattenable) {
+ sp<TestLightRefBaseFlattenable> a = new TestLightRefBaseFlattenable{1};
+ sp<TestLightRefBaseFlattenable> aPlusOne;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_NE(nullptr, aPlusOne.get());
+ ASSERT_EQ(a->value + 1, aPlusOne->value);
+}
+
+namespace { // Anonymous namespace
+
+bool fdsAreEquivalent(int a, int b) {
+ struct stat statA {};
+ struct stat statB {};
+ if (fstat(a, &statA) != 0) return false;
+ if (fstat(b, &statB) != 0) return false;
+ return (statA.st_dev == statB.st_dev) && (statA.st_ino == statB.st_ino);
+}
+
+} // Anonymous namespace
+
+TEST_F(SafeInterfaceTest, TestIncrementNativeHandle) {
+ // Create an fd we can use to send and receive from the remote process
+ base::unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)};
+ ASSERT_NE(-1, eventFd);
+
+ // Determine the maximum number of fds this process can have open
+ struct rlimit limit {};
+ ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit));
+ uint32_t maxFds = static_cast<uint32_t>(limit.rlim_cur);
+
+ // Perform this test enough times to rule out fd leaks
+ for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) {
+ native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/);
+ ASSERT_NE(nullptr, handle);
+ handle->data[0] = dup(eventFd.get());
+ handle->data[1] = 1;
+
+ // This cannot fail, as it is just the sp<NativeHandle> taking responsibility for closing
+ // the native_handle when it goes out of scope
+ sp<NativeHandle> a = NativeHandle::create(handle, true);
+
+ sp<NativeHandle> aPlusOne;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_TRUE(fdsAreEquivalent(a->handle()->data[0], aPlusOne->handle()->data[0]));
+ ASSERT_EQ(a->handle()->data[1] + 1, aPlusOne->handle()->data[1]);
+ }
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementNoCopyNoMove) {
+ const NoCopyNoMove a{1};
+ NoCopyNoMove aPlusOne{0};
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a.getValue() + 1, aPlusOne.getValue());
+}
+
+TEST_F(SafeInterfaceTest, TestIncremementParcelableVector) {
+ const std::vector<TestParcelable> a{TestParcelable{1}, TestParcelable{2}};
+ std::vector<TestParcelable> aPlusOne;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(a.size(), aPlusOne.size());
+ for (size_t i = 0; i < a.size(); ++i) {
+ ASSERT_EQ(a[i].getValue() + 1, aPlusOne[i].getValue());
+ }
+}
+
+TEST_F(SafeInterfaceTest, TestToUpper) {
+ const String8 str{"Hello, world!"};
+ String8 upperStr;
+ status_t result = mSafeInterfaceTest->toUpper(str, &upperStr);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_TRUE(upperStr == String8{"HELLO, WORLD!"});
+}
+
+TEST_F(SafeInterfaceTest, TestCallMeBack) {
+ class CallbackReceiver : public BnCallback {
+ public:
+ void onCallback(int32_t aPlusOne) override {
+ ALOG(LOG_INFO, "CallbackReceiver", "%s", __PRETTY_FUNCTION__);
+ std::unique_lock<decltype(mMutex)> lock(mMutex);
+ mValue = aPlusOne;
+ mCondition.notify_one();
+ }
+
+ std::optional<int32_t> waitForCallback() {
+ std::unique_lock<decltype(mMutex)> lock(mMutex);
+ bool success =
+ mCondition.wait_for(lock, 100ms, [&]() { return static_cast<bool>(mValue); });
+ return success ? mValue : std::nullopt;
+ }
+
+ private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ std::optional<int32_t> mValue;
+ };
+
+ sp<CallbackReceiver> receiver = new CallbackReceiver;
+ const int32_t a = 1;
+ mSafeInterfaceTest->callMeBack(receiver, a);
+ auto result = receiver->waitForCallback();
+ ASSERT_TRUE(result);
+ ASSERT_EQ(a + 1, *result);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementInt32) {
+ const int32_t a = 1;
+ int32_t aPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementUint32) {
+ const uint32_t a = 1;
+ uint32_t aPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementInt64) {
+ const int64_t a = 1;
+ int64_t aPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementUint64) {
+ const uint64_t a = 1;
+ uint64_t aPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementTwo) {
+ const int32_t a = 1;
+ int32_t aPlusOne = 0;
+ const int32_t b = 2;
+ int32_t bPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(1, &aPlusOne, 2, &bPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+ ASSERT_EQ(b + 1, bPlusOne);
+}
+
+} // namespace tests
+} // namespace android
diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp
new file mode 100644
index 0000000..f6dd22d
--- /dev/null
+++ b/libs/binder/tests/binderTextOutputTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits>
+#include <cstddef>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+#include <binder/TextOutput.h>
+#include <binder/Debug.h>
+
+static void CheckMessage(const CapturedStderr& cap,
+ const char* expected,
+ bool singleline) {
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
+ if (singleline)
+ output.erase(std::remove(output.begin(), output.end(), '\n'));
+ ASSERT_STREQ(output.c_str(), expected);
+}
+
+#define CHECK_LOG_(input, expect, singleline) \
+{ \
+ CapturedStderr cap; \
+ android::aerr << input << android::endl; \
+ CheckMessage(cap, expect, singleline); \
+} \
+
+#define CHECK_VAL_(val, singleline) \
+{ \
+ std::stringstream ss; \
+ ss << val; \
+ std::string s = ss.str(); \
+ CHECK_LOG_(val, s.c_str(), singleline); \
+} \
+
+#define CHECK_LOG(input, expect) CHECK_LOG_(input, expect, true)
+#define CHECK_VAL(val) CHECK_VAL_(val, true)
+
+TEST(TextOutput, HandlesStdEndl) {
+ CapturedStderr cap;
+ android::aerr << "foobar" << std::endl;
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandlesCEndl) {
+ CapturedStderr cap;
+ android::aerr << "foobar" << "\n";
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandlesAndroidEndl) {
+ CapturedStderr cap;
+ android::aerr << "foobar" << android::endl;
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandleEmptyString) {
+ CHECK_LOG("", "");
+}
+
+TEST(TextOutput, HandleString) {
+ CHECK_LOG("foobar", "foobar");
+}
+
+TEST(TextOutput, HandleNum) {
+ CHECK_LOG(12345, "12345");
+}
+
+TEST(TextOutput, HandleBool) {
+ CHECK_LOG(false, "false");
+}
+
+TEST(TextOutput, HandleChar) {
+ CHECK_LOG('T', "T");
+}
+
+TEST(TextOutput, HandleParcel) {
+ android::Parcel val;
+ CHECK_LOG(val, "Parcel(NULL)");
+}
+
+TEST(TextOutput, HandleHexDump) {
+ const char buf[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+ android::HexDump val(buf, sizeof(buf));
+ CHECK_LOG(val, "03020100 07060504 0b0a0908 0f0e0d0c '................'");
+}
+
+TEST(TextOutput, HandleHexDumpCustom) {
+ const char buf[4] = {0x11,0x22,0x33,0x44};
+ android::HexDump val(buf, sizeof(buf), 4);
+ CHECK_LOG(val, "11 22 33 44 '.\"3D'");
+}
+
+TEST(TextOutput, HandleTypeCode) {
+ android::TypeCode val(1234);
+ CHECK_LOG(val, "'\\x04\\xd2'");
+}
+
+TEST(TextOutput, HandleCookie) {
+ int32_t val = 321; //0x141
+ CHECK_LOG((void*)(long)val, "0x141");
+}
+
+TEST(TextOutput, HandleString8) {
+ android::String8 val("foobar");
+ CHECK_LOG(val, "foobar");
+}
+
+TEST(TextOutput, HandleString16) {
+ android::String16 val("foobar");
+ CHECK_LOG(val, "foobar");
+}
+
+template <typename T>
+class TextTest : public testing::Test {};
+
+typedef testing::Types<short, unsigned short,
+ int, unsigned int,
+ long, unsigned long,
+ long long, unsigned long long,
+ float, double, long double> TestTypes;
+TYPED_TEST_CASE(TextTest, TestTypes);
+
+TYPED_TEST(TextTest, TextMax)
+{
+ TypeParam max = std::numeric_limits<TypeParam>::max();
+ CHECK_VAL(max);
+}
+
+TYPED_TEST(TextTest, TestMin)
+{
+ TypeParam min = std::numeric_limits<TypeParam>::min();
+ CHECK_VAL(min);
+}
+
+TYPED_TEST(TextTest, TestDenom)
+{
+ TypeParam min = std::numeric_limits<TypeParam>::denorm_min();
+ CHECK_VAL(min);
+}
+
+TYPED_TEST(TextTest, TestEpsilon)
+{
+ TypeParam eps = std::numeric_limits<TypeParam>::epsilon();
+ CHECK_VAL(eps);
+}
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 71b96d4..6e8f7df 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -170,6 +170,8 @@
int num,
int worker_count,
int iterations,
+ int payload_size,
+ bool cs_pair,
Pipe p)
{
// Create BinderWorkerService and for go.
@@ -182,22 +184,32 @@
p.signal();
p.wait();
+ // If client/server pairs, then half the workers are
+ // servers and half are clients
+ int server_count = cs_pair ? worker_count / 2 : worker_count;
+
// Get references to other binder services.
cout << "Created BinderWorker" << num << endl;
(void)worker_count;
vector<sp<IBinder> > workers;
- for (int i = 0; i < worker_count; i++) {
+ for (int i = 0; i < server_count; i++) {
if (num == i)
continue;
workers.push_back(serviceMgr->getService(generateServiceName(i)));
}
- // Run the benchmark.
+ // Run the benchmark if client
ProcResults results;
chrono::time_point<chrono::high_resolution_clock> start, end;
- for (int i = 0; i < iterations; i++) {
- int target = rand() % workers.size();
+ for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) {
Parcel data, reply;
+ int target = cs_pair ? num % server_count : rand() % workers.size();
+ int sz = payload_size;
+
+ while (sz > sizeof(uint32_t)) {
+ data.writeInt32(0);
+ sz -= sizeof(uint32_t);
+ }
start = chrono::high_resolution_clock::now();
status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
end = chrono::high_resolution_clock::now();
@@ -210,6 +222,7 @@
exit(EXIT_FAILURE);
}
}
+
// Signal completion to master and wait.
p.signal();
p.wait();
@@ -221,7 +234,7 @@
exit(EXIT_SUCCESS);
}
-Pipe make_worker(int num, int iterations, int worker_count)
+Pipe make_worker(int num, int iterations, int worker_count, int payload_size, bool cs_pair)
{
auto pipe_pair = Pipe::createPipePair();
pid_t pid = fork();
@@ -230,7 +243,7 @@
return move(get<0>(pipe_pair));
} else {
/* child */
- worker_fx(num, worker_count, iterations, move(get<1>(pipe_pair)));
+ worker_fx(num, worker_count, iterations, payload_size, cs_pair, move(get<1>(pipe_pair)));
/* never get here */
return move(get<0>(pipe_pair));
}
@@ -255,6 +268,8 @@
{
int workers = 2;
int iterations = 10000;
+ int payload_size = 0;
+ bool cs_pair = false;
(void)argc;
(void)argv;
vector<Pipe> pipes;
@@ -271,11 +286,21 @@
i++;
continue;
}
+ if (string(argv[i]) == "-s") {
+ payload_size = atoi(argv[i+1]);
+ i++;
+ }
+ if (string(argv[i]) == "-p") {
+ // client/server pairs instead of spreading
+ // requests to all workers. If true, half
+ // the workers become clients and half servers
+ cs_pair = true;
+ }
}
// Create all the workers and wait for them to spawn.
for (int i = 0; i < workers; i++) {
- pipes.push_back(make_worker(i, iterations, workers));
+ pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
}
wait_all(pipes);
diff --git a/libs/binder/tests/binderValueTypeTest.cpp b/libs/binder/tests/binderValueTypeTest.cpp
new file mode 100644
index 0000000..c8f4697
--- /dev/null
+++ b/libs/binder/tests/binderValueTypeTest.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits>
+#include <cstddef>
+#include <vector>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+#include <binder/Value.h>
+#include <binder/Debug.h>
+
+using ::android::binder::Value;
+using ::android::os::PersistableBundle;
+using ::android::String16;
+using ::std::vector;
+
+#define VALUE_TYPE_TEST(T, TYPENAME, VAL) \
+ TEST(ValueType, Handles ## TYPENAME) { \
+ T x = VAL; \
+ T y = T(); \
+ Value value = VAL; \
+ ASSERT_FALSE(value.empty()); \
+ ASSERT_TRUE(value.is ## TYPENAME ()); \
+ ASSERT_TRUE(value.get ## TYPENAME (&y)); \
+ ASSERT_EQ(x, y); \
+ ASSERT_EQ(value, Value(y)); \
+ value.put ## TYPENAME (x); \
+ ASSERT_EQ(value, Value(y)); \
+ value = Value(); \
+ ASSERT_TRUE(value.empty()); \
+ ASSERT_NE(value, Value(y)); \
+ value = y; \
+ ASSERT_EQ(value, Value(x)); \
+ }
+
+#define VALUE_TYPE_VECTOR_TEST(T, TYPENAME, VAL) \
+ TEST(ValueType, Handles ## TYPENAME ## Vector) { \
+ vector<T> x; \
+ vector<T> y; \
+ x.push_back(VAL); \
+ x.push_back(T()); \
+ Value value(x); \
+ ASSERT_FALSE(value.empty()); \
+ ASSERT_TRUE(value.is ## TYPENAME ## Vector()); \
+ ASSERT_TRUE(value.get ## TYPENAME ## Vector(&y)); \
+ ASSERT_EQ(x, y); \
+ ASSERT_EQ(value, Value(y)); \
+ value.put ## TYPENAME ## Vector(x); \
+ ASSERT_EQ(value, Value(y)); \
+ value = Value(); \
+ ASSERT_TRUE(value.empty()); \
+ ASSERT_NE(value, Value(y)); \
+ value = y; \
+ ASSERT_EQ(value, Value(x)); \
+ }
+
+VALUE_TYPE_TEST(bool, Boolean, true)
+VALUE_TYPE_TEST(int32_t, Int, 31337)
+VALUE_TYPE_TEST(int64_t, Long, 13370133701337l)
+VALUE_TYPE_TEST(double, Double, 3.14159265358979323846)
+VALUE_TYPE_TEST(String16, String, String16("Lovely"))
+
+VALUE_TYPE_VECTOR_TEST(bool, Boolean, true)
+VALUE_TYPE_VECTOR_TEST(int32_t, Int, 31337)
+VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337l)
+VALUE_TYPE_VECTOR_TEST(double, Double, 3.14159265358979323846)
+VALUE_TYPE_VECTOR_TEST(String16, String, String16("Lovely"))
+
+VALUE_TYPE_TEST(PersistableBundle, PersistableBundle, PersistableBundle())
+
+TEST(ValueType, HandlesClear) {
+ Value value;
+ ASSERT_TRUE(value.empty());
+ value.putInt(31337);
+ ASSERT_FALSE(value.empty());
+ value.clear();
+ ASSERT_TRUE(value.empty());
+}
+
+TEST(ValueType, HandlesSwap) {
+ Value value_a, value_b;
+ int32_t int_x;
+ value_a.putInt(31337);
+ ASSERT_FALSE(value_a.empty());
+ ASSERT_TRUE(value_b.empty());
+ value_a.swap(value_b);
+ ASSERT_FALSE(value_b.empty());
+ ASSERT_TRUE(value_a.empty());
+ ASSERT_TRUE(value_b.getInt(&int_x));
+ ASSERT_EQ(31337, int_x);
+}
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp
new file mode 100644
index 0000000..13f03b1
--- /dev/null
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -0,0 +1,502 @@
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+#include <iomanip>
+#include <iostream>
+#include <tuple>
+#include <vector>
+
+#include <pthread.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fstream>
+
+using namespace std;
+using namespace android;
+
+enum BinderWorkerServiceCode {
+ BINDER_NOP = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+#define ASSERT(cond) \
+ do { \
+ if (!(cond)) { \
+ cerr << __func__ << ":" << __LINE__ << " condition:" << #cond \
+ << " failed\n" \
+ << endl; \
+ exit(EXIT_FAILURE); \
+ } \
+ } while (0)
+
+vector<sp<IBinder> > workers;
+
+// the ratio that the service is synced on the same cpu beyond
+// GOOD_SYNC_MIN is considered as good
+#define GOOD_SYNC_MIN (0.6)
+
+#define DUMP_PRESICION 2
+
+string trace_path = "/sys/kernel/debug/tracing";
+
+// the default value
+int no_process = 2;
+int iterations = 100;
+int payload_size = 16;
+int no_inherent = 0;
+int no_sync = 0;
+int verbose = 0;
+int trace;
+
+bool traceIsOn() {
+ fstream file;
+ file.open(trace_path + "/tracing_on", ios::in);
+ char on;
+ file >> on;
+ file.close();
+ return on == '1';
+}
+
+void traceStop() {
+ ofstream file;
+ file.open(trace_path + "/tracing_on", ios::out | ios::trunc);
+ file << '0' << endl;
+ file.close();
+}
+
+// the deadline latency that we are interested in
+uint64_t deadline_us = 2500;
+
+int thread_pri() {
+ struct sched_param param;
+ int policy;
+ ASSERT(!pthread_getschedparam(pthread_self(), &policy, ¶m));
+ return param.sched_priority;
+}
+
+void thread_dump(const char* prefix) {
+ struct sched_param param;
+ int policy;
+ if (!verbose) return;
+ cout << "--------------------------------------------------" << endl;
+ cout << setw(12) << left << prefix << " pid: " << getpid()
+ << " tid: " << gettid() << " cpu: " << sched_getcpu() << endl;
+ ASSERT(!pthread_getschedparam(pthread_self(), &policy, ¶m));
+ string s = (policy == SCHED_OTHER)
+ ? "SCHED_OTHER"
+ : (policy == SCHED_FIFO)
+ ? "SCHED_FIFO"
+ : (policy == SCHED_RR) ? "SCHED_RR" : "???";
+ cout << setw(12) << left << s << param.sched_priority << endl;
+ return;
+}
+
+class BinderWorkerService : public BBinder {
+ public:
+ BinderWorkerService() {
+ }
+ ~BinderWorkerService() {
+ }
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) {
+ (void)flags;
+ (void)data;
+ (void)reply;
+ switch (code) {
+ // The transaction format is like
+ //
+ // data[in]: int32: caller priority
+ // int32: caller cpu
+ //
+ // reply[out]: int32: 1 if caller's priority != callee's priority
+ // int32: 1 if caller's cpu != callee's cpu
+ //
+ // note the caller cpu read here is not always correct
+ // there're still chances that the caller got switched out
+ // right after it read the cpu number and still before the transaction.
+ case BINDER_NOP: {
+ thread_dump("binder");
+ int priority = thread_pri();
+ int priority_caller = data.readInt32();
+ int h = 0, s = 0;
+ if (priority_caller != priority) {
+ h++;
+ if (verbose) {
+ cout << "err priority_caller:" << priority_caller
+ << ", priority:" << priority << endl;
+ }
+ }
+ if (priority == sched_get_priority_max(SCHED_FIFO)) {
+ int cpu = sched_getcpu();
+ int cpu_caller = data.readInt32();
+ if (cpu != cpu_caller) {
+ s++;
+ }
+ }
+ reply->writeInt32(h);
+ reply->writeInt32(s);
+ return NO_ERROR;
+ }
+ default:
+ return UNKNOWN_TRANSACTION;
+ };
+ }
+};
+
+class Pipe {
+ int m_readFd;
+ int m_writeFd;
+ Pipe(int readFd, int writeFd) : m_readFd{readFd}, m_writeFd{writeFd} {
+ }
+ Pipe(const Pipe&) = delete;
+ Pipe& operator=(const Pipe&) = delete;
+ Pipe& operator=(const Pipe&&) = delete;
+
+ public:
+ Pipe(Pipe&& rval) noexcept {
+ m_readFd = rval.m_readFd;
+ m_writeFd = rval.m_writeFd;
+ rval.m_readFd = 0;
+ rval.m_writeFd = 0;
+ }
+ ~Pipe() {
+ if (m_readFd) close(m_readFd);
+ if (m_writeFd) close(m_writeFd);
+ }
+ void signal() {
+ bool val = true;
+ int error = write(m_writeFd, &val, sizeof(val));
+ ASSERT(error >= 0);
+ };
+ void wait() {
+ bool val = false;
+ int error = read(m_readFd, &val, sizeof(val));
+ ASSERT(error >= 0);
+ }
+ template <typename T>
+ void send(const T& v) {
+ int error = write(m_writeFd, &v, sizeof(T));
+ ASSERT(error >= 0);
+ }
+ template <typename T>
+ void recv(T& v) {
+ int error = read(m_readFd, &v, sizeof(T));
+ ASSERT(error >= 0);
+ }
+ static tuple<Pipe, Pipe> createPipePair() {
+ int a[2];
+ int b[2];
+
+ int error1 = pipe(a);
+ int error2 = pipe(b);
+ ASSERT(error1 >= 0);
+ ASSERT(error2 >= 0);
+
+ return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
+ }
+};
+
+typedef chrono::time_point<chrono::high_resolution_clock> Tick;
+
+static inline Tick tickNow() {
+ return chrono::high_resolution_clock::now();
+}
+
+static inline uint64_t tickNano(Tick& sta, Tick& end) {
+ return uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - sta).count());
+}
+
+struct Results {
+ uint64_t m_best = 0xffffffffffffffffULL;
+ uint64_t m_worst = 0;
+ uint64_t m_transactions = 0;
+ uint64_t m_total_time = 0;
+ uint64_t m_miss = 0;
+ bool tracing;
+ Results(bool _tracing) : tracing(_tracing) {
+ }
+ inline bool miss_deadline(uint64_t nano) {
+ return nano > deadline_us * 1000;
+ }
+ void add_time(uint64_t nano) {
+ m_best = min(nano, m_best);
+ m_worst = max(nano, m_worst);
+ m_transactions += 1;
+ m_total_time += nano;
+ if (miss_deadline(nano)) m_miss++;
+ if (miss_deadline(nano) && tracing) {
+ // There might be multiple process pair running the test concurrently
+ // each may execute following statements and only the first one actually
+ // stop the trace and any traceStop() afterthen has no effect.
+ traceStop();
+ cout << endl;
+ cout << "deadline triggered: halt & stop trace" << endl;
+ cout << "log:" + trace_path + "/trace" << endl;
+ cout << endl;
+ exit(1);
+ }
+ }
+ void dump() {
+ double best = (double)m_best / 1.0E6;
+ double worst = (double)m_worst / 1.0E6;
+ double average = (double)m_total_time / m_transactions / 1.0E6;
+ // FIXME: libjson?
+ int W = DUMP_PRESICION + 2;
+ cout << setprecision(DUMP_PRESICION) << "{ \"avg\":" << setw(W) << left
+ << average << ",\"wst\":" << setw(W) << left << worst
+ << ",\"bst\":" << setw(W) << left << best << ",\"miss\":" << left
+ << m_miss << ",\"meetR\":" << left << setprecision(DUMP_PRESICION + 3)
+ << (1.0 - (double)m_miss / m_transactions) << "}";
+ }
+};
+
+String16 generateServiceName(int num) {
+ char num_str[32];
+ snprintf(num_str, sizeof(num_str), "%d", num);
+ String16 serviceName = String16("binderWorker") + String16(num_str);
+ return serviceName;
+}
+
+static void parcel_fill(Parcel& data, int sz, int priority, int cpu) {
+ ASSERT(sz >= (int)sizeof(uint32_t) * 2);
+ data.writeInt32(priority);
+ data.writeInt32(cpu);
+ sz -= sizeof(uint32_t);
+ while (sz > (int)sizeof(uint32_t)) {
+ data.writeInt32(0);
+ sz -= sizeof(uint32_t);
+ }
+}
+
+typedef struct {
+ void* result;
+ int target;
+} thread_priv_t;
+
+static void* thread_start(void* p) {
+ thread_priv_t* priv = (thread_priv_t*)p;
+ int target = priv->target;
+ Results* results_fifo = (Results*)priv->result;
+ Parcel data, reply;
+ Tick sta, end;
+
+ parcel_fill(data, payload_size, thread_pri(), sched_getcpu());
+ thread_dump("fifo-caller");
+
+ sta = tickNow();
+ status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
+ end = tickNow();
+ results_fifo->add_time(tickNano(sta, end));
+
+ no_inherent += reply.readInt32();
+ no_sync += reply.readInt32();
+ return 0;
+}
+
+// create a fifo thread to transact and wait it to finished
+static void thread_transaction(int target, Results* results_fifo) {
+ thread_priv_t thread_priv;
+ void* dummy;
+ pthread_t thread;
+ pthread_attr_t attr;
+ struct sched_param param;
+ thread_priv.target = target;
+ thread_priv.result = results_fifo;
+ ASSERT(!pthread_attr_init(&attr));
+ ASSERT(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO));
+ param.sched_priority = sched_get_priority_max(SCHED_FIFO);
+ ASSERT(!pthread_attr_setschedparam(&attr, ¶m));
+ ASSERT(!pthread_create(&thread, &attr, &thread_start, &thread_priv));
+ ASSERT(!pthread_join(thread, &dummy));
+}
+
+#define is_client(_num) ((_num) >= (no_process / 2))
+
+void worker_fx(int num, int no_process, int iterations, int payload_size,
+ Pipe p) {
+ int dummy;
+ Results results_other(false), results_fifo(trace);
+
+ // Create BinderWorkerService and for go.
+ ProcessState::self()->startThreadPool();
+ sp<IServiceManager> serviceMgr = defaultServiceManager();
+ sp<BinderWorkerService> service = new BinderWorkerService;
+ serviceMgr->addService(generateServiceName(num), service);
+ // init done
+ p.signal();
+ // wait for kick-off
+ p.wait();
+
+ // If client/server pairs, then half the workers are
+ // servers and half are clients
+ int server_count = no_process / 2;
+
+ for (int i = 0; i < server_count; i++) {
+ // self service is in-process so just skip
+ if (num == i) continue;
+ workers.push_back(serviceMgr->getService(generateServiceName(i)));
+ }
+
+ // Client for each pair iterates here
+ // each iterations contains exatcly 2 transactions
+ for (int i = 0; is_client(num) && i < iterations; i++) {
+ Parcel data, reply;
+ Tick sta, end;
+ // the target is paired to make it easier to diagnose
+ int target = num % server_count;
+
+ // 1. transaction by fifo thread
+ thread_transaction(target, &results_fifo);
+ parcel_fill(data, payload_size, thread_pri(), sched_getcpu());
+ thread_dump("other-caller");
+
+ // 2. transaction by other thread
+ sta = tickNow();
+ ASSERT(NO_ERROR == workers[target]->transact(BINDER_NOP, data, &reply));
+ end = tickNow();
+ results_other.add_time(tickNano(sta, end));
+
+ no_inherent += reply.readInt32();
+ no_sync += reply.readInt32();
+ }
+ // Signal completion to master and wait.
+ p.signal();
+ p.wait();
+
+ p.send(&dummy);
+ // wait for kill
+ p.wait();
+ // Client for each pair dump here
+ if (is_client(num)) {
+ int no_trans = iterations * 2;
+ double sync_ratio = (1.0 - (double)no_sync / no_trans);
+ // FIXME: libjson?
+ cout << "\"P" << (num - server_count) << "\":{\"SYNC\":\""
+ << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\","
+ << "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
+ << "\"R\":" << sync_ratio << "," << endl;
+
+ cout << " \"other_ms\":";
+ results_other.dump();
+ cout << "," << endl;
+ cout << " \"fifo_ms\": ";
+ results_fifo.dump();
+ cout << endl;
+ cout << "}," << endl;
+ }
+ exit(no_inherent);
+}
+
+Pipe make_process(int num, int iterations, int no_process, int payload_size) {
+ auto pipe_pair = Pipe::createPipePair();
+ pid_t pid = fork();
+ if (pid) {
+ // parent
+ return move(get<0>(pipe_pair));
+ } else {
+ // child
+ thread_dump(is_client(num) ? "client" : "server");
+ worker_fx(num, no_process, iterations, payload_size,
+ move(get<1>(pipe_pair)));
+ // never get here
+ return move(get<0>(pipe_pair));
+ }
+}
+
+void wait_all(vector<Pipe>& v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i].wait();
+ }
+}
+
+void signal_all(vector<Pipe>& v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i].signal();
+ }
+}
+
+// This test is modified from binderThroughputTest.cpp
+int main(int argc, char** argv) {
+ for (int i = 1; i < argc; i++) {
+ if (string(argv[i]) == "-i") {
+ iterations = atoi(argv[i + 1]);
+ i++;
+ continue;
+ }
+ if (string(argv[i]) == "-pair") {
+ no_process = 2 * atoi(argv[i + 1]);
+ i++;
+ continue;
+ }
+ if (string(argv[i]) == "-deadline_us") {
+ deadline_us = atoi(argv[i + 1]);
+ i++;
+ continue;
+ }
+ if (string(argv[i]) == "-v") {
+ verbose = 1;
+ }
+ // The -trace argument is used like that:
+ //
+ // First start trace with atrace command as usual
+ // >atrace --async_start sched freq
+ //
+ // then use schd-dbg with -trace arguments
+ //./schd-dbg -trace -deadline_us 2500
+ //
+ // This makes schd-dbg to stop trace once it detects a transaction
+ // duration over the deadline. By writing '0' to
+ // /sys/kernel/debug/tracing and halt the process. The tracelog is
+ // then available on /sys/kernel/debug/trace
+ if (string(argv[i]) == "-trace") {
+ trace = 1;
+ }
+ }
+ if (trace && !traceIsOn()) {
+ cout << "trace is not running" << endl;
+ cout << "check " << trace_path + "/tracing_on" << endl;
+ cout << "use atrace --async_start first" << endl;
+ exit(-1);
+ }
+ vector<Pipe> pipes;
+ thread_dump("main");
+ // FIXME: libjson?
+ cout << "{" << endl;
+ cout << "\"cfg\":{\"pair\":" << (no_process / 2)
+ << ",\"iterations\":" << iterations << ",\"deadline_us\":" << deadline_us
+ << "}," << endl;
+
+ // the main process fork 2 processes for each pairs
+ // 1 server + 1 client
+ // each has a pipe to communicate with
+ for (int i = 0; i < no_process; i++) {
+ pipes.push_back(make_process(i, iterations, no_process, payload_size));
+ }
+ // wait for init done
+ wait_all(pipes);
+ // kick-off iterations
+ signal_all(pipes);
+ // wait for completion
+ wait_all(pipes);
+ // start to send result
+ signal_all(pipes);
+ for (int i = 0; i < no_process; i++) {
+ int status;
+ // kill
+ pipes[i].signal();
+ wait(&status);
+ // the exit status is number of transactions without priority inheritance
+ // detected in the child process
+ no_inherent += status;
+ }
+ // FIXME: libjson?
+ cout << "\"inheritance\": " << (no_inherent == 0 ? "\"PASS\"" : "\"FAIL\"")
+ << endl;
+ cout << "}" << endl;
+ return -no_inherent;
+}
diff --git a/libs/diskusage/Android.bp b/libs/diskusage/Android.bp
new file mode 100644
index 0000000..156ddff
--- /dev/null
+++ b/libs/diskusage/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "libdiskusage",
+ srcs: ["dirsize.c"],
+}
diff --git a/libs/diskusage/Android.mk b/libs/diskusage/Android.mk
deleted file mode 100644
index d54f8ad..0000000
--- a/libs/diskusage/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libdiskusage
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := dirsize.c
-
-include $(BUILD_STATIC_LIBRARY)
\ No newline at end of file
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
new file mode 100644
index 0000000..a07726a
--- /dev/null
+++ b/libs/gui/Android.bp
@@ -0,0 +1,133 @@
+// Copyright 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libgui",
+
+ clang: true,
+ cppflags: [
+ "-Weverything",
+ "-Werror",
+
+ // The static constructors and destructors in this library have not been noted to
+ // introduce significant overheads
+ "-Wno-exit-time-destructors",
+ "-Wno-global-constructors",
+
+ // We only care about compiling as C++14
+ "-Wno-c++98-compat-pedantic",
+
+ // We don't need to enumerate every case in a switch as long as a default case
+ // is present
+ "-Wno-switch-enum",
+
+ // Allow calling variadic macros without a __VA_ARGS__ list
+ "-Wno-gnu-zero-variadic-macro-arguments",
+
+ // Don't warn about struct padding
+ "-Wno-padded",
+
+ // We are aware of the risks inherent in comparing floats for equality
+ "-Wno-float-equal",
+
+ // Pure abstract classes trigger this warning
+ "-Wno-weak-vtables",
+
+ // Allow four-character integer literals
+ "-Wno-four-char-constants",
+
+ // Allow documentation warnings
+ "-Wno-documentation",
+
+ "-DDEBUG_ONLY_CODE=0",
+ ],
+
+ product_variables: {
+ brillo: {
+ cflags: ["-DHAVE_NO_SURFACE_FLINGER"],
+ },
+ eng: {
+ cppflags: [
+ "-UDEBUG_ONLY_CODE",
+ "-DDEBUG_ONLY_CODE=1",
+ ],
+ },
+ },
+
+ srcs: [
+ "BitTube.cpp",
+ "BufferItem.cpp",
+ "BufferItemConsumer.cpp",
+ "BufferQueue.cpp",
+ "BufferQueueConsumer.cpp",
+ "BufferQueueCore.cpp",
+ "BufferQueueProducer.cpp",
+ "BufferSlot.cpp",
+ "ConsumerBase.cpp",
+ "CpuConsumer.cpp",
+ "DisplayEventReceiver.cpp",
+ "FrameTimestamps.cpp",
+ "GLConsumer.cpp",
+ "GuiConfig.cpp",
+ "IDisplayEventConnection.cpp",
+ "IConsumerListener.cpp",
+ "IGraphicBufferConsumer.cpp",
+ "IGraphicBufferProducer.cpp",
+ "IProducerListener.cpp",
+ "ISurfaceComposer.cpp",
+ "ISurfaceComposerClient.cpp",
+ "LayerState.cpp",
+ "OccupancyTracker.cpp",
+ "StreamSplitter.cpp",
+ "Surface.cpp",
+ "SurfaceControl.cpp",
+ "SurfaceComposerClient.cpp",
+ "SyncFeatures.cpp",
+ "view/Surface.cpp",
+ "bufferqueue/1.0/B2HProducerListener.cpp",
+ "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
+ ],
+
+ shared_libs: [
+ "libsync",
+ "libbinder",
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libui",
+ "libutils",
+ "libnativewindow",
+ "liblog",
+ "libhidlbase",
+ "libhidltransport",
+ "android.hidl.base@1.0",
+ "android.hidl.token@1.0-utils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore-utils",
+ ],
+
+ export_shared_lib_headers: [
+ "libbinder",
+ "libui",
+ "android.hidl.token@1.0-utils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+subdirs = ["tests"]
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
deleted file mode 100644
index 9e2fc2b..0000000
--- a/libs/gui/Android.mk
+++ /dev/null
@@ -1,103 +0,0 @@
-# Copyright 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -std=c++1y -Weverything -Werror
-
-# The static constructors and destructors in this library have not been noted to
-# introduce significant overheads
-LOCAL_CPPFLAGS += -Wno-exit-time-destructors
-LOCAL_CPPFLAGS += -Wno-global-constructors
-
-# We only care about compiling as C++14
-LOCAL_CPPFLAGS += -Wno-c++98-compat-pedantic
-
-# We don't need to enumerate every case in a switch as long as a default case
-# is present
-LOCAL_CPPFLAGS += -Wno-switch-enum
-
-# Allow calling variadic macros without a __VA_ARGS__ list
-LOCAL_CPPFLAGS += -Wno-gnu-zero-variadic-macro-arguments
-
-# Don't warn about struct padding
-LOCAL_CPPFLAGS += -Wno-padded
-
-LOCAL_CPPFLAGS += -DDEBUG_ONLY_CODE=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
-
-LOCAL_SRC_FILES := \
- IGraphicBufferConsumer.cpp \
- IConsumerListener.cpp \
- BitTube.cpp \
- BufferItem.cpp \
- BufferItemConsumer.cpp \
- BufferQueue.cpp \
- BufferQueueConsumer.cpp \
- BufferQueueCore.cpp \
- BufferQueueProducer.cpp \
- BufferSlot.cpp \
- ConsumerBase.cpp \
- CpuConsumer.cpp \
- DisplayEventReceiver.cpp \
- GLConsumer.cpp \
- GraphicBufferAlloc.cpp \
- GraphicsEnv.cpp \
- GuiConfig.cpp \
- IDisplayEventConnection.cpp \
- IGraphicBufferAlloc.cpp \
- IGraphicBufferProducer.cpp \
- IProducerListener.cpp \
- ISensorEventConnection.cpp \
- ISensorServer.cpp \
- ISurfaceComposer.cpp \
- ISurfaceComposerClient.cpp \
- LayerState.cpp \
- OccupancyTracker.cpp \
- Sensor.cpp \
- SensorEventQueue.cpp \
- SensorManager.cpp \
- StreamSplitter.cpp \
- Surface.cpp \
- SurfaceControl.cpp \
- SurfaceComposerClient.cpp \
- SyncFeatures.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- libnativeloader \
- libbinder \
- libcutils \
- libEGL \
- libGLESv2 \
- libsync \
- libui \
- libutils \
- liblog
-
-
-LOCAL_MODULE := libgui
-
-ifeq ($(TARGET_BOARD_PLATFORM), tegra)
- LOCAL_CFLAGS += -DDONT_USE_FENCE_SYNC
-endif
-ifeq ($(TARGET_BOARD_PLATFORM), tegra3)
- LOCAL_CFLAGS += -DDONT_USE_FENCE_SYNC
-endif
-
-include $(BUILD_SHARED_LIBRARY)
-
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
index b653c5b..ef7a6f5 100644
--- a/libs/gui/BitTube.cpp
+++ b/libs/gui/BitTube.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#include <private/gui/BitTube.h>
+
#include <stdint.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
@@ -25,46 +27,21 @@
#include <binder/Parcel.h>
-#include <gui/BitTube.h>
-
namespace android {
-// ----------------------------------------------------------------------------
+namespace gui {
-// Socket buffer size. The default is typically about 128KB, which is much larger than
-// we really need. So we make it smaller.
+// Socket buffer size. The default is typically about 128KB, which is much larger than we really
+// need. So we make it smaller.
static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
-
-BitTube::BitTube()
- : mSendFd(-1), mReceiveFd(-1)
-{
- init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
-}
-
-BitTube::BitTube(size_t bufsize)
- : mSendFd(-1), mReceiveFd(-1)
-{
+BitTube::BitTube(size_t bufsize) {
init(bufsize, bufsize);
}
-BitTube::BitTube(const Parcel& data)
- : mSendFd(-1), mReceiveFd(-1)
-{
- mReceiveFd = dup(data.readFileDescriptor());
- if (mReceiveFd < 0) {
- mReceiveFd = -errno;
- ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)",
- strerror(-mReceiveFd));
- }
-}
+BitTube::BitTube(DefaultSizeType) : BitTube(DEFAULT_SOCKET_BUFFER_SIZE) {}
-BitTube::~BitTube()
-{
- if (mSendFd >= 0)
- close(mSendFd);
-
- if (mReceiveFd >= 0)
- close(mReceiveFd);
+BitTube::BitTube(const Parcel& data) {
+ readFromParcel(&data);
}
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
@@ -73,39 +50,43 @@
size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
- // sine we don't use the "return channel", we keep it small...
+ // since we don't use the "return channel", we keep it small...
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
fcntl(sockets[0], F_SETFL, O_NONBLOCK);
fcntl(sockets[1], F_SETFL, O_NONBLOCK);
- mReceiveFd = sockets[0];
- mSendFd = sockets[1];
+ mReceiveFd.reset(sockets[0]);
+ mSendFd.reset(sockets[1]);
} else {
- mReceiveFd = -errno;
- ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
+ mReceiveFd.reset();
+ ALOGE("BitTube: pipe creation failed (%s)", strerror(errno));
}
}
-status_t BitTube::initCheck() const
-{
+status_t BitTube::initCheck() const {
if (mReceiveFd < 0) {
return status_t(mReceiveFd);
}
return NO_ERROR;
}
-int BitTube::getFd() const
-{
+int BitTube::getFd() const {
return mReceiveFd;
}
-int BitTube::getSendFd() const
-{
+int BitTube::getSendFd() const {
return mSendFd;
}
-ssize_t BitTube::write(void const* vaddr, size_t size)
-{
+base::unique_fd BitTube::moveReceiveFd() {
+ return std::move(mReceiveFd);
+}
+
+void BitTube::setReceiveFd(base::unique_fd&& receiveFd) {
+ mReceiveFd = std::move(receiveFd);
+}
+
+ssize_t BitTube::write(void const* vaddr, size_t size) {
ssize_t err, len;
do {
len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
@@ -115,62 +96,66 @@
return err == 0 ? len : -err;
}
-ssize_t BitTube::read(void* vaddr, size_t size)
-{
+ssize_t BitTube::read(void* vaddr, size_t size) {
ssize_t err, len;
do {
len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
err = len < 0 ? errno : 0;
} while (err == EINTR);
if (err == EAGAIN || err == EWOULDBLOCK) {
- // EAGAIN means that we have non-blocking I/O but there was
- // no data to be read. Nothing the client should care about.
+ // EAGAIN means that we have non-blocking I/O but there was no data to be read. Nothing the
+ // client should care about.
return 0;
}
return err == 0 ? len : -err;
}
-status_t BitTube::writeToParcel(Parcel* reply) const
-{
- if (mReceiveFd < 0)
- return -EINVAL;
+status_t BitTube::writeToParcel(Parcel* reply) const {
+ if (mReceiveFd < 0) return -EINVAL;
status_t result = reply->writeDupFileDescriptor(mReceiveFd);
- close(mReceiveFd);
- mReceiveFd = -1;
+ mReceiveFd.reset();
return result;
}
+status_t BitTube::readFromParcel(const Parcel* parcel) {
+ mReceiveFd.reset(dup(parcel->readFileDescriptor()));
+ if (mReceiveFd < 0) {
+ mReceiveFd.reset();
+ int error = errno;
+ ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
+ return -error;
+ }
+ return NO_ERROR;
+}
-ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
- void const* events, size_t count, size_t objSize)
-{
+ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
const char* vaddr = reinterpret_cast<const char*>(events);
- ssize_t size = tube->write(vaddr, count*objSize);
+ ssize_t size = tube->write(vaddr, count * objSize);
// should never happen because of SOCK_SEQPACKET
LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
- "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)",
- count, objSize, size);
+ "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were "
+ "sent!)",
+ count, objSize, size);
- //ALOGE_IF(size<0, "error %d sending %d events", size, count);
+ // ALOGE_IF(size<0, "error %d sending %d events", size, count);
return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
-ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
- void* events, size_t count, size_t objSize)
-{
+ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) {
char* vaddr = reinterpret_cast<char*>(events);
- ssize_t size = tube->read(vaddr, count*objSize);
+ ssize_t size = tube->read(vaddr, count * objSize);
// should never happen because of SOCK_SEQPACKET
LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
- "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)",
- count, objSize, size);
+ "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were "
+ "received!)",
+ count, objSize, size);
- //ALOGE_IF(size<0, "error %d receiving %d events", size, count);
+ // ALOGE_IF(size<0, "error %d receiving %d events", size, count);
return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
-// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace gui
+} // namespace android
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 5e3924a..69b5962 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -23,6 +23,21 @@
namespace android {
+template<typename T>
+static inline constexpr uint32_t low32(const T n) {
+ return static_cast<uint32_t>(static_cast<uint64_t>(n));
+}
+
+template<typename T>
+static inline constexpr uint32_t high32(const T n) {
+ return static_cast<uint32_t>(static_cast<uint64_t>(n)>>32);
+}
+
+template<typename T>
+static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+ return static_cast<T>(static_cast<uint64_t>(hi)<<32 | lo);
+}
+
BufferItem::BufferItem() :
mGraphicBuffer(NULL),
mFence(NULL),
@@ -56,16 +71,19 @@
addAligned(size, mCrop);
addAligned(size, mTransform);
addAligned(size, mScalingMode);
- addAligned(size, mTimestampLo);
- addAligned(size, mTimestampHi);
+ addAligned(size, low32(mTimestamp));
+ addAligned(size, high32(mTimestamp));
addAligned(size, mIsAutoTimestamp);
addAligned(size, mDataSpace);
- addAligned(size, mFrameNumberLo);
- addAligned(size, mFrameNumberHi);
+ addAligned(size, low32(mFrameNumber));
+ addAligned(size, high32(mFrameNumber));
addAligned(size, mSlot);
addAligned(size, mIsDroppable);
addAligned(size, mAcquireCalled);
addAligned(size, mTransformToDisplayInverse);
+ addAligned(size, mAutoRefresh);
+ addAligned(size, mQueuedBuffer);
+ addAligned(size, mIsStale);
return size;
}
@@ -73,11 +91,11 @@
size_t size = sizeof(uint32_t); // Flags
if (mGraphicBuffer != 0) {
size += mGraphicBuffer->getFlattenedSize();
- FlattenableUtils::align<4>(size);
+ size = FlattenableUtils::align<4>(size);
}
if (mFence != 0) {
size += mFence->getFlattenedSize();
- FlattenableUtils::align<4>(size);
+ size = FlattenableUtils::align<4>(size);
}
size += mSurfaceDamage.getFlattenedSize();
size = FlattenableUtils::align<8>(size);
@@ -141,16 +159,19 @@
writeAligned(buffer, size, mCrop);
writeAligned(buffer, size, mTransform);
writeAligned(buffer, size, mScalingMode);
- writeAligned(buffer, size, mTimestampLo);
- writeAligned(buffer, size, mTimestampHi);
+ writeAligned(buffer, size, low32(mTimestamp));
+ writeAligned(buffer, size, high32(mTimestamp));
writeAligned(buffer, size, mIsAutoTimestamp);
writeAligned(buffer, size, mDataSpace);
- writeAligned(buffer, size, mFrameNumberLo);
- writeAligned(buffer, size, mFrameNumberHi);
+ writeAligned(buffer, size, low32(mFrameNumber));
+ writeAligned(buffer, size, high32(mFrameNumber));
writeAligned(buffer, size, mSlot);
writeAligned(buffer, size, mIsDroppable);
writeAligned(buffer, size, mAcquireCalled);
writeAligned(buffer, size, mTransformToDisplayInverse);
+ writeAligned(buffer, size, mAutoRefresh);
+ writeAligned(buffer, size, mQueuedBuffer);
+ writeAligned(buffer, size, mIsStale);
return NO_ERROR;
}
@@ -183,6 +204,8 @@
status_t err = mFence->unflatten(buffer, size, fds, count);
if (err) return err;
size -= FlattenableUtils::align<4>(buffer);
+
+ mFenceTime = std::make_shared<FenceTime>(mFence);
}
status_t err = mSurfaceDamage.unflatten(buffer, size);
@@ -194,19 +217,27 @@
return NO_MEMORY;
}
+ uint32_t timestampLo = 0, timestampHi = 0;
+ uint32_t frameNumberLo = 0, frameNumberHi = 0;
+
readAligned(buffer, size, mCrop);
readAligned(buffer, size, mTransform);
readAligned(buffer, size, mScalingMode);
- readAligned(buffer, size, mTimestampLo);
- readAligned(buffer, size, mTimestampHi);
+ readAligned(buffer, size, timestampLo);
+ readAligned(buffer, size, timestampHi);
+ mTimestamp = to64<int64_t>(timestampLo, timestampHi);
readAligned(buffer, size, mIsAutoTimestamp);
readAligned(buffer, size, mDataSpace);
- readAligned(buffer, size, mFrameNumberLo);
- readAligned(buffer, size, mFrameNumberHi);
+ readAligned(buffer, size, frameNumberLo);
+ readAligned(buffer, size, frameNumberHi);
+ mFrameNumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
readAligned(buffer, size, mSlot);
readAligned(buffer, size, mIsDroppable);
readAligned(buffer, size, mAcquireCalled);
readAligned(buffer, size, mTransformToDisplayInverse);
+ readAligned(buffer, size, mAutoRefresh);
+ readAligned(buffer, size, mQueuedBuffer);
+ readAligned(buffer, size, mIsStale);
return NO_ERROR;
}
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 3491043..d9d50db 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -22,7 +22,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
-//#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
//#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
//#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
//#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
@@ -57,6 +57,12 @@
mConsumer->setConsumerName(name);
}
+void BufferItemConsumer::setBufferFreedListener(
+ const wp<BufferFreedListener>& listener) {
+ Mutex::Autolock _l(mMutex);
+ mBufferFreedListener = listener;
+}
+
status_t BufferItemConsumer::acquireBuffer(BufferItem *item,
nsecs_t presentWhen, bool waitForFence) {
status_t err;
@@ -104,4 +110,14 @@
return err;
}
+void BufferItemConsumer::freeBufferLocked(int slotIndex) {
+ sp<BufferFreedListener> listener = mBufferFreedListener.promote();
+ if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) {
+ // Fire callback if we have a listener registered and the buffer being freed is valid.
+ BI_LOGV("actually calling onBufferFreed");
+ listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer);
+ }
+ ConsumerBase::freeBufferLocked(slotIndex);
+}
+
} // namespace android
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 6de98f5..4151212 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -31,6 +31,13 @@
BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
+void BufferQueue::ProxyConsumerListener::onDisconnect() {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onDisconnect();
+ }
+}
+
void BufferQueue::ProxyConsumerListener::onFrameAvailable(
const BufferItem& item) {
sp<ConsumerListener> listener(mConsumerListener.promote());
@@ -61,28 +68,28 @@
}
}
-bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
- uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
- return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ if (listener != nullptr) {
+ listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
- return false;
}
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
- const sp<IGraphicBufferAlloc>& allocator) {
+ bool consumerIsSurfaceFlinger) {
LOG_ALWAYS_FATAL_IF(outProducer == NULL,
"BufferQueue: outProducer must not be NULL");
LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
"BufferQueue: outConsumer must not be NULL");
- sp<BufferQueueCore> core(new BufferQueueCore(allocator));
+ sp<BufferQueueCore> core(new BufferQueueCore());
LOG_ALWAYS_FATAL_IF(core == NULL,
"BufferQueue: failed to create BufferQueueCore");
- sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
+ sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
LOG_ALWAYS_FATAL_IF(producer == NULL,
"BufferQueue: failed to create BufferQueueProducer");
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 73d2042..5e5de44 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -205,6 +205,7 @@
// was cached when it was last queued.
outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
outBuffer->mFence = Fence::NO_FENCE;
+ outBuffer->mFenceTime = FenceTime::NO_FENCE;
outBuffer->mCrop = mCore->mSharedBufferCache.crop;
outBuffer->mTransform = mCore->mSharedBufferCache.transform &
~static_cast<uint32_t>(
@@ -259,7 +260,8 @@
// decrease.
mCore->mDequeueCondition.broadcast();
- ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+ ATRACE_INT(mCore->mConsumerName.string(),
+ static_cast<int32_t>(mCore->mQueue.size()));
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
VALIDATE_CONSISTENCY();
@@ -673,12 +675,13 @@
return NO_ERROR;
}
-void BufferQueueConsumer::setConsumerName(const String8& name) {
+status_t BufferQueueConsumer::setConsumerName(const String8& name) {
ATRACE_CALL();
BQ_LOGV("setConsumerName: '%s'", name.string());
Mutex::Autolock lock(mCore->mMutex);
mCore->mConsumerName = name;
mConsumerName = name;
+ return NO_ERROR;
}
status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) {
@@ -706,6 +709,14 @@
return NO_ERROR;
}
+status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) {
+ ATRACE_CALL();
+ BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false");
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->mConsumerIsProtected = isProtected;
+ return NO_ERROR;
+}
+
status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
ATRACE_CALL();
BQ_LOGV("setTransformHint: %#x", hint);
@@ -714,9 +725,10 @@
return NO_ERROR;
}
-sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const {
+status_t BufferQueueConsumer::getSidebandStream(sp<NativeHandle>* outStream) const {
Mutex::Autolock lock(mCore->mMutex);
- return mCore->mSidebandStream;
+ *outStream = mCore->mSidebandStream;
+ return NO_ERROR;
}
status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
@@ -732,19 +744,22 @@
return NO_ERROR;
}
-void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
+status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const {
const IPCThreadState* ipc = IPCThreadState::self();
const pid_t pid = ipc->getCallingPid();
const uid_t uid = ipc->getCallingUid();
if ((uid != AID_SHELL)
&& !PermissionCache::checkPermission(String16(
"android.permission.DUMP"), pid, uid)) {
- result.appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+ outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
"from pid=%d, uid=%d\n", pid, uid);
- android_errorWriteWithInfoLog(0x534e4554, "27046057", uid, NULL, 0);
- } else {
- mCore->dump(result, prefix);
+ android_errorWriteWithInfoLog(0x534e4554, "27046057",
+ static_cast<int32_t>(uid), NULL, 0);
+ return PERMISSION_DENIED;
}
+
+ mCore->dumpState(prefix, outResult);
+ return NO_ERROR;
}
} // namespace android
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index c4714e3..cfb25e0 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -28,10 +28,12 @@
#include <inttypes.h>
+#include <cutils/properties.h>
+#include <cutils/atomic.h>
+
#include <gui/BufferItem.h>
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferAlloc.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
@@ -50,14 +52,14 @@
return id | counter++;
}
-BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
- mAllocator(allocator),
+BufferQueueCore::BufferQueueCore() :
mMutex(),
mIsAbandoned(false),
mConsumerControlledByApp(false),
mConsumerName(getUniqueName()),
mConsumerListener(),
mConsumerUsageBits(0),
+ mConsumerIsProtected(false),
mConnectedApi(NO_CONNECTED_API),
mLinkedToDeath(),
mConnectedProducerListener(),
@@ -93,14 +95,6 @@
mLastQueuedSlot(INVALID_BUFFER_SLOT),
mUniqueId(getUniqueId())
{
- if (allocator == NULL) {
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- mAllocator = composer->createGraphicBufferAlloc();
- if (mAllocator == NULL) {
- BQ_LOGE("createGraphicBufferAlloc failed");
- }
- }
-
int numStartingBuffers = getMaxBufferCountLocked();
for (int s = 0; s < numStartingBuffers; s++) {
mFreeSlots.insert(s);
@@ -113,7 +107,7 @@
BufferQueueCore::~BufferQueueCore() {}
-void BufferQueueCore::dump(String8& result, const char* prefix) const {
+void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
Mutex::Autolock lock(mMutex);
String8 fifo;
@@ -128,10 +122,10 @@
++current;
}
- result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
+ outResult->appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
"mMaxDequeuedBufferCount=%d, mDequeueBufferCannotBlock=%d "
"mAsyncMode=%d, default-size=[%dx%d], default-format=%d, "
- "transform-hint=%02x, FIFO(%zu)={%s}\n", prefix,
+ "transform-hint=%02x, FIFO(%zu)={%s}\n", prefix.string(),
mMaxAcquiredBufferCount, mMaxDequeuedBufferCount,
mDequeueBufferCannotBlock, mAsyncMode, mDefaultWidth,
mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(),
@@ -141,28 +135,28 @@
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
// A dequeued buffer might be null if it's still being allocated
if (buffer.get()) {
- result.appendFormat("%s%s[%02d:%p] state=%-8s, %p "
- "[%4ux%4u:%4u,%3X]\n", prefix,
+ outResult->appendFormat("%s%s[%02d:%p] state=%-8s, %p "
+ "[%4ux%4u:%4u,%3X]\n", prefix.string(),
(mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
buffer.get(), mSlots[s].mBufferState.string(),
buffer->handle, buffer->width, buffer->height,
buffer->stride, buffer->format);
} else {
- result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+ outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s,
buffer.get(), mSlots[s].mBufferState.string());
}
}
for (int s : mFreeBuffers) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
- result.appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
- prefix, s, buffer.get(), mSlots[s].mBufferState.string(),
+ outResult->appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+ prefix.string(), s, buffer.get(), mSlots[s].mBufferState.string(),
buffer->handle, buffer->width, buffer->height, buffer->stride,
buffer->format);
}
for (int s : mFreeSlots) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
- result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+ outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s,
buffer.get(), mSlots[s].mBufferState.string());
}
}
@@ -241,6 +235,13 @@
for (auto& b : mQueue) {
b.mIsStale = true;
+
+ // We set this to false to force the BufferQueue to resend the buffer
+ // handle upon acquire, since if we're here due to a producer
+ // disconnect, the consumer will have been told to purge its cache of
+ // slot-to-buffer-handle mappings and will not be able to otherwise
+ // obtain a valid buffer handle.
+ b.mAcquireCalled = false;
}
VALIDATE_CONSISTENCY();
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index ff85eb5..8385864 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -34,7 +34,6 @@
#include <gui/BufferQueueProducer.h>
#include <gui/GLConsumer.h>
#include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferAlloc.h>
#include <gui/IProducerListener.h>
#include <utils/Log.h>
@@ -42,12 +41,17 @@
namespace android {
-BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
+static constexpr uint32_t BQ_LAYER_COUNT = 1;
+
+BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
+ bool consumerIsSurfaceFlinger) :
mCore(core),
mSlots(core->mSlots),
mConsumerName(),
mStickyTransform(0),
+ mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),
mLastQueueBufferFence(Fence::NO_FENCE),
+ mLastQueuedTransform(0),
mCallbackMutex(),
mNextCallbackTicket(0),
mCurrentCallbackTicket(0),
@@ -343,7 +347,8 @@
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
sp<android::Fence> *outFence, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t usage) {
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
ATRACE_CALL();
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -411,7 +416,8 @@
// buffer. If this buffer would require reallocation to meet the
// requested attributes, we free it and attempt to get another one.
if (!mCore->mAllowAllocation) {
- if (buffer->needsReallocation(width, height, format, usage)) {
+ if (buffer->needsReallocation(width, height, format,
+ BQ_LAYER_COUNT, usage)) {
if (mCore->mSharedBufferSlot == found) {
BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
"buffer");
@@ -427,7 +433,8 @@
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if (mCore->mSharedBufferSlot == found &&
- buffer->needsReallocation(width, height, format, usage)) {
+ buffer->needsReallocation(width, height, format,
+ BQ_LAYER_COUNT, usage)) {
BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
"buffer");
@@ -446,7 +453,7 @@
mSlots[found].mBufferState.dequeue();
if ((buffer == NULL) ||
- buffer->needsReallocation(width, height, format, usage))
+ buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
{
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = NULL;
@@ -494,15 +501,17 @@
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
- status_t error;
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
- sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
- width, height, format, usage,
- {mConsumerName.string(), mConsumerName.size()}, &error));
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+ width, height, format, BQ_LAYER_COUNT, usage,
+ {mConsumerName.string(), mConsumerName.size()});
+
+ status_t error = graphicBuffer->initCheck();
+
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
- if (graphicBuffer != NULL && !mCore->mIsAbandoned) {
+ if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
}
@@ -510,7 +519,7 @@
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.broadcast();
- if (graphicBuffer == NULL) {
+ if (error != NO_ERROR) {
mCore->mFreeSlots.insert(*outSlot);
mCore->clearBufferSlotLocked(*outSlot);
BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
@@ -552,6 +561,8 @@
mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+ addAndGetFrameTimestamps(nullptr, outTimestamps);
+
return returnFlags;
}
@@ -621,41 +632,49 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mCore->mMutex);
+ sp<IConsumerListener> listener;
+ {
+ Mutex::Autolock lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
- return NO_INIT;
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
+
+ if (mCore->mSharedBufferMode) {
+ BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer "
+ "mode");
+ return BAD_VALUE;
+ }
+
+ mCore->waitWhileAllocatingLocked();
+
+ if (mCore->mFreeBuffers.empty()) {
+ return NO_MEMORY;
+ }
+
+ int found = mCore->mFreeBuffers.front();
+ mCore->mFreeBuffers.remove(found);
+ mCore->mFreeSlots.insert(found);
+
+ BQ_LOGV("detachNextBuffer detached slot %d", found);
+
+ *outBuffer = mSlots[found].mGraphicBuffer;
+ *outFence = mSlots[found].mFence;
+ mCore->clearBufferSlotLocked(found);
+ VALIDATE_CONSISTENCY();
+ listener = mCore->mConsumerListener;
}
- if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
- return NO_INIT;
+ if (listener != NULL) {
+ listener->onBuffersReleased();
}
- if (mCore->mSharedBufferMode) {
- BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer "
- "mode");
- return BAD_VALUE;
- }
-
- mCore->waitWhileAllocatingLocked();
-
- if (mCore->mFreeBuffers.empty()) {
- return NO_MEMORY;
- }
-
- int found = mCore->mFreeBuffers.front();
- mCore->mFreeBuffers.remove(found);
- mCore->mFreeSlots.insert(found);
-
- BQ_LOGV("detachNextBuffer detached slot %d", found);
-
- *outBuffer = mSlots[found].mGraphicBuffer;
- *outFence = mSlots[found].mFence;
- mCore->clearBufferSlotLocked(found);
- VALIDATE_CONSISTENCY();
-
return NO_ERROR;
}
@@ -721,6 +740,7 @@
mSlots[*outSlot].mFence = Fence::NO_FENCE;
mSlots[*outSlot].mRequestBufferCalled = true;
mSlots[*outSlot].mAcquireCalled = false;
+ mSlots[*outSlot].mNeedsReallocation = false;
mCore->mActiveBuffers.insert(found);
VALIDATE_CONSISTENCY();
@@ -732,23 +752,27 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- int64_t timestamp;
+ int64_t requestedPresentTimestamp;
bool isAutoTimestamp;
android_dataspace dataSpace;
Rect crop(Rect::EMPTY_RECT);
int scalingMode;
uint32_t transform;
uint32_t stickyTransform;
- sp<Fence> fence;
- input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode,
- &transform, &fence, &stickyTransform);
+ sp<Fence> acquireFence;
+ bool getFrameTimestamps = false;
+ input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
+ &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
+ &getFrameTimestamps);
Region surfaceDamage = input.getSurfaceDamage();
- if (fence == NULL) {
+ if (acquireFence == NULL) {
BQ_LOGE("queueBuffer: fence is NULL");
return BAD_VALUE;
}
+ auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);
+
switch (scalingMode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
@@ -763,6 +787,7 @@
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
+ uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -801,8 +826,9 @@
BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
" crop=[%d,%d,%d,%d] transform=%#x scale=%s",
- slot, mCore->mFrameCounter + 1, timestamp, dataSpace,
- crop.left, crop.top, crop.right, crop.bottom, transform,
+ slot, mCore->mFrameCounter + 1, requestedPresentTimestamp,
+ dataSpace, crop.left, crop.top, crop.right, crop.bottom,
+ transform,
BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
@@ -820,11 +846,14 @@
dataSpace = mCore->mDefaultBufferDataSpace;
}
- mSlots[slot].mFence = fence;
+ mSlots[slot].mFence = acquireFence;
mSlots[slot].mBufferState.queue();
+ // Increment the frame counter and store a local version of it
+ // for use outside the lock on mCore->mMutex.
++mCore->mFrameCounter;
- mSlots[slot].mFrameNumber = mCore->mFrameCounter;
+ currentFrameNumber = mCore->mFrameCounter;
+ mSlots[slot].mFrameNumber = currentFrameNumber;
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
@@ -834,12 +863,13 @@
item.mTransformToDisplayInverse =
(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
item.mScalingMode = static_cast<uint32_t>(scalingMode);
- item.mTimestamp = timestamp;
+ item.mTimestamp = requestedPresentTimestamp;
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
- item.mFrameNumber = mCore->mFrameCounter;
+ item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
- item.mFence = fence;
+ item.mFence = acquireFence;
+ item.mFenceTime = acquireFenceTime;
item.mIsDroppable = mCore->mAsyncMode ||
mCore->mDequeueBufferCannotBlock ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
@@ -858,6 +888,7 @@
mCore->mSharedBufferCache.dataspace = dataSpace;
}
+ output->bufferReplaced = false;
if (mCore->mQueue.empty()) {
// When the queue is empty, we can ignore mDequeueBufferCannotBlock
// and simply queue this buffer
@@ -884,6 +915,7 @@
if (!mSlots[last.mSlot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(last.mSlot);
mCore->mFreeBuffers.push_back(last.mSlot);
+ output->bufferReplaced = true;
}
}
@@ -900,12 +932,14 @@
mCore->mDequeueCondition.broadcast();
mCore->mLastQueuedSlot = slot;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()),
- mCore->mFrameCounter + 1);
+ output->width = mCore->mDefaultWidth;
+ output->height = mCore->mDefaultHeight;
+ output->transformHint = mCore->mTransformHint;
+ output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
+ output->nextFrameNumber = mCore->mFrameCounter + 1;
- ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+ ATRACE_INT(mCore->mConsumerName.string(),
+ static_cast<int32_t>(mCore->mQueue.size()));
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
// Take a ticket for the callback functions
@@ -914,9 +948,14 @@
VALIDATE_CONSISTENCY();
} // Autolock scope
- // Don't send the GraphicBuffer through the callback, and don't send
- // the slot number, since the consumer shouldn't need it
- item.mGraphicBuffer.clear();
+ // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because
+ // it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and
+ // there will be no Binder call
+ if (!mConsumerIsSurfaceFlinger) {
+ item.mGraphicBuffer.clear();
+ }
+
+ // Don't send the slot number through the callback since the consumer shouldn't need it
item.mSlot = BufferItem::INVALID_BUFFER_SLOT;
// Call back without the main BufferQueue lock held, but with the callback
@@ -940,7 +979,7 @@
connectedApi = mCore->mConnectedApi;
lastQueuedFence = std::move(mLastQueueBufferFence);
- mLastQueueBufferFence = std::move(fence);
+ mLastQueueBufferFence = std::move(acquireFence);
mLastQueuedCrop = item.mCrop;
mLastQueuedTransform = item.mTransform;
@@ -956,6 +995,17 @@
lastQueuedFence->waitForever("Throttling EGL Production");
}
+ // Update and get FrameEventHistory.
+ nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ NewFrameEventsEntry newFrameEventsEntry = {
+ currentFrameNumber,
+ postedTime,
+ requestedPresentTimestamp,
+ std::move(acquireFenceTime)
+ };
+ addAndGetFrameTimestamps(&newFrameEventsEntry,
+ getFrameTimestamps ? &output->frameTimestamps : nullptr);
+
return NO_ERROR;
}
@@ -1038,6 +1088,10 @@
case NATIVE_WINDOW_FORMAT:
value = static_cast<int32_t>(mCore->mDefaultBufferFormat);
break;
+ case NATIVE_WINDOW_LAYER_COUNT:
+ // All BufferQueue buffers have a single layer.
+ value = BQ_LAYER_COUNT;
+ break;
case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
value = mCore->getMinUndequeuedBufferCountLocked();
break;
@@ -1060,6 +1114,9 @@
value = static_cast<int32_t>(mCore->mBufferAge);
}
break;
+ case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
+ value = static_cast<int32_t>(mCore->mConsumerIsProtected);
+ break;
default:
return BAD_VALUE;
}
@@ -1116,10 +1173,14 @@
case NATIVE_WINDOW_API_MEDIA:
case NATIVE_WINDOW_API_CAMERA:
mCore->mConnectedApi = api;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()),
- mCore->mFrameCounter + 1);
+
+ output->width = mCore->mDefaultWidth;
+ output->height = mCore->mDefaultHeight;
+ output->transformHint = mCore->mTransformHint;
+ output->numPendingBuffers =
+ static_cast<uint32_t>(mCore->mQueue.size());
+ output->nextFrameNumber = mCore->mFrameCounter + 1;
+ output->bufferReplaced = false;
if (listener != NULL) {
// Set up a death notification so that we can disconnect
@@ -1181,6 +1242,9 @@
}
if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) {
+ if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) {
+ ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode);
+ }
api = mCore->mConnectedApi;
// If we're asked to disconnect the currently connected api but
// nobody is connected, it's not really an error.
@@ -1215,7 +1279,10 @@
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener;
- } else if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+ } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("disconnect: not connected (req=%d)", api);
+ status = NO_INIT;
+ } else {
BQ_LOGE("disconnect: still connected to another API "
"(cur=%d req=%d)", mCore->mConnectedApi, api);
status = BAD_VALUE;
@@ -1231,6 +1298,7 @@
// Call back without lock held
if (listener != NULL) {
listener->onBuffersReleased();
+ listener->onDisconnect();
}
return status;
@@ -1284,10 +1352,12 @@
Vector<sp<GraphicBuffer>> buffers;
for (size_t i = 0; i < newBufferCount; ++i) {
- status_t result = NO_ERROR;
- sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
- allocWidth, allocHeight, allocFormat, allocUsage,
- {mConsumerName.string(), mConsumerName.size()}, &result));
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+ allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+ allocUsage, {mConsumerName.string(), mConsumerName.size()});
+
+ status_t result = graphicBuffer->initCheck();
+
if (result != NO_ERROR) {
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
" %u, usage %u)", width, height, format, usage);
@@ -1438,20 +1508,27 @@
return NO_ERROR;
}
-bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- ATRACE_CALL();
- BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
- sp<IConsumerListener> listener;
+void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ addAndGetFrameTimestamps(nullptr, outDelta);
+}
+void BufferQueueProducer::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
+ if (newTimestamps == nullptr && outDelta == nullptr) {
+ return;
+ }
+
+ ATRACE_CALL();
+ BQ_LOGV("addAndGetFrameTimestamps");
+ sp<IConsumerListener> listener;
{
Mutex::Autolock lock(mCore->mMutex);
listener = mCore->mConsumerListener;
}
if (listener != NULL) {
- return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
- return false;
}
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 65e4fee..3d36376 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -27,8 +27,9 @@
#include <hardware/hardware.h>
+#include <cutils/atomic.h>
+
#include <gui/BufferItem.h>
-#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/ConsumerBase.h>
@@ -56,7 +57,8 @@
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
mAbandoned(false),
- mConsumer(bufferQueue) {
+ mConsumer(bufferQueue),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
@@ -104,7 +106,7 @@
sp<FrameAvailableListener> listener;
{ // scope for the lock
- Mutex::Autolock lock(mMutex);
+ Mutex::Autolock lock(mFrameAvailableMutex);
listener = mFrameAvailableListener.promote();
}
@@ -119,7 +121,7 @@
sp<FrameAvailableListener> listener;
{
- Mutex::Autolock lock(mMutex);
+ Mutex::Autolock lock(mFrameAvailableMutex);
listener = mFrameAvailableListener.promote();
}
@@ -183,7 +185,7 @@
void ConsumerBase::setFrameAvailableListener(
const wp<FrameAvailableListener>& listener) {
CB_LOGV("setFrameAvailableListener");
- Mutex::Autolock lock(mMutex);
+ Mutex::Autolock lock(mFrameAvailableMutex);
mFrameAvailableListener = listener;
}
@@ -251,14 +253,25 @@
CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!");
return NO_INIT;
}
- return mConsumer->discardFreeBuffers();
+ status_t err = mConsumer->discardFreeBuffers();
+ if (err != OK) {
+ return err;
+ }
+ uint64_t mask;
+ mConsumer->getReleasedBuffers(&mask);
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1ULL << i)) {
+ freeBufferLocked(i);
+ }
+ }
+ return OK;
}
-void ConsumerBase::dump(String8& result) const {
- dump(result, "");
+void ConsumerBase::dumpState(String8& result) const {
+ dumpState(result, "");
}
-void ConsumerBase::dump(String8& result, const char* prefix) const {
+void ConsumerBase::dumpState(String8& result, const char* prefix) const {
Mutex::Autolock _l(mMutex);
dumpLocked(result, prefix);
}
@@ -267,7 +280,9 @@
result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
if (!mAbandoned) {
- mConsumer->dump(result, prefix);
+ String8 consumerState;
+ mConsumer->dumpState(String8(prefix), &consumerState);
+ result.append(consumerState);
}
}
@@ -284,6 +299,9 @@
}
if (item->mGraphicBuffer != NULL) {
+ if (mSlots[item->mSlot].mGraphicBuffer != NULL) {
+ freeBufferLocked(item->mSlot);
+ }
mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;
}
@@ -317,16 +335,16 @@
return OK;
}
- auto signaled = mSlots[slot].mFence->hasSignaled();
+ auto status = mSlots[slot].mFence->getStatus();
- if (!signaled) {
+ if (status == Fence::Status::Invalid) {
CB_LOGE("fence has invalid state");
return BAD_VALUE;
}
- if (*signaled) {
+ if (status == Fence::Status::Signaled) {
mSlots[slot].mFence = fence;
- } else {
+ } else { // status == Fence::Status::Unsignaled
char fenceName[32] = {};
snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
sp<Fence> mergedFence = Fence::merge(
@@ -366,6 +384,7 @@
freeBufferLocked(slot);
}
+ mPrevFinalReleaseFence = mSlots[slot].mFence;
mSlots[slot].mFence = Fence::NO_FENCE;
return err;
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 8393160..ae7c65c 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -64,6 +64,8 @@
switch (static_cast<int>(format)) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
+ case HAL_PIXEL_FORMAT_RGBA_1010102:
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_BGRA_8888:
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 9973e8d..1757ec1 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -18,25 +18,27 @@
#include <utils/Errors.h>
-#include <gui/BitTube.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
+#include <private/gui/BitTube.h>
+
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
-DisplayEventReceiver::DisplayEventReceiver() {
+DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != NULL) {
- mEventConnection = sf->createDisplayEventConnection();
+ mEventConnection = sf->createDisplayEventConnection(vsyncSource);
if (mEventConnection != NULL) {
- mDataChannel = mEventConnection->getDataChannel();
+ mDataChannel = std::make_unique<gui::BitTube>();
+ mEventConnection->stealReceiveChannel(mDataChannel.get());
}
}
}
@@ -79,19 +81,19 @@
ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
size_t count) {
- return DisplayEventReceiver::getEvents(mDataChannel, events, count);
+ return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
}
-ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel,
+ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel,
Event* events, size_t count)
{
- return BitTube::recvObjects(dataChannel, events, count);
+ return gui::BitTube::recvObjects(dataChannel, events, count);
}
-ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
+ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
Event const* events, size_t count)
{
- return BitTube::sendObjects(dataChannel, events, count);
+ return gui::BitTube::sendObjects(dataChannel, events, count);
}
// ---------------------------------------------------------------------------
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000..fccca97
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,701 @@
+/*
+* 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.
+*/
+
+#include <gui/FrameTimestamps.h>
+
+#define LOG_TAG "FrameEvents"
+
+#include <cutils/compiler.h> // For CC_[UN]LIKELY
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <algorithm>
+#include <limits>
+#include <numeric>
+
+namespace android {
+
+
+// ============================================================================
+// FrameEvents
+// ============================================================================
+
+bool FrameEvents::hasPostedInfo() const {
+ return FrameEvents::isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+ return FrameEvents::isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+ return FrameEvents::isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+ return FrameEvents::isValidTimestamp(firstRefreshStartTime);
+}
+
+bool FrameEvents::hasLastRefreshStartInfo() const {
+ // The last refresh start time may continue to update until a new frame
+ // is latched. We know we have the final value once the release info is set.
+ return addReleaseCalled;
+}
+
+bool FrameEvents::hasDequeueReadyInfo() const {
+ return FrameEvents::isValidTimestamp(dequeueReadyTime);
+}
+
+bool FrameEvents::hasAcquireInfo() const {
+ return acquireFence->isValid();
+}
+
+bool FrameEvents::hasGpuCompositionDoneInfo() const {
+ // We may not get a gpuCompositionDone in addPostComposite if
+ // client/gles compositing isn't needed.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayPresentInfo() const {
+ // We may not get a displayPresent in addPostComposite for HWC1.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasReleaseInfo() const {
+ return addReleaseCalled;
+}
+
+void FrameEvents::checkFencesForCompletion() {
+ acquireFence->getSignalTime();
+ gpuCompositionDoneFence->getSignalTime();
+ displayPresentFence->getSignalTime();
+ releaseFence->getSignalTime();
+}
+
+static void dumpFenceTime(String8& outString, const char* name,
+ bool pending, const FenceTime& fenceTime) {
+ outString.appendFormat("--- %s", name);
+ nsecs_t signalTime = fenceTime.getCachedSignalTime();
+ if (Fence::isValidTimestamp(signalTime)) {
+ outString.appendFormat("%" PRId64 "\n", signalTime);
+ } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) {
+ outString.appendFormat("Pending\n");
+ } else if (&fenceTime == FenceTime::NO_FENCE.get()){
+ outString.appendFormat("N/A\n");
+ } else {
+ outString.appendFormat("Error\n");
+ }
+}
+
+void FrameEvents::dump(String8& outString) const
+{
+ if (!valid) {
+ return;
+ }
+
+ outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
+ outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime);
+ outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
+
+ outString.appendFormat("--- Latched \t");
+ if (FrameEvents::isValidTimestamp(latchTime)) {
+ outString.appendFormat("%" PRId64 "\n", latchTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (First)\t");
+ if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (Last)\t");
+ if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ dumpFenceTime(outString, "Acquire \t",
+ true, *acquireFence);
+ dumpFenceTime(outString, "GPU Composite Done\t",
+ !addPostCompositeCalled, *gpuCompositionDoneFence);
+ dumpFenceTime(outString, "Display Present \t",
+ !addPostCompositeCalled, *displayPresentFence);
+
+ outString.appendFormat("--- DequeueReady \t");
+ if (FrameEvents::isValidTimestamp(dequeueReadyTime)) {
+ outString.appendFormat("%" PRId64 "\n", dequeueReadyTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ dumpFenceTime(outString, "Release \t",
+ true, *releaseFence);
+}
+
+
+// ============================================================================
+// FrameEventHistory
+// ============================================================================
+
+namespace {
+
+struct FrameNumberEqual {
+ FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {}
+ bool operator()(const FrameEvents& frame) {
+ return frame.valid && mFrameNumber == frame.frameNumber;
+ }
+ const uint64_t mFrameNumber;
+};
+
+} // namespace
+
+FrameEventHistory::~FrameEventHistory() = default;
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
+ auto frame = std::find_if(
+ mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber));
+ return frame == mFrames.end() ? nullptr : &(*frame);
+}
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) {
+ *iHint = std::min(*iHint, mFrames.size());
+ auto hint = mFrames.begin() + *iHint;
+ auto frame = std::find_if(
+ hint, mFrames.end(), FrameNumberEqual(frameNumber));
+ if (frame == mFrames.end()) {
+ frame = std::find_if(
+ mFrames.begin(), hint, FrameNumberEqual(frameNumber));
+ if (frame == hint) {
+ return nullptr;
+ }
+ }
+ *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+ return &(*frame);
+}
+
+void FrameEventHistory::checkFencesForCompletion() {
+ for (auto& frame : mFrames) {
+ frame.checkFencesForCompletion();
+ }
+}
+
+// Uses !|valid| as the MSB.
+static bool FrameNumberLessThan(
+ const FrameEvents& lhs, const FrameEvents& rhs) {
+ if (lhs.valid == rhs.valid) {
+ return lhs.frameNumber < rhs.frameNumber;
+ }
+ return lhs.valid;
+}
+
+void FrameEventHistory::dump(String8& outString) const {
+ auto earliestFrame = std::min_element(
+ mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+ if (!earliestFrame->valid) {
+ outString.appendFormat("-- N/A\n");
+ return;
+ }
+ for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+ frame->dump(outString);
+ }
+ for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+ frame->dump(outString);
+ }
+}
+
+
+// ============================================================================
+// ProducerFrameEventHistory
+// ============================================================================
+
+ProducerFrameEventHistory::~ProducerFrameEventHistory() = default;
+
+nsecs_t ProducerFrameEventHistory::snapToNextTick(
+ nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) {
+ nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval;
+ // Integer modulo rounds towards 0 and not -inf before taking the remainder,
+ // so adjust the offset if it is negative.
+ if (tickOffset < 0) {
+ tickOffset += tickInterval;
+ }
+ return timestamp + tickOffset;
+}
+
+nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline(
+ const nsecs_t now) const{
+ return snapToNextTick(
+ now, mCompositorTiming.deadline, mCompositorTiming.interval);
+}
+
+void ProducerFrameEventHistory::updateAcquireFence(
+ uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) {
+ FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
+ if (frame == nullptr) {
+ ALOGE("updateAcquireFence: Did not find frame.");
+ return;
+ }
+
+ if (acquire->isValid()) {
+ mAcquireTimeline.push(acquire);
+ frame->acquireFence = std::move(acquire);
+ } else {
+ // If there isn't an acquire fence, assume that buffer was
+ // ready for the consumer when posted.
+ frame->acquireFence = std::make_shared<FenceTime>(frame->postedTime);
+ }
+}
+
+void ProducerFrameEventHistory::applyDelta(
+ const FrameEventHistoryDelta& delta) {
+ mCompositorTiming = delta.mCompositorTiming;
+
+ for (auto& d : delta.mDeltas) {
+ // Avoid out-of-bounds access.
+ if (CC_UNLIKELY(d.mIndex >= mFrames.size())) {
+ ALOGE("applyDelta: Bad index.");
+ return;
+ }
+
+ FrameEvents& frame = mFrames[d.mIndex];
+
+ frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
+ frame.addReleaseCalled = d.mAddReleaseCalled != 0;
+
+ frame.postedTime = d.mPostedTime;
+ frame.requestedPresentTime = d.mRequestedPresentTime;
+ 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.
+ frame.frameNumber = d.mFrameNumber;
+ frame.acquireFence = FenceTime::NO_FENCE;
+ frame.gpuCompositionDoneFence = FenceTime::NO_FENCE;
+ frame.displayPresentFence = FenceTime::NO_FENCE;
+ frame.releaseFence = FenceTime::NO_FENCE;
+ // The consumer only sends valid frames.
+ frame.valid = true;
+ }
+
+ applyFenceDelta(&mGpuCompositionDoneTimeline,
+ &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence);
+ applyFenceDelta(&mPresentTimeline,
+ &frame.displayPresentFence, d.mDisplayPresentFence);
+ applyFenceDelta(&mReleaseTimeline,
+ &frame.releaseFence, d.mReleaseFence);
+ }
+}
+
+void ProducerFrameEventHistory::updateSignalTimes() {
+ mAcquireTimeline.updateSignalTimes();
+ mGpuCompositionDoneTimeline.updateSignalTimes();
+ mPresentTimeline.updateSignalTimes();
+ mReleaseTimeline.updateSignalTimes();
+}
+
+void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline,
+ std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) const {
+ if (CC_UNLIKELY(dst == nullptr || dst->get() == nullptr)) {
+ ALOGE("applyFenceDelta: dst is null.");
+ return;
+ }
+
+ switch (src.state) {
+ case FenceTime::Snapshot::State::EMPTY:
+ return;
+ case FenceTime::Snapshot::State::FENCE:
+ ALOGE_IF((*dst)->isValid(), "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
+// ============================================================================
+
+ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
+
+void ConsumerFrameEventHistory::onDisconnect() {
+ mCurrentConnectId++;
+ mProducerWantsEvents = false;
+}
+
+void ConsumerFrameEventHistory::initializeCompositorTiming(
+ const CompositorTiming& compositorTiming) {
+ mCompositorTiming = compositorTiming;
+}
+
+void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) {
+ // Overwrite all fields of the frame with default values unless set here.
+ FrameEvents newTimestamps;
+ newTimestamps.connectId = mCurrentConnectId;
+ newTimestamps.frameNumber = newEntry.frameNumber;
+ newTimestamps.postedTime = newEntry.postedTime;
+ newTimestamps.requestedPresentTime = newEntry.requestedPresentTime;
+ newTimestamps.acquireFence = newEntry.acquireFence;
+ newTimestamps.valid = true;
+ mFrames[mQueueOffset] = newTimestamps;
+
+ // Note: We avoid sending the acquire fence back to the caller since
+ // they have the original one already, so there is no need to set the
+ // acquire dirty bit.
+ mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>();
+
+ mQueueOffset = (mQueueOffset + 1) % mFrames.size();
+}
+
+void ConsumerFrameEventHistory::addLatch(
+ uint64_t frameNumber, nsecs_t latchTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents, "addLatch: Did not find frame.");
+ return;
+ }
+ frame->latchTime = latchTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>();
+}
+
+void ConsumerFrameEventHistory::addPreComposition(
+ uint64_t frameNumber, nsecs_t refreshStartTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents,
+ "addPreComposition: Did not find frame.");
+ return;
+ }
+ frame->lastRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
+ if (!FrameEvents::isValidTimestamp(frame->firstRefreshStartTime)) {
+ frame->firstRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
+ }
+}
+
+void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& gpuCompositionDone,
+ const std::shared_ptr<FenceTime>& displayPresent,
+ const CompositorTiming& compositorTiming) {
+ mCompositorTiming = compositorTiming;
+
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents,
+ "addPostComposition: Did not find frame.");
+ return;
+ }
+ // Only get GPU and present info for the first composite.
+ if (!frame->addPostCompositeCalled) {
+ frame->addPostCompositeCalled = true;
+ frame->gpuCompositionDoneFence = gpuCompositionDone;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GPU_COMPOSITION_DONE>();
+ if (!frame->displayPresentFence->isValid()) {
+ frame->displayPresentFence = displayPresent;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
+ }
+ }
+}
+
+void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber,
+ nsecs_t dequeueReadyTime, std::shared_ptr<FenceTime>&& release) {
+ FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents, "addRelease: Did not find frame.");
+ return;
+ }
+ frame->addReleaseCalled = true;
+ frame->dequeueReadyTime = dequeueReadyTime;
+ frame->releaseFence = std::move(release);
+ mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
+}
+
+void ConsumerFrameEventHistory::getFrameDelta(
+ FrameEventHistoryDelta* delta,
+ const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) {
+ mProducerWantsEvents = true;
+ size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+ if (mFramesDirty[i].anyDirty()) {
+ // Make sure only to send back deltas for the current connection
+ // since the producer won't have the correct state to apply a delta
+ // from a previous connection.
+ if (mFrames[i].connectId == mCurrentConnectId) {
+ delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]);
+ }
+ mFramesDirty[i].reset();
+ }
+}
+
+void ConsumerFrameEventHistory::getAndResetDelta(
+ FrameEventHistoryDelta* delta) {
+ mProducerWantsEvents = true;
+ delta->mCompositorTiming = mCompositorTiming;
+
+ // Write these in order of frame number so that it is easy to
+ // add them to a FenceTimeline in the proper order producer side.
+ delta->mDeltas.reserve(mFramesDirty.size());
+ auto earliestFrame = std::min_element(
+ mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+ for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+ getFrameDelta(delta, frame);
+ }
+ for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+ getFrameDelta(delta, frame);
+ }
+}
+
+
+// ============================================================================
+// FrameEventsDelta
+// ============================================================================
+
+FrameEventsDelta::FrameEventsDelta(
+ size_t index,
+ const FrameEvents& frameTimestamps,
+ const FrameEventDirtyFields& dirtyFields)
+ : mIndex(index),
+ mFrameNumber(frameTimestamps.frameNumber),
+ mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled),
+ mAddReleaseCalled(frameTimestamps.addReleaseCalled),
+ mPostedTime(frameTimestamps.postedTime),
+ mRequestedPresentTime(frameTimestamps.requestedPresentTime),
+ mLatchTime(frameTimestamps.latchTime),
+ mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime),
+ mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime),
+ mDequeueReadyTime(frameTimestamps.dequeueReadyTime) {
+ if (dirtyFields.isDirty<FrameEvent::GPU_COMPOSITION_DONE>()) {
+ mGpuCompositionDoneFence =
+ frameTimestamps.gpuCompositionDoneFence->getSnapshot();
+ }
+ if (dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>()) {
+ mDisplayPresentFence =
+ frameTimestamps.displayPresentFence->getSnapshot();
+ }
+ if (dirtyFields.isDirty<FrameEvent::RELEASE>()) {
+ mReleaseFence = frameTimestamps.releaseFence->getSnapshot();
+ }
+}
+
+constexpr size_t FrameEventsDelta::minFlattenedSize() {
+ return sizeof(FrameEventsDelta::mFrameNumber) +
+ sizeof(uint16_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(FrameEventsDelta::mPostedTime) +
+ sizeof(FrameEventsDelta::mRequestedPresentTime) +
+ sizeof(FrameEventsDelta::mLatchTime) +
+ sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
+ sizeof(FrameEventsDelta::mLastRefreshStartTime) +
+ sizeof(FrameEventsDelta::mDequeueReadyTime);
+}
+
+// Flattenable implementation
+size_t FrameEventsDelta::getFlattenedSize() const {
+ auto fences = allFences(this);
+ return minFlattenedSize() +
+ std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const FenceTime::Snapshot* fence) {
+ return a + fence->getFlattenedSize();
+ });
+}
+
+size_t FrameEventsDelta::getFdCount() const {
+ auto fences = allFences(this);
+ return std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const FenceTime::Snapshot* fence) {
+ return a + fence->getFdCount();
+ });
+}
+
+status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const {
+ if (size < getFlattenedSize() || count < getFdCount()) {
+ return NO_MEMORY;
+ }
+
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
+ mIndex > std::numeric_limits<uint16_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, mFrameNumber);
+
+ // These are static_cast to uint16_t/uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint16_t>(mIndex));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, mPostedTime);
+ FlattenableUtils::write(buffer, size, mRequestedPresentTime);
+ 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)) {
+ status_t status = fence->flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size,
+ int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mFrameNumber);
+
+ // These were written as uint16_t/uint8_t for alignment.
+ uint16_t temp16 = 0;
+ FlattenableUtils::read(buffer, size, temp16);
+ mIndex = temp16;
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ uint8_t temp8 = 0;
+ FlattenableUtils::read(buffer, size, temp8);
+ mAddPostCompositeCalled = static_cast<bool>(temp8);
+ FlattenableUtils::read(buffer, size, temp8);
+ mAddReleaseCalled = static_cast<bool>(temp8);
+
+ FlattenableUtils::read(buffer, size, mPostedTime);
+ FlattenableUtils::read(buffer, size, mRequestedPresentTime);
+ 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)) {
+ status_t status = fence->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+// ============================================================================
+// FrameEventHistoryDelta
+// ============================================================================
+
+FrameEventHistoryDelta& FrameEventHistoryDelta::operator=(
+ FrameEventHistoryDelta&& src) {
+ mCompositorTiming = src.mCompositorTiming;
+
+ if (CC_UNLIKELY(!mDeltas.empty())) {
+ ALOGE("FrameEventHistoryDelta assign clobbering history.");
+ }
+ mDeltas = std::move(src.mDeltas);
+ ALOGE_IF(src.mDeltas.empty(), "Source mDeltas not empty.");
+ return *this;
+}
+
+constexpr size_t FrameEventHistoryDelta::minFlattenedSize() {
+ return sizeof(uint32_t) + // mDeltas.size()
+ sizeof(mCompositorTiming);
+}
+
+size_t FrameEventHistoryDelta::getFlattenedSize() const {
+ return minFlattenedSize() +
+ std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFlattenedSize();
+ });
+}
+
+size_t FrameEventHistoryDelta::getFdCount() const {
+ return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFdCount();
+ });
+}
+
+status_t FrameEventHistoryDelta::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, mCompositorTiming);
+
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint32_t>(mDeltas.size()));
+ for (auto& d : mDeltas) {
+ status_t status = d.flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventHistoryDelta::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mCompositorTiming);
+
+ uint32_t deltaCount = 0;
+ FlattenableUtils::read(buffer, size, deltaCount);
+ if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ mDeltas.resize(deltaCount);
+ for (auto& d : mDeltas) {
+ status_t status = d.unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+} // namespace android
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 10e999c..c654f08 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -31,7 +31,6 @@
#include <gui/BufferItem.h>
#include <gui/GLConsumer.h>
-#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
@@ -157,6 +156,7 @@
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mCurrentFence(Fence::NO_FENCE),
mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
mCurrentFrameNumber(0),
mDefaultWidth(1),
mDefaultHeight(1),
@@ -185,6 +185,7 @@
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mCurrentFence(Fence::NO_FENCE),
mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
mCurrentFrameNumber(0),
mDefaultWidth(1),
mDefaultHeight(1),
@@ -321,7 +322,9 @@
mCurrentCrop.makeInvalid();
mCurrentTransform = 0;
mCurrentTimestamp = 0;
+ mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
mCurrentFence = Fence::NO_FENCE;
+ mCurrentFenceTime = FenceTime::NO_FENCE;
if (mAttached) {
// This binds a dummy buffer (mReleasedTexImage).
@@ -487,7 +490,9 @@
mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode;
mCurrentTimestamp = item.mTimestamp;
+ mCurrentDataSpace = item.mDataSpace;
mCurrentFence = item.mFence;
+ mCurrentFenceTime = item.mFenceTime;
mCurrentFrameNumber = item.mFrameNumber;
computeCurrentTransformMatrixLocked();
@@ -856,6 +861,8 @@
switch (buf->getPixelFormat()) {
case PIXEL_FORMAT_RGBA_8888:
case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
case PIXEL_FORMAT_RGB_888:
case PIXEL_FORMAT_RGB_565:
case PIXEL_FORMAT_BGRA_8888:
@@ -911,15 +918,26 @@
return mCurrentTimestamp;
}
+android_dataspace GLConsumer::getCurrentDataSpace() {
+ GLC_LOGV("getCurrentDataSpace");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentDataSpace;
+}
+
uint64_t GLConsumer::getFrameNumber() {
GLC_LOGV("getFrameNumber");
Mutex::Autolock lock(mMutex);
return mCurrentFrameNumber;
}
-sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
+sp<GraphicBuffer> GLConsumer::getCurrentBuffer(int* outSlot) const {
Mutex::Autolock lock(mMutex);
- return (mCurrentTextureImage == NULL) ?
+
+ if (outSlot != nullptr) {
+ *outSlot = mCurrentTexture;
+ }
+
+ return (mCurrentTextureImage == nullptr) ?
NULL : mCurrentTextureImage->graphicBuffer();
}
@@ -981,6 +999,11 @@
return mCurrentFence;
}
+std::shared_ptr<FenceTime> GLConsumer::getCurrentFenceTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFenceTime;
+}
+
status_t GLConsumer::doGLFenceWait() const {
Mutex::Autolock lock(mMutex);
return doGLFenceWaitLocked();
diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp
deleted file mode 100644
index e6150f4..0000000
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- **
- ** Copyright 2012 The Android Open Source Project
- **
- ** Licensed under the Apache License Version 2.0(the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing software
- ** distributed under the License is distributed on an "AS IS" BASIS
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#include <cutils/log.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <gui/GraphicBufferAlloc.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-GraphicBufferAlloc::GraphicBufferAlloc() {
-}
-
-GraphicBufferAlloc::~GraphicBufferAlloc() {
-}
-
-sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage,
- std::string requestorName, status_t* error) {
- sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
- width, height, format, usage, std::move(requestorName)));
- status_t err = graphicBuffer->initCheck();
- *error = err;
- if (err != 0 || graphicBuffer->handle == 0) {
- if (err == NO_MEMORY) {
- GraphicBuffer::dumpAllocationsToSystemLog();
- }
- ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
- "failed (%s), handle=%p",
- width, height, strerror(-err), graphicBuffer->handle);
- return 0;
- }
- return graphicBuffer;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/libs/gui/GraphicsEnv.cpp b/libs/gui/GraphicsEnv.cpp
deleted file mode 100644
index 68f0f98..0000000
--- a/libs/gui/GraphicsEnv.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 9a06011..85ac304 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -14,146 +14,86 @@
* limitations under the License.
*/
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
#include <gui/IConsumerListener.h>
+
#include <gui/BufferItem.h>
-// ---------------------------------------------------------------------------
namespace android {
-// ---------------------------------------------------------------------------
-enum {
- ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION,
- ON_BUFFER_RELEASED,
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
+ ON_FRAME_AVAILABLE,
+ ON_FRAME_REPLACED,
+ ON_BUFFERS_RELEASED,
ON_SIDEBAND_STREAM_CHANGED,
- GET_FRAME_TIMESTAMPS
+ LAST = ON_SIDEBAND_STREAM_CHANGED,
};
-class BpConsumerListener : public BpInterface<IConsumerListener>
-{
+} // Anonymous namespace
+
+class BpConsumerListener : public SafeBpInterface<IConsumerListener> {
public:
- BpConsumerListener(const sp<IBinder>& impl)
- : BpInterface<IConsumerListener>(impl) {
+ explicit BpConsumerListener(const sp<IBinder>& impl)
+ : SafeBpInterface<IConsumerListener>(impl, "BpConsumerListener") {}
+
+ ~BpConsumerListener() override;
+
+ void onDisconnect() override {
+ callRemoteAsync<decltype(&IConsumerListener::onDisconnect)>(Tag::ON_DISCONNECT);
}
- virtual ~BpConsumerListener();
-
- virtual void onFrameAvailable(const BufferItem& item) {
- Parcel data, reply;
- data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
- data.write(item);
- remote()->transact(ON_FRAME_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY);
+ void onFrameAvailable(const BufferItem& item) override {
+ callRemoteAsync<decltype(&IConsumerListener::onFrameAvailable)>(Tag::ON_FRAME_AVAILABLE,
+ item);
}
- virtual void onBuffersReleased() {
- Parcel data, reply;
- data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
- remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
+ void onFrameReplaced(const BufferItem& item) override {
+ callRemoteAsync<decltype(&IConsumerListener::onFrameReplaced)>(Tag::ON_FRAME_REPLACED,
+ item);
}
- virtual void onSidebandStreamChanged() {
- Parcel data, reply;
- data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
- remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
+ void onBuffersReleased() override {
+ callRemoteAsync<decltype(&IConsumerListener::onBuffersReleased)>(Tag::ON_BUFFERS_RELEASED);
}
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IConsumerListener::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write token: %d", result);
- return false;
- }
- result = data.writeUint64(frameNumber);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write: %d", result);
- return false;
- }
- result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to transact: %d", result);
- return false;
- }
- bool found = false;
- result = reply.readBool(&found);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read: %d", result);
- return false;
- }
- if (found) {
- result = reply.read(*outTimestamps);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read timestamps: %d",
- result);
- return false;
- }
- }
- return found;
+ void onSidebandStreamChanged() override {
+ callRemoteAsync<decltype(&IConsumerListener::onSidebandStreamChanged)>(
+ Tag::ON_SIDEBAND_STREAM_CHANGED);
+ }
+
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/,
+ FrameEventHistoryDelta* /*outDelta*/) override {
+ LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied");
}
};
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpConsumerListener::~BpConsumerListener() {}
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpConsumerListener::~BpConsumerListener() = default;
+ConsumerListener::~ConsumerListener() = default;
IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");
-// ----------------------------------------------------------------------
-
-status_t BnConsumerListener::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case ON_FRAME_AVAILABLE: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- BufferItem item;
- data.read(item);
- onFrameAvailable(item);
- return NO_ERROR; }
- case ON_BUFFER_RELEASED: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- onBuffersReleased();
- return NO_ERROR; }
- case ON_SIDEBAND_STREAM_CHANGED: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- onSidebandStreamChanged();
- return NO_ERROR; }
- case GET_FRAME_TIMESTAMPS: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- uint64_t frameNumber = 0;
- status_t result = data.readUint64(&frameNumber);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to read: %d", result);
- return result;
- }
- FrameTimestamps timestamps;
- bool found = getFrameTimestamps(frameNumber, ×tamps);
- result = reply->writeBool(found);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write: %d", result);
- return result;
- }
- if (found) {
- result = reply->write(timestamps);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write timestamps: %d", result);
- return result;
- }
- }
- return NO_ERROR;
- }
+status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
}
- return BBinder::onTransact(code, data, reply, flags);
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ON_DISCONNECT:
+ return callLocalAsync(data, reply, &IConsumerListener::onDisconnect);
+ case Tag::ON_FRAME_AVAILABLE:
+ return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable);
+ case Tag::ON_FRAME_REPLACED:
+ return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced);
+ case Tag::ON_BUFFERS_RELEASED:
+ return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased);
+ case Tag::ON_SIDEBAND_STREAM_CHANGED:
+ return callLocalAsync(data, reply, &IConsumerListener::onSidebandStreamChanged);
+ }
}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index 9890f44..c0e246f 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -14,91 +14,67 @@
* limitations under the License.
*/
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
#include <gui/IDisplayEventConnection.h>
-#include <gui/BitTube.h>
+
+#include <private/gui/BitTube.h>
namespace android {
-// ----------------------------------------------------------------------------
-enum {
- GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
SET_VSYNC_RATE,
- REQUEST_NEXT_VSYNC
+ REQUEST_NEXT_VSYNC,
+ LAST = REQUEST_NEXT_VSYNC,
};
-class BpDisplayEventConnection : public BpInterface<IDisplayEventConnection>
-{
+} // Anonymous namespace
+
+class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> {
public:
- BpDisplayEventConnection(const sp<IBinder>& impl)
- : BpInterface<IDisplayEventConnection>(impl)
- {
+ explicit BpDisplayEventConnection(const sp<IBinder>& impl)
+ : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {}
+
+ ~BpDisplayEventConnection() override;
+
+ status_t stealReceiveChannel(gui::BitTube* outChannel) override {
+ return callRemote<decltype(
+ &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL,
+ outChannel);
}
- virtual ~BpDisplayEventConnection();
-
- virtual sp<BitTube> getDataChannel() const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
- remote()->transact(GET_DATA_CHANNEL, data, &reply);
- return new BitTube(reply);
+ status_t setVsyncRate(uint32_t count) override {
+ return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE,
+ count);
}
- virtual void setVsyncRate(uint32_t count) {
- Parcel data, reply;
- data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
- data.writeUint32(count);
- remote()->transact(SET_VSYNC_RATE, data, &reply);
- }
-
- virtual void requestNextVsync() {
- Parcel data, reply;
- data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
- remote()->transact(REQUEST_NEXT_VSYNC, data, &reply, IBinder::FLAG_ONEWAY);
+ void requestNextVsync() override {
+ callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
+ Tag::REQUEST_NEXT_VSYNC);
}
};
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpDisplayEventConnection::~BpDisplayEventConnection() {}
+// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpDisplayEventConnection::~BpDisplayEventConnection() = default;
IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
-// ----------------------------------------------------------------------------
-
-status_t BnDisplayEventConnection::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case GET_DATA_CHANNEL: {
- CHECK_INTERFACE(IDisplayEventConnection, data, reply);
- sp<BitTube> channel(getDataChannel());
- channel->writeToParcel(reply);
- return NO_ERROR;
- }
- case SET_VSYNC_RATE: {
- CHECK_INTERFACE(IDisplayEventConnection, data, reply);
- setVsyncRate(data.readUint32());
- return NO_ERROR;
- }
- case REQUEST_NEXT_VSYNC: {
- CHECK_INTERFACE(IDisplayEventConnection, data, reply);
- requestNextVsync();
- return NO_ERROR;
- }
+status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
}
- return BBinder::onTransact(code, data, reply, flags);
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::STEAL_RECEIVE_CHANNEL:
+ return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel);
+ case Tag::SET_VSYNC_RATE:
+ return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
+ case Tag::REQUEST_NEXT_VSYNC:
+ return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
+ }
}
-// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
deleted file mode 100644
index 7b3b7c1..0000000
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// tag as surfaceflinger
-#define LOG_TAG "SurfaceFlinger"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <gui/IGraphicBufferAlloc.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-enum {
- CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
-};
-
-class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc>
-{
-public:
- BpGraphicBufferAlloc(const sp<IBinder>& impl)
- : BpInterface<IGraphicBufferAlloc>(impl)
- {
- }
-
- virtual ~BpGraphicBufferAlloc();
-
- virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage,
- std::string requestorName, status_t* error) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor());
- data.writeUint32(width);
- data.writeUint32(height);
- data.writeInt32(static_cast<int32_t>(format));
- data.writeUint32(usage);
- if (requestorName.empty()) {
- requestorName += "[PID ";
- requestorName += std::to_string(getpid());
- requestorName += ']';
- }
- data.writeUtf8AsUtf16(requestorName);
- remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply);
- sp<GraphicBuffer> graphicBuffer;
- status_t result = reply.readInt32();
- if (result == NO_ERROR) {
- graphicBuffer = new GraphicBuffer();
- result = reply.read(*graphicBuffer);
- if (result != NO_ERROR) {
- graphicBuffer.clear();
- }
- // reply.readStrongBinder();
- // here we don't even have to read the BufferReference from
- // the parcel, it'll die with the parcel.
- }
- *error = result;
- return graphicBuffer;
- }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpGraphicBufferAlloc::~BpGraphicBufferAlloc() {}
-
-IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc");
-
-// ----------------------------------------------------------------------
-
-status_t BnGraphicBufferAlloc::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- // codes that don't require permission check
-
- // BufferReference just keeps a strong reference to a GraphicBuffer until it
- // is destroyed (that is, until no local or remote process have a reference
- // to it).
- class BufferReference : public BBinder {
- sp<GraphicBuffer> mBuffer;
- public:
- BufferReference(const sp<GraphicBuffer>& buffer) : mBuffer(buffer) {}
- };
-
-
- switch (code) {
- case CREATE_GRAPHIC_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
- uint32_t width = data.readUint32();
- uint32_t height = data.readUint32();
- PixelFormat format = static_cast<PixelFormat>(data.readInt32());
- uint32_t usage = data.readUint32();
- status_t error = NO_ERROR;
- std::string requestorName;
- data.readUtf8FromUtf16(&requestorName);
- sp<GraphicBuffer> result = createGraphicBuffer(width, height,
- format, usage, requestorName, &error);
- reply->writeInt32(error);
- if (result != 0) {
- reply->write(*result);
- // We add a BufferReference to this parcel to make sure the
- // buffer stays alive until the GraphicBuffer object on
- // the other side has been created.
- // This is needed so that the buffer handle can be
- // registered before the buffer is destroyed on implementations
- // that do not use file-descriptors to track their buffers.
- reply->writeStrongBinder( new BufferReference(result) );
- }
- return NO_ERROR;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-}; // namespace android
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index c8eff00..a573bee 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -14,27 +14,24 @@
* limitations under the License.
*/
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/NativeHandle.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
+#include <gui/IGraphicBufferConsumer.h>
#include <gui/BufferItem.h>
#include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferConsumer.h>
-#include <ui/GraphicBuffer.h>
+#include <binder/Parcel.h>
+
#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
-#include <system/window.h>
+#include <utils/NativeHandle.h>
+#include <utils/String8.h>
namespace android {
-enum {
+namespace { // Anonymous namespace
+
+enum class Tag : uint32_t {
ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
DETACH_BUFFER,
ATTACH_BUFFER,
@@ -49,443 +46,185 @@
SET_DEFAULT_BUFFER_FORMAT,
SET_DEFAULT_BUFFER_DATA_SPACE,
SET_CONSUMER_USAGE_BITS,
+ SET_CONSUMER_IS_PROTECTED,
SET_TRANSFORM_HINT,
GET_SIDEBAND_STREAM,
GET_OCCUPANCY_HISTORY,
DISCARD_FREE_BUFFERS,
- DUMP,
+ DUMP_STATE,
+ LAST = DUMP_STATE,
};
+} // Anonymous namespace
-class BpGraphicBufferConsumer : public BpInterface<IGraphicBufferConsumer>
-{
+class BpGraphicBufferConsumer : public SafeBpInterface<IGraphicBufferConsumer> {
public:
- BpGraphicBufferConsumer(const sp<IBinder>& impl)
- : BpInterface<IGraphicBufferConsumer>(impl)
- {
+ explicit BpGraphicBufferConsumer(const sp<IBinder>& impl)
+ : SafeBpInterface<IGraphicBufferConsumer>(impl, "BpGraphicBufferConsumer") {}
+
+ ~BpGraphicBufferConsumer() override;
+
+ status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
+ uint64_t maxFrameNumber) override {
+ using Signature = decltype(&IGraphicBufferConsumer::acquireBuffer);
+ return callRemote<Signature>(Tag::ACQUIRE_BUFFER, buffer, presentWhen, maxFrameNumber);
}
- virtual ~BpGraphicBufferConsumer();
-
- virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen,
- uint64_t maxFrameNumber) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt64(presentWhen);
- data.writeUint64(maxFrameNumber);
- status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- result = reply.read(*buffer);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t detachBuffer(int slot) override {
+ using Signature = decltype(&IGraphicBufferConsumer::detachBuffer);
+ return callRemote<Signature>(Tag::DETACH_BUFFER, slot);
}
- virtual status_t detachBuffer(int slot) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(slot);
- status_t result = remote()->transact(DETACH_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- result = reply.readInt32();
- return result;
+ status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) override {
+ using Signature = decltype(&IGraphicBufferConsumer::attachBuffer);
+ return callRemote<Signature>(Tag::ATTACH_BUFFER, slot, buffer);
}
- virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.write(*buffer.get());
- status_t result = remote()->transact(ATTACH_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- *slot = reply.readInt32();
- result = reply.readInt32();
- return result;
+ status_t releaseBuffer(int buf, uint64_t frameNumber,
+ EGLDisplay display __attribute__((unused)),
+ EGLSyncKHR fence __attribute__((unused)),
+ const sp<Fence>& releaseFence) override {
+ return callRemote<ReleaseBuffer>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence);
}
- virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
- EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)),
- const sp<Fence>& releaseFence) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(buf);
- data.writeInt64(static_cast<int64_t>(frameNumber));
- data.write(*releaseFence);
- status_t result = remote()->transact(RELEASE_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override {
+ using Signature = decltype(&IGraphicBufferConsumer::consumerConnect);
+ return callRemote<Signature>(Tag::CONSUMER_CONNECT, consumer, controlledByApp);
}
- virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(consumer));
- data.writeInt32(controlledByApp);
- status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t consumerDisconnect() override {
+ return callRemote<decltype(&IGraphicBufferConsumer::consumerDisconnect)>(
+ Tag::CONSUMER_DISCONNECT);
}
- virtual status_t consumerDisconnect() {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t getReleasedBuffers(uint64_t* slotMask) override {
+ using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffers);
+ return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask);
}
- virtual status_t getReleasedBuffers(uint64_t* slotMask) {
- Parcel data, reply;
- if (slotMask == NULL) {
- ALOGE("getReleasedBuffers: slotMask must not be NULL");
- return BAD_VALUE;
- }
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- *slotMask = static_cast<uint64_t>(reply.readInt64());
- return reply.readInt32();
+ status_t setDefaultBufferSize(uint32_t width, uint32_t height) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize);
+ return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height);
}
- virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeUint32(width);
- data.writeUint32(height);
- status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setMaxBufferCount(int bufferCount) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount);
+ return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount);
}
- virtual status_t setMaxBufferCount(int bufferCount) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(bufferCount);
- status_t result = remote()->transact(SET_MAX_BUFFER_COUNT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setMaxAcquiredBufferCount);
+ return callRemote<Signature>(Tag::SET_MAX_ACQUIRED_BUFFER_COUNT, maxAcquiredBuffers);
}
- virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(maxAcquiredBuffers);
- status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setConsumerName(const String8& name) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setConsumerName);
+ return callRemote<Signature>(Tag::SET_CONSUMER_NAME, name);
}
- virtual void setConsumerName(const String8& name) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeString8(name);
- remote()->transact(SET_CONSUMER_NAME, data, &reply);
+ status_t setDefaultBufferFormat(PixelFormat defaultFormat) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferFormat);
+ return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_FORMAT, defaultFormat);
}
- virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(static_cast<int32_t>(defaultFormat));
- status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferDataSpace);
+ return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_DATA_SPACE, defaultDataSpace);
}
- virtual status_t setDefaultBufferDataSpace(
- android_dataspace defaultDataSpace) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(static_cast<int32_t>(defaultDataSpace));
- status_t result = remote()->transact(SET_DEFAULT_BUFFER_DATA_SPACE,
- data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setConsumerUsageBits(uint32_t usage) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setConsumerUsageBits);
+ return callRemote<Signature>(Tag::SET_CONSUMER_USAGE_BITS, usage);
}
- virtual status_t setConsumerUsageBits(uint32_t usage) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeUint32(usage);
- status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setConsumerIsProtected(bool isProtected) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setConsumerIsProtected);
+ return callRemote<Signature>(Tag::SET_CONSUMER_IS_PROTECTED, isProtected);
}
- virtual status_t setTransformHint(uint32_t hint) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeUint32(hint);
- status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setTransformHint(uint32_t hint) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setTransformHint);
+ return callRemote<Signature>(Tag::SET_TRANSFORM_HINT, hint);
}
- virtual sp<NativeHandle> getSidebandStream() const {
- Parcel data, reply;
- status_t err;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- if ((err = remote()->transact(GET_SIDEBAND_STREAM, data, &reply)) != NO_ERROR) {
- return NULL;
- }
- sp<NativeHandle> stream;
- if (reply.readInt32()) {
- stream = NativeHandle::create(reply.readNativeHandle(), true);
- }
- return stream;
+ status_t getSidebandStream(sp<NativeHandle>* outStream) const override {
+ using Signature = decltype(&IGraphicBufferConsumer::getSidebandStream);
+ return callRemote<Signature>(Tag::GET_SIDEBAND_STREAM, outStream);
}
- virtual status_t getOccupancyHistory(bool forceFlush,
- std::vector<OccupancyTracker::Segment>* outHistory) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- status_t error = data.writeBool(forceFlush);
- if (error != NO_ERROR) {
- return error;
- }
- error = remote()->transact(GET_OCCUPANCY_HISTORY, data,
- &reply);
- if (error != NO_ERROR) {
- return error;
- }
- error = reply.readParcelableVector(outHistory);
- if (error != NO_ERROR) {
- return error;
- }
- status_t result = NO_ERROR;
- error = reply.readInt32(&result);
- if (error != NO_ERROR) {
- return error;
- }
- return result;
+ status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) override {
+ using Signature = decltype(&IGraphicBufferConsumer::getOccupancyHistory);
+ return callRemote<Signature>(Tag::GET_OCCUPANCY_HISTORY, forceFlush, outHistory);
}
- virtual status_t discardFreeBuffers() {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply);
- if (error != NO_ERROR) {
- return error;
- }
- int32_t result = NO_ERROR;
- error = reply.readInt32(&result);
- if (error != NO_ERROR) {
- return error;
- }
- return result;
+ status_t discardFreeBuffers() override {
+ return callRemote<decltype(&IGraphicBufferConsumer::discardFreeBuffers)>(
+ Tag::DISCARD_FREE_BUFFERS);
}
- virtual void dump(String8& result, const char* prefix) const {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeString8(result);
- data.writeString8(String8(prefix ? prefix : ""));
- remote()->transact(DUMP, data, &reply);
- reply.readString8();
+ status_t dumpState(const String8& prefix, String8* outResult) const override {
+ using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
+ return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult);
}
};
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpGraphicBufferConsumer::~BpGraphicBufferConsumer() {}
+// Out-of-line virtual method definition to trigger vtable emission in this translation unit
+// (see clang warning -Wweak-vtables)
+BpGraphicBufferConsumer::~BpGraphicBufferConsumer() = default;
IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer");
-// ----------------------------------------------------------------------
-
-status_t BnGraphicBufferConsumer::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case ACQUIRE_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- BufferItem item;
- int64_t presentWhen = data.readInt64();
- uint64_t maxFrameNumber = data.readUint64();
- status_t result = acquireBuffer(&item, presentWhen, maxFrameNumber);
- status_t err = reply->write(item);
- if (err) return err;
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case DETACH_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- int slot = data.readInt32();
- int result = detachBuffer(slot);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case ATTACH_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- data.read(*buffer.get());
- int slot = -1;
- int result = attachBuffer(&slot, buffer);
- reply->writeInt32(slot);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case RELEASE_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- int buf = data.readInt32();
- uint64_t frameNumber = static_cast<uint64_t>(data.readInt64());
- sp<Fence> releaseFence = new Fence();
- status_t err = data.read(*releaseFence);
- if (err) return err;
- status_t result = releaseBuffer(buf, frameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case CONSUMER_CONNECT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() );
- bool controlledByApp = data.readInt32();
- status_t result = consumerConnect(consumer, controlledByApp);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case CONSUMER_DISCONNECT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- status_t result = consumerDisconnect();
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case GET_RELEASED_BUFFERS: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint64_t slotMask = 0;
- status_t result = getReleasedBuffers(&slotMask);
- reply->writeInt64(static_cast<int64_t>(slotMask));
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_DEFAULT_BUFFER_SIZE: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint32_t width = data.readUint32();
- uint32_t height = data.readUint32();
- status_t result = setDefaultBufferSize(width, height);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_MAX_BUFFER_COUNT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- int bufferCount = data.readInt32();
- status_t result = setMaxBufferCount(bufferCount);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_MAX_ACQUIRED_BUFFER_COUNT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- int maxAcquiredBuffers = data.readInt32();
- status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_CONSUMER_NAME: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- setConsumerName( data.readString8() );
- return NO_ERROR;
- }
- case SET_DEFAULT_BUFFER_FORMAT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- PixelFormat defaultFormat = static_cast<PixelFormat>(data.readInt32());
- status_t result = setDefaultBufferFormat(defaultFormat);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_DEFAULT_BUFFER_DATA_SPACE: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- android_dataspace defaultDataSpace =
- static_cast<android_dataspace>(data.readInt32());
- status_t result = setDefaultBufferDataSpace(defaultDataSpace);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_CONSUMER_USAGE_BITS: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint32_t usage = data.readUint32();
- status_t result = setConsumerUsageBits(usage);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_TRANSFORM_HINT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint32_t hint = data.readUint32();
- status_t result = setTransformHint(hint);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case GET_SIDEBAND_STREAM: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- sp<NativeHandle> stream = getSidebandStream();
- reply->writeInt32(static_cast<int32_t>(stream != NULL));
- if (stream != NULL) {
- reply->writeNativeHandle(stream->handle());
- }
- return NO_ERROR;
- }
- case GET_OCCUPANCY_HISTORY: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- bool forceFlush = false;
- status_t error = data.readBool(&forceFlush);
- if (error != NO_ERROR) {
- return error;
- }
- std::vector<OccupancyTracker::Segment> history;
- status_t result = getOccupancyHistory(forceFlush, &history);
- error = reply->writeParcelableVector(history);
- if (error != NO_ERROR) {
- return error;
- }
- error = reply->writeInt32(result);
- if (error != NO_ERROR) {
- return error;
- }
- return NO_ERROR;
- }
- case DISCARD_FREE_BUFFERS: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- status_t result = discardFreeBuffers();
- status_t error = reply->writeInt32(result);
- return error;
- }
- case DUMP: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- String8 result = data.readString8();
- String8 prefix = data.readString8();
- static_cast<IGraphicBufferConsumer*>(this)->dump(result, prefix);
- reply->writeString8(result);
- return NO_ERROR;
+status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ACQUIRE_BUFFER:
+ return callLocal(data, reply, &IGraphicBufferConsumer::acquireBuffer);
+ case Tag::DETACH_BUFFER:
+ return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer);
+ case Tag::ATTACH_BUFFER:
+ return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer);
+ case Tag::RELEASE_BUFFER:
+ return callLocal(data, reply, &IGraphicBufferConsumer::releaseHelper);
+ case Tag::CONSUMER_CONNECT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect);
+ case Tag::CONSUMER_DISCONNECT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::consumerDisconnect);
+ case Tag::GET_RELEASED_BUFFERS:
+ return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffers);
+ case Tag::SET_DEFAULT_BUFFER_SIZE:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferSize);
+ case Tag::SET_MAX_BUFFER_COUNT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setMaxBufferCount);
+ case Tag::SET_MAX_ACQUIRED_BUFFER_COUNT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setMaxAcquiredBufferCount);
+ case Tag::SET_CONSUMER_NAME:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerName);
+ case Tag::SET_DEFAULT_BUFFER_FORMAT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferFormat);
+ case Tag::SET_DEFAULT_BUFFER_DATA_SPACE:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferDataSpace);
+ case Tag::SET_CONSUMER_USAGE_BITS:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerUsageBits);
+ case Tag::SET_CONSUMER_IS_PROTECTED:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerIsProtected);
+ case Tag::SET_TRANSFORM_HINT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setTransformHint);
+ case Tag::GET_SIDEBAND_STREAM:
+ return callLocal(data, reply, &IGraphicBufferConsumer::getSidebandStream);
+ case Tag::GET_OCCUPANCY_HISTORY:
+ return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory);
+ case Tag::DISCARD_FREE_BUFFERS:
+ return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers);
+ case Tag::DUMP_STATE: {
+ using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
+ return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
}
}
- return BBinder::onTransact(code, data, reply, flags);
}
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 1a08130..bca645f 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>
@@ -30,9 +31,14 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+
namespace android {
// ----------------------------------------------------------------------------
+using ::android::hardware::graphics::bufferqueue::V1_0::utils::
+ H2BGraphicBufferProducer;
+
enum {
REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
DEQUEUE_BUFFER,
@@ -62,7 +68,7 @@
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
{
public:
- BpGraphicBufferProducer(const sp<IBinder>& impl)
+ explicit BpGraphicBufferProducer(const sp<IBinder>& impl)
: BpInterface<IGraphicBufferProducer>(impl)
{
}
@@ -119,24 +125,35 @@
}
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage) {
+ uint32_t height, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
Parcel data, reply;
+ bool getFrameTimestamps = (outTimestamps != nullptr);
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeUint32(width);
data.writeUint32(height);
data.writeInt32(static_cast<int32_t>(format));
data.writeUint32(usage);
+ data.writeBool(getFrameTimestamps);
+
status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
+
*buf = reply.readInt32();
- bool nonNull = reply.readInt32();
- if (nonNull) {
- *fence = new Fence();
- result = reply.read(**fence);
+ *fence = new Fence();
+ result = reply.read(**fence);
+ if (result != NO_ERROR) {
+ fence->clear();
+ return result;
+ }
+ if (getFrameTimestamps) {
+ result = reply.read(*outTimestamps);
if (result != NO_ERROR) {
- fence->clear();
+ ALOGE("IGBP::dequeueBuffer failed to read timestamps: %d",
+ result);
return result;
}
}
@@ -220,14 +237,21 @@
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(buf);
data.write(input);
+
status_t result = remote()->transact(QUEUE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+
+ result = reply.read(*output);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
result = reply.readInt32();
return result;
}
@@ -274,7 +298,7 @@
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+ reply.read(*output);
result = reply.readInt32();
return result;
}
@@ -431,40 +455,24 @@
return result;
}
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(
IGraphicBufferProducer::getInterfaceDescriptor());
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write token: %d", result);
- return false;
- }
- result = data.writeUint64(frameNumber);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to write token: %d", result);
+ return;
}
result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to transact: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to transact: %d", result);
+ return;
}
- bool found = false;
- result = reply.readBool(&found);
+ result = reply.read(*outDelta);
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to read timestamps: %d",
+ result);
}
- if (found) {
- result = reply.read(*outTimestamps);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read timestamps: %d",
- result);
- return false;
- }
- }
- return found;
}
virtual status_t getUniqueId(uint64_t* outId) const {
@@ -491,7 +499,123 @@
// translation unit (see clang warning -Wweak-vtables)
BpGraphicBufferProducer::~BpGraphicBufferProducer() {}
-IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer");
+class HpGraphicBufferProducer : public HpInterface<
+ BpGraphicBufferProducer, H2BGraphicBufferProducer> {
+public:
+ HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {}
+
+ status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override {
+ return mBase->requestBuffer(slot, buf);
+ }
+
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
+ return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+ }
+
+ status_t setAsyncMode(bool async) override {
+ return mBase->setAsyncMode(async);
+ }
+
+ status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) override {
+ return mBase->dequeueBuffer(
+ slot, fence, w, h, format, usage, outTimestamps);
+ }
+
+ status_t detachBuffer(int slot) override {
+ return mBase->detachBuffer(slot);
+ }
+
+ status_t detachNextBuffer(
+ sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
+ return mBase->detachNextBuffer(outBuffer, outFence);
+ }
+
+ status_t attachBuffer(
+ int* outSlot, const sp<GraphicBuffer>& buffer) override {
+ return mBase->attachBuffer(outSlot, buffer);
+ }
+
+ status_t queueBuffer(
+ int slot,
+ const QueueBufferInput& input,
+ QueueBufferOutput* output) override {
+ return mBase->queueBuffer(slot, input, output);
+ }
+
+ status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
+ return mBase->cancelBuffer(slot, fence);
+ }
+
+ int query(int what, int* value) override {
+ return mBase->query(what, value);
+ }
+
+ status_t connect(
+ const sp<IProducerListener>& listener,
+ int api, bool producerControlledByApp,
+ QueueBufferOutput* output) override {
+ return mBase->connect(listener, api, producerControlledByApp, output);
+ }
+
+ status_t disconnect(
+ int api, DisconnectMode mode = DisconnectMode::Api) override {
+ return mBase->disconnect(api, mode);
+ }
+
+ status_t setSidebandStream(const sp<NativeHandle>& stream) override {
+ return mBase->setSidebandStream(stream);
+ }
+
+ void allocateBuffers(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t usage) override {
+ return mBase->allocateBuffers(width, height, format, usage);
+ }
+
+ status_t allowAllocation(bool allow) override {
+ return mBase->allowAllocation(allow);
+ }
+
+ status_t setGenerationNumber(uint32_t generationNumber) override {
+ return mBase->setGenerationNumber(generationNumber);
+ }
+
+ String8 getConsumerName() const override {
+ return mBase->getConsumerName();
+ }
+
+ status_t setSharedBufferMode(bool sharedBufferMode) override {
+ return mBase->setSharedBufferMode(sharedBufferMode);
+ }
+
+ status_t setAutoRefresh(bool autoRefresh) override {
+ return mBase->setAutoRefresh(autoRefresh);
+ }
+
+ status_t setDequeueTimeout(nsecs_t timeout) override {
+ return mBase->setDequeueTimeout(timeout);
+ }
+
+ status_t getLastQueuedBuffer(
+ sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence,
+ float outTransformMatrix[16]) override {
+ return mBase->getLastQueuedBuffer(
+ outBuffer, outFence, outTransformMatrix);
+ }
+
+ void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override {
+ return mBase->getFrameTimestamps(outDelta);
+ }
+
+ status_t getUniqueId(uint64_t* outId) const override {
+ return mBase->getUniqueId(outId);
+ }
+};
+
+IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,
+ "android.gui.IGraphicBufferProducer");
// ----------------------------------------------------------------------
@@ -531,14 +655,18 @@
uint32_t height = data.readUint32();
PixelFormat format = static_cast<PixelFormat>(data.readInt32());
uint32_t usage = data.readUint32();
+ bool getTimestamps = data.readBool();
+
int buf = 0;
- sp<Fence> fence;
+ sp<Fence> fence = Fence::NO_FENCE;
+ FrameEventHistoryDelta frameTimestamps;
int result = dequeueBuffer(&buf, &fence, width, height, format,
- usage);
+ usage, getTimestamps ? &frameTimestamps : nullptr);
+
reply->writeInt32(buf);
- reply->writeInt32(fence != NULL);
- if (fence != NULL) {
- reply->write(*fence);
+ reply->write(*fence);
+ if (getTimestamps) {
+ reply->write(frameTimestamps);
}
reply->writeInt32(result);
return NO_ERROR;
@@ -582,14 +710,14 @@
}
case QUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+
int buf = data.readInt32();
QueueBufferInput input(data);
- QueueBufferOutput* const output =
- reinterpret_cast<QueueBufferOutput *>(
- reply->writeInplace(sizeof(QueueBufferOutput)));
- memset(output, 0, sizeof(QueueBufferOutput));
- status_t result = queueBuffer(buf, input, output);
+ QueueBufferOutput output;
+ status_t result = queueBuffer(buf, input, &output);
+ reply->write(output);
reply->writeInt32(result);
+
return NO_ERROR;
}
case CANCEL_BUFFER: {
@@ -620,11 +748,9 @@
}
int api = data.readInt32();
bool producerControlledByApp = data.readInt32();
- QueueBufferOutput* const output =
- reinterpret_cast<QueueBufferOutput *>(
- reply->writeInplace(sizeof(QueueBufferOutput)));
- memset(output, 0, sizeof(QueueBufferOutput));
- status_t res = connect(listener, api, producerControlledByApp, output);
+ QueueBufferOutput output;
+ status_t res = connect(listener, api, producerControlledByApp, &output);
+ reply->write(output);
reply->writeInt32(res);
return NO_ERROR;
}
@@ -727,26 +853,14 @@
}
case GET_FRAME_TIMESTAMPS: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- uint64_t frameNumber = 0;
- status_t result = data.readUint64(&frameNumber);
+ FrameEventHistoryDelta frameTimestamps;
+ getFrameTimestamps(&frameTimestamps);
+ status_t result = reply->write(frameTimestamps);
if (result != NO_ERROR) {
- ALOGE("onTransact failed to read: %d", result);
+ ALOGE("BnGBP::GET_FRAME_TIMESTAMPS failed to write buffer: %d",
+ result);
return result;
}
- FrameTimestamps timestamps;
- bool found = getFrameTimestamps(frameNumber, ×tamps);
- result = reply->writeBool(found);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write: %d", result);
- return result;
- }
- if (found) {
- result = reply->write(timestamps);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write timestamps: %d", result);
- return result;
- }
- }
return NO_ERROR;
}
case GET_UNIQUE_ID: {
@@ -773,16 +887,21 @@
parcel.read(*this);
}
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+ return sizeof(timestamp) +
+ sizeof(isAutoTimestamp) +
+ sizeof(dataSpace) +
+ sizeof(crop) +
+ sizeof(scalingMode) +
+ sizeof(transform) +
+ sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps);
+}
+
size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return sizeof(timestamp)
- + sizeof(isAutoTimestamp)
- + sizeof(dataSpace)
- + sizeof(crop)
- + sizeof(scalingMode)
- + sizeof(transform)
- + sizeof(stickyTransform)
- + fence->getFlattenedSize()
- + surfaceDamage.getFlattenedSize();
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ surfaceDamage.getFlattenedSize();
}
size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
@@ -795,6 +914,7 @@
if (size < getFlattenedSize()) {
return NO_MEMORY;
}
+
FlattenableUtils::write(buffer, size, timestamp);
FlattenableUtils::write(buffer, size, isAutoTimestamp);
FlattenableUtils::write(buffer, size, dataSpace);
@@ -802,6 +922,8 @@
FlattenableUtils::write(buffer, size, scalingMode);
FlattenableUtils::write(buffer, size, transform);
FlattenableUtils::write(buffer, size, stickyTransform);
+ FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
status_t result = fence->flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
return result;
@@ -812,16 +934,7 @@
status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count)
{
- size_t minNeeded =
- sizeof(timestamp)
- + sizeof(isAutoTimestamp)
- + sizeof(dataSpace)
- + sizeof(crop)
- + sizeof(scalingMode)
- + sizeof(transform)
- + sizeof(stickyTransform);
-
- if (size < minNeeded) {
+ if (size < minFlattenedSize()) {
return NO_MEMORY;
}
@@ -832,6 +945,7 @@
FlattenableUtils::read(buffer, size, scalingMode);
FlattenableUtils::read(buffer, size, transform);
FlattenableUtils::read(buffer, size, stickyTransform);
+ FlattenableUtils::read(buffer, size, getFrameTimestamps);
fence = new Fence();
status_t result = fence->unflatten(buffer, size, fds, count);
@@ -841,4 +955,56 @@
return surfaceDamage.unflatten(buffer, size);
}
+// ----------------------------------------------------------------------------
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+ return sizeof(width) +
+ sizeof(height) +
+ sizeof(transformHint) +
+ sizeof(numPendingBuffers) +
+ sizeof(nextFrameNumber) +
+ sizeof(bufferReplaced);
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, transformHint);
+ FlattenableUtils::write(buffer, size, numPendingBuffers);
+ FlattenableUtils::write(buffer, size, nextFrameNumber);
+ FlattenableUtils::write(buffer, size, bufferReplaced);
+
+ return frameTimestamps.flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, transformHint);
+ FlattenableUtils::read(buffer, size, numPendingBuffers);
+ FlattenableUtils::read(buffer, size, nextFrameNumber);
+ FlattenableUtils::read(buffer, size, bufferReplaced);
+
+ return frameTimestamps.unflatten(buffer, size, fds, count);
+}
+
}; // namespace android
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index da54ce1..62abfa8 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -28,7 +28,7 @@
class BpProducerListener : public BpInterface<IProducerListener>
{
public:
- BpProducerListener(const sp<IBinder>& impl)
+ explicit BpProducerListener(const sp<IBinder>& impl)
: BpInterface<IProducerListener>(impl) {}
virtual ~BpProducerListener();
@@ -78,6 +78,10 @@
return BBinder::onTransact(code, data, reply, flags);
}
+ProducerListener::~ProducerListener() = default;
+
+DummyProducerListener::~DummyProducerListener() = default;
+
bool BnProducerListener::needsReleaseNotify() {
return true;
}
diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp
deleted file mode 100644
index dc7a35c..0000000
--- a/libs/gui/ISensorEventConnection.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
-#include <gui/ISensorEventConnection.h>
-#include <gui/BitTube.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-enum {
- GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
- ENABLE_DISABLE,
- SET_EVENT_RATE,
- FLUSH_SENSOR
-};
-
-class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
-{
-public:
- BpSensorEventConnection(const sp<IBinder>& impl)
- : BpInterface<ISensorEventConnection>(impl)
- {
- }
-
- virtual ~BpSensorEventConnection();
-
- virtual sp<BitTube> getSensorChannel() const
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
- remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
- return new BitTube(reply);
- }
-
- virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
- nsecs_t maxBatchReportLatencyNs, int reservedFlags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
- data.writeInt32(handle);
- data.writeInt32(enabled);
- data.writeInt64(samplingPeriodNs);
- data.writeInt64(maxBatchReportLatencyNs);
- data.writeInt32(reservedFlags);
- remote()->transact(ENABLE_DISABLE, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t setEventRate(int handle, nsecs_t ns)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
- data.writeInt32(handle);
- data.writeInt64(ns);
- remote()->transact(SET_EVENT_RATE, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t flush() {
- Parcel data, reply;
- data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
- remote()->transact(FLUSH_SENSOR, data, &reply);
- return reply.readInt32();
- }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpSensorEventConnection::~BpSensorEventConnection() {}
-
-IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection");
-
-// ----------------------------------------------------------------------------
-
-status_t BnSensorEventConnection::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case GET_SENSOR_CHANNEL: {
- CHECK_INTERFACE(ISensorEventConnection, data, reply);
- sp<BitTube> channel(getSensorChannel());
- channel->writeToParcel(reply);
- return NO_ERROR;
- }
- case ENABLE_DISABLE: {
- CHECK_INTERFACE(ISensorEventConnection, data, reply);
- int handle = data.readInt32();
- int enabled = data.readInt32();
- nsecs_t samplingPeriodNs = data.readInt64();
- nsecs_t maxBatchReportLatencyNs = data.readInt64();
- int reservedFlags = data.readInt32();
- status_t result = enableDisable(handle, enabled, samplingPeriodNs,
- maxBatchReportLatencyNs, reservedFlags);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_EVENT_RATE: {
- CHECK_INTERFACE(ISensorEventConnection, data, reply);
- int handle = data.readInt32();
- nsecs_t ns = data.readInt64();
- status_t result = setEventRate(handle, ns);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case FLUSH_SENSOR: {
- CHECK_INTERFACE(ISensorEventConnection, data, reply);
- status_t result = flush();
- reply->writeInt32(result);
- return NO_ERROR;
- }
- }
- return BBinder::onTransact(code, data, reply, flags);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp
deleted file mode 100644
index 3a4c7e4..0000000
--- a/libs/gui/ISensorServer.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
-#include <gui/Sensor.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-enum {
- GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION,
- CREATE_SENSOR_EVENT_CONNECTION,
- ENABLE_DATA_INJECTION,
- GET_DYNAMIC_SENSOR_LIST,
-};
-
-class BpSensorServer : public BpInterface<ISensorServer>
-{
-public:
- BpSensorServer(const sp<IBinder>& impl)
- : BpInterface<ISensorServer>(impl)
- {
- }
-
- virtual ~BpSensorServer();
-
- virtual Vector<Sensor> getSensorList(const String16& opPackageName)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
- data.writeString16(opPackageName);
- remote()->transact(GET_SENSOR_LIST, data, &reply);
- Sensor s;
- Vector<Sensor> v;
- uint32_t n = reply.readUint32();
- v.setCapacity(n);
- while (n--) {
- reply.read(s);
- v.add(s);
- }
- return v;
- }
-
- virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
- data.writeString16(opPackageName);
- remote()->transact(GET_DYNAMIC_SENSOR_LIST, data, &reply);
- Sensor s;
- Vector<Sensor> v;
- uint32_t n = reply.readUint32();
- v.setCapacity(n);
- while (n--) {
- reply.read(s);
- v.add(s);
- }
- return v;
- }
-
- virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
- int mode, const String16& opPackageName)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
- data.writeString8(packageName);
- data.writeInt32(mode);
- data.writeString16(opPackageName);
- remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
- return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
- }
-
- virtual int isDataInjectionEnabled() {
- Parcel data, reply;
- data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
- remote()->transact(ENABLE_DATA_INJECTION, data, &reply);
- return reply.readInt32();
- }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpSensorServer::~BpSensorServer() {}
-
-IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer");
-
-// ----------------------------------------------------------------------
-
-status_t BnSensorServer::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case GET_SENSOR_LIST: {
- CHECK_INTERFACE(ISensorServer, data, reply);
- const String16& opPackageName = data.readString16();
- Vector<Sensor> v(getSensorList(opPackageName));
- size_t n = v.size();
- reply->writeUint32(static_cast<uint32_t>(n));
- for (size_t i = 0; i < n; i++) {
- reply->write(v[i]);
- }
- return NO_ERROR;
- }
- case CREATE_SENSOR_EVENT_CONNECTION: {
- CHECK_INTERFACE(ISensorServer, data, reply);
- String8 packageName = data.readString8();
- int32_t mode = data.readInt32();
- const String16& opPackageName = data.readString16();
- sp<ISensorEventConnection> connection(createSensorEventConnection(packageName, mode,
- opPackageName));
- reply->writeStrongBinder(IInterface::asBinder(connection));
- return NO_ERROR;
- }
- case ENABLE_DATA_INJECTION: {
- CHECK_INTERFACE(ISensorServer, data, reply);
- int32_t ret = isDataInjectionEnabled();
- reply->writeInt32(static_cast<int32_t>(ret));
- return NO_ERROR;
- }
- case GET_DYNAMIC_SENSOR_LIST: {
- CHECK_INTERFACE(ISensorServer, data, reply);
- const String16& opPackageName = data.readString16();
- Vector<Sensor> v(getDynamicSensorList(opPackageName));
- size_t n = v.size();
- reply->writeUint32(static_cast<uint32_t>(n));
- for (size_t i = 0; i < n; i++) {
- reply->write(v[i]);
- }
- return NO_ERROR;
- }
- }
- return BBinder::onTransact(code, data, reply, flags);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index f0b0ada..0a0d112 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -21,14 +21,13 @@
#include <sys/types.h>
#include <binder/Parcel.h>
-#include <binder/IMemory.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-#include <gui/BitTube.h>
#include <gui/IDisplayEventConnection.h>
-#include <gui/ISurfaceComposer.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/ISurfaceComposerClient.h>
#include <private/gui/LayerState.h>
@@ -44,12 +43,10 @@
namespace android {
-class IDisplayEventConnection;
-
class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
{
public:
- BpSurfaceComposer(const sp<IBinder>& impl)
+ explicit BpSurfaceComposer(const sp<IBinder>& impl)
: BpInterface<ISurfaceComposer>(impl)
{
}
@@ -64,12 +61,14 @@
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
- virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc()
+ virtual sp<ISurfaceComposerClient> createScopedConnection(
+ const sp<IGraphicBufferProducer>& parent)
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, data, &reply);
- return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder());
+ data.writeStrongBinder(IInterface::asBinder(parent));
+ remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply);
+ return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
virtual void setTransactionState(
@@ -104,7 +103,7 @@
virtual 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,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform,
ISurfaceComposer::Rotation rotation)
{
@@ -115,8 +114,8 @@
data.write(sourceCrop);
data.writeUint32(reqWidth);
data.writeUint32(reqHeight);
- data.writeUint32(minLayerZ);
- data.writeUint32(maxLayerZ);
+ data.writeInt32(minLayerZ);
+ data.writeInt32(maxLayerZ);
data.writeInt32(static_cast<int32_t>(useIdentityTransform));
data.writeInt32(static_cast<int32_t>(rotation));
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
@@ -158,7 +157,51 @@
return result != 0;
}
- virtual sp<IDisplayEventConnection> createDisplayEventConnection()
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ if (!outSupported) {
+ return UNEXPECTED_NULL;
+ }
+ outSupported->clear();
+
+ Parcel data, reply;
+
+ status_t err = data.writeInterfaceToken(
+ ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = remote()->transact(
+ BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS,
+ data, &reply);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ int32_t result = 0;
+ err = reply.readInt32(&result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ std::vector<int32_t> supported;
+ err = reply.readInt32Vector(&supported);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ outSupported->reserve(supported.size());
+ for (int32_t s : supported) {
+ outSupported->push_back(static_cast<FrameEvent>(s));
+ }
+ return NO_ERROR;
+ }
+
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource)
{
Parcel data, reply;
sp<IDisplayEventConnection> result;
@@ -167,6 +210,7 @@
if (err != NO_ERROR) {
return result;
}
+ data.writeInt32(static_cast<int32_t>(vsyncSource));
err = remote()->transact(
BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
data, &reply);
@@ -379,10 +423,52 @@
}
result = reply.readInt32();
if (result == NO_ERROR) {
- result = reply.readParcelable(outCapabilities);
+ result = reply.read(*outCapabilities);
}
return result;
}
+
+ virtual status_t enableVSyncInjections(bool enable) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeBool(enable);
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to writeBool: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS,
+ data, &reply, TF_ONE_WAY);
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to transact: %d", result);
+ return result;
+ }
+ return result;
+ }
+
+ virtual status_t injectVSync(nsecs_t when) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("injectVSync failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeInt64(when);
+ if (result != NO_ERROR) {
+ ALOGE("injectVSync failed to writeInt64: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, TF_ONE_WAY);
+ if (result != NO_ERROR) {
+ ALOGE("injectVSync failed to transact: %d", result);
+ return result;
+ }
+ return result;
+ }
+
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -403,9 +489,11 @@
reply->writeStrongBinder(b);
return NO_ERROR;
}
- case CREATE_GRAPHIC_BUFFER_ALLOC: {
+ case CREATE_SCOPED_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> b = IInterface::asBinder(createGraphicBufferAlloc());
+ sp<IGraphicBufferProducer> bufferProducer =
+ interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+ sp<IBinder> b = IInterface::asBinder(createScopedConnection(bufferProducer));
reply->writeStrongBinder(b);
return NO_ERROR;
}
@@ -458,8 +546,8 @@
data.read(sourceCrop);
uint32_t reqWidth = data.readUint32();
uint32_t reqHeight = data.readUint32();
- uint32_t minLayerZ = data.readUint32();
- uint32_t maxLayerZ = data.readUint32();
+ int32_t minLayerZ = data.readInt32();
+ int32_t maxLayerZ = data.readInt32();
bool useIdentityTransform = static_cast<bool>(data.readInt32());
int32_t rotation = data.readInt32();
@@ -478,9 +566,29 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case GET_SUPPORTED_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ std::vector<FrameEvent> supportedTimestamps;
+ status_t result = getSupportedFrameTimestamps(&supportedTimestamps);
+ status_t err = reply->writeInt32(result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ std::vector<int32_t> supported;
+ supported.reserve(supportedTimestamps.size());
+ for (FrameEvent s : supportedTimestamps) {
+ supported.push_back(static_cast<int32_t>(s));
+ }
+ return reply->writeInt32Vector(supported);
+ }
case CREATE_DISPLAY_EVENT_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IDisplayEventConnection> connection(createDisplayEventConnection());
+ sp<IDisplayEventConnection> connection(createDisplayEventConnection(
+ static_cast<ISurfaceComposer::VsyncSource>(data.readInt32())));
reply->writeStrongBinder(IInterface::asBinder(connection));
return NO_ERROR;
}
@@ -631,10 +739,30 @@
result = getHdrCapabilities(display, &capabilities);
reply->writeInt32(result);
if (result == NO_ERROR) {
- reply->writeParcelable(capabilities);
+ reply->write(capabilities);
}
return NO_ERROR;
}
+ case ENABLE_VSYNC_INJECTIONS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ bool enable = false;
+ status_t result = data.readBool(&enable);
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to readBool: %d", result);
+ return result;
+ }
+ return enableVSyncInjections(enable);
+ }
+ case INJECT_VSYNC: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int64_t when = 0;
+ status_t result = data.readInt64(&when);
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to readInt64: %d", result);
+ return result;
+ }
+ return injectVSync(when);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index dd5b169..679f44b 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -17,112 +17,61 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
-#include <stdio.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <ui/Point.h>
-#include <ui/Rect.h>
+#include <gui/ISurfaceComposerClient.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/ISurfaceComposerClient.h>
-#include <private/gui/LayerState.h>
-// ---------------------------------------------------------------------------
+#include <binder/SafeInterface.h>
+
+#include <ui/FrameStats.h>
namespace android {
-enum {
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
DESTROY_SURFACE,
CLEAR_LAYER_FRAME_STATS,
GET_LAYER_FRAME_STATS,
- GET_TRANSFORM_TO_DISPLAY_INVERSE
+ LAST = GET_LAYER_FRAME_STATS,
};
-class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
-{
+} // Anonymous namespace
+
+class BpSurfaceComposerClient : public SafeBpInterface<ISurfaceComposerClient> {
public:
- BpSurfaceComposerClient(const sp<IBinder>& impl)
- : BpInterface<ISurfaceComposerClient>(impl) {
+ explicit BpSurfaceComposerClient(const sp<IBinder>& impl)
+ : SafeBpInterface<ISurfaceComposerClient>(impl, "BpSurfaceComposerClient") {}
+
+ ~BpSurfaceComposerClient() override;
+
+ status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t flags, const sp<IBinder>& parent, uint32_t windowType,
+ uint32_t ownerUid, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) override {
+ return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
+ name, width, height,
+ format, flags, parent,
+ windowType, ownerUid,
+ handle, gbp);
}
- virtual ~BpSurfaceComposerClient();
-
- virtual status_t createSurface(const String8& name, uint32_t width,
- uint32_t height, PixelFormat format, uint32_t flags,
- sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeString8(name);
- data.writeUint32(width);
- data.writeUint32(height);
- data.writeInt32(static_cast<int32_t>(format));
- data.writeUint32(flags);
- remote()->transact(CREATE_SURFACE, data, &reply);
- *handle = reply.readStrongBinder();
- *gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
- return reply.readInt32();
+ status_t destroySurface(const sp<IBinder>& handle) override {
+ return callRemote<decltype(&ISurfaceComposerClient::destroySurface)>(Tag::DESTROY_SURFACE,
+ handle);
}
- virtual status_t destroySurface(const sp<IBinder>& handle) {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeStrongBinder(handle);
- remote()->transact(DESTROY_SURFACE, data, &reply);
- return reply.readInt32();
+ status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
+ return callRemote<decltype(
+ &ISurfaceComposerClient::clearLayerFrameStats)>(Tag::CLEAR_LAYER_FRAME_STATS,
+ handle);
}
- virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeStrongBinder(handle);
- remote()->transact(CLEAR_LAYER_FRAME_STATS, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeStrongBinder(handle);
- remote()->transact(GET_LAYER_FRAME_STATS, data, &reply);
- reply.read(*outStats);
- return reply.readInt32();
- }
-
- virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
- bool* outTransformToDisplayInverse) const {
- Parcel data, reply;
- status_t result =
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- return result;
- }
- result = data.writeStrongBinder(handle);
- if (result != NO_ERROR) {
- return result;
- }
- result = remote()->transact(GET_TRANSFORM_TO_DISPLAY_INVERSE, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- int transformInverse;
- result = reply.readInt32(&transformInverse);
- if (result != NO_ERROR) {
- return result;
- }
- *outTransformToDisplayInverse = transformInverse != 0 ? true : false;
- status_t result2 = reply.readInt32(&result);
- if (result2 != NO_ERROR) {
- return result2;
- }
- return result;
+ status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const override {
+ return callRemote<decltype(
+ &ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle,
+ outStats);
}
};
@@ -134,69 +83,22 @@
// ----------------------------------------------------------------------
-status_t BnSurfaceComposerClient::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case CREATE_SURFACE: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- String8 name = data.readString8();
- uint32_t width = data.readUint32();
- uint32_t height = data.readUint32();
- PixelFormat format = static_cast<PixelFormat>(data.readInt32());
- uint32_t createFlags = data.readUint32();
- sp<IBinder> handle;
- sp<IGraphicBufferProducer> gbp;
- status_t result = createSurface(name, width, height, format,
- createFlags, &handle, &gbp);
- reply->writeStrongBinder(handle);
- reply->writeStrongBinder(IInterface::asBinder(gbp));
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case DESTROY_SURFACE: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- reply->writeInt32(destroySurface( data.readStrongBinder() ) );
- return NO_ERROR;
- }
- case CLEAR_LAYER_FRAME_STATS: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- sp<IBinder> handle = data.readStrongBinder();
- status_t result = clearLayerFrameStats(handle);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case GET_LAYER_FRAME_STATS: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- sp<IBinder> handle = data.readStrongBinder();
- FrameStats stats;
- status_t result = getLayerFrameStats(handle, &stats);
- reply->write(stats);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case GET_TRANSFORM_TO_DISPLAY_INVERSE: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- sp<IBinder> handle;
- status_t result = data.readStrongBinder(&handle);
- if (result != NO_ERROR) {
- return result;
- }
- bool transformInverse = false;
- result = getTransformToDisplayInverse(handle, &transformInverse);
- if (result != NO_ERROR) {
- return result;
- }
- result = reply->writeInt32(transformInverse ? 1 : 0);
- if (result != NO_ERROR) {
- return result;
- }
- result = reply->writeInt32(NO_ERROR);
- return result;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
+status_t BnSurfaceComposerClient::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::CREATE_SURFACE:
+ return callLocal(data, reply, &ISurfaceComposerClient::createSurface);
+ case Tag::DESTROY_SURFACE:
+ return callLocal(data, reply, &ISurfaceComposerClient::destroySurface);
+ case Tag::CLEAR_LAYER_FRAME_STATS:
+ return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats);
+ case Tag::GET_LAYER_FRAME_STATS:
+ return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats);
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index d1c576e..9b06e63 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -28,7 +28,7 @@
output.writeUint32(what);
output.writeFloat(x);
output.writeFloat(y);
- output.writeUint32(z);
+ output.writeInt32(z);
output.writeUint32(w);
output.writeUint32(h);
output.writeUint32(layerStack);
@@ -39,9 +39,12 @@
output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
output.write(crop);
output.write(finalCrop);
- output.writeStrongBinder(handle);
+ output.writeStrongBinder(barrierHandle);
+ output.writeStrongBinder(reparentHandle);
output.writeUint64(frameNumber);
output.writeInt32(overrideScalingMode);
+ output.writeStrongBinder(IInterface::asBinder(barrierGbp));
+ output.writeStrongBinder(relativeLayerHandle);
output.write(transparentRegion);
return NO_ERROR;
}
@@ -52,7 +55,7 @@
what = input.readUint32();
x = input.readFloat();
y = input.readFloat();
- z = input.readUint32();
+ z = input.readInt32();
w = input.readUint32();
h = input.readUint32();
layerStack = input.readUint32();
@@ -67,9 +70,13 @@
}
input.read(crop);
input.read(finalCrop);
- handle = input.readStrongBinder();
+ barrierHandle = input.readStrongBinder();
+ reparentHandle = input.readStrongBinder();
frameNumber = input.readUint64();
overrideScalingMode = input.readInt32();
+ barrierGbp =
+ interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
+ relativeLayerHandle = input.readStrongBinder();
input.read(transparentRegion);
return NO_ERROR;
}
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
deleted file mode 100644
index 053d153..0000000
--- a/libs/gui/Sensor.cpp
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#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>
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/limits.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-Sensor::Sensor(const char * name) :
- mName(name), mHandle(0), mType(0),
- mMinValue(0), mMaxValue(0), mResolution(0),
- mPower(0), mMinDelay(0), mVersion(0), mFifoReservedEventCount(0),
- mFifoMaxEventCount(0), mRequiredAppOp(0),
- mMaxDelay(0), mFlags(0) {
-}
-
-Sensor::Sensor(struct sensor_t const* hwSensor, int halVersion) :
- Sensor(*hwSensor, uuid_t(), halVersion) {
-}
-
-Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion) {
- mName = hwSensor.name;
- mVendor = hwSensor.vendor;
- mVersion = hwSensor.version;
- mHandle = hwSensor.handle;
- mType = hwSensor.type;
- mMinValue = 0; // FIXME: minValue
- mMaxValue = hwSensor.maxRange; // FIXME: maxValue
- mResolution = hwSensor.resolution;
- mPower = hwSensor.power;
- mMinDelay = hwSensor.minDelay;
- mFlags = 0;
- mUuid = uuid;
-
- // Set fifo event count zero for older devices which do not support batching. Fused
- // sensors also have their fifo counts set to zero.
- if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
- mFifoReservedEventCount = hwSensor.fifoReservedEventCount;
- mFifoMaxEventCount = hwSensor.fifoMaxEventCount;
- } else {
- mFifoReservedEventCount = 0;
- mFifoMaxEventCount = 0;
- }
-
- if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
- if (hwSensor.maxDelay > INT_MAX) {
- // Max delay is declared as a 64 bit integer for 64 bit architectures. But it should
- // always fit in a 32 bit integer, log error and cap it to INT_MAX.
- ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.string(),
- static_cast<int64_t>(hwSensor.maxDelay));
- mMaxDelay = INT_MAX;
- } else {
- mMaxDelay = static_cast<int32_t>(hwSensor.maxDelay);
- }
- } else {
- // For older hals set maxDelay to 0.
- mMaxDelay = 0;
- }
-
- // Ensure existing sensors have correct string type, required permissions and reporting mode.
- // Set reportingMode for all android defined sensor types, set wake-up flag only for proximity
- // sensor, significant motion, tilt, pick_up gesture, wake gesture and glance gesture on older
- // HALs. Newer HALs can define both wake-up and non wake-up proximity sensors.
- // All the OEM defined defined sensors have flags set to whatever is provided by the HAL.
- switch (mType) {
- case SENSOR_TYPE_ACCELEROMETER:
- mStringType = SENSOR_STRING_TYPE_ACCELEROMETER;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_AMBIENT_TEMPERATURE:
- mStringType = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE;
- mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- break;
- case SENSOR_TYPE_GAME_ROTATION_VECTOR:
- mStringType = SENSOR_STRING_TYPE_GAME_ROTATION_VECTOR;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
- mStringType = SENSOR_STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_GRAVITY:
- mStringType = SENSOR_STRING_TYPE_GRAVITY;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_GYROSCOPE:
- mStringType = SENSOR_STRING_TYPE_GYROSCOPE;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
- mStringType = SENSOR_STRING_TYPE_GYROSCOPE_UNCALIBRATED;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_HEART_RATE: {
- mStringType = SENSOR_STRING_TYPE_HEART_RATE;
- mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
- AppOpsManager appOps;
- mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
- mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- } break;
- case SENSOR_TYPE_LIGHT:
- mStringType = SENSOR_STRING_TYPE_LIGHT;
- mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- break;
- case SENSOR_TYPE_LINEAR_ACCELERATION:
- mStringType = SENSOR_STRING_TYPE_LINEAR_ACCELERATION;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_MAGNETIC_FIELD:
- mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
- mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_ORIENTATION:
- mStringType = SENSOR_STRING_TYPE_ORIENTATION;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_PRESSURE:
- mStringType = SENSOR_STRING_TYPE_PRESSURE;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_PROXIMITY:
- mStringType = SENSOR_STRING_TYPE_PROXIMITY;
- mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- break;
- case SENSOR_TYPE_RELATIVE_HUMIDITY:
- mStringType = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY;
- mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- break;
- case SENSOR_TYPE_ROTATION_VECTOR:
- mStringType = SENSOR_STRING_TYPE_ROTATION_VECTOR;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_SIGNIFICANT_MOTION:
- mStringType = SENSOR_STRING_TYPE_SIGNIFICANT_MOTION;
- mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- break;
- case SENSOR_TYPE_STEP_COUNTER:
- mStringType = SENSOR_STRING_TYPE_STEP_COUNTER;
- mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- break;
- case SENSOR_TYPE_STEP_DETECTOR:
- mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR;
- mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
- break;
- case SENSOR_TYPE_TEMPERATURE:
- mStringType = SENSOR_STRING_TYPE_TEMPERATURE;
- mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- break;
- case SENSOR_TYPE_TILT_DETECTOR:
- mStringType = SENSOR_STRING_TYPE_TILT_DETECTOR;
- mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- break;
- case SENSOR_TYPE_WAKE_GESTURE:
- mStringType = SENSOR_STRING_TYPE_WAKE_GESTURE;
- mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- break;
- case SENSOR_TYPE_GLANCE_GESTURE:
- mStringType = SENSOR_STRING_TYPE_GLANCE_GESTURE;
- mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- break;
- case SENSOR_TYPE_PICK_UP_GESTURE:
- mStringType = SENSOR_STRING_TYPE_PICK_UP_GESTURE;
- mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- break;
- case SENSOR_TYPE_WRIST_TILT_GESTURE:
- mStringType = SENSOR_STRING_TYPE_WRIST_TILT_GESTURE;
- mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- 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
- break;
- case SENSOR_TYPE_POSE_6DOF:
- mStringType = SENSOR_STRING_TYPE_POSE_6DOF;
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- break;
- case SENSOR_TYPE_STATIONARY_DETECT:
- mStringType = SENSOR_STRING_TYPE_STATIONARY_DETECT;
- mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- break;
- case SENSOR_TYPE_MOTION_DETECT:
- mStringType = SENSOR_STRING_TYPE_MOTION_DETECT;
- mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= SENSOR_FLAG_WAKE_UP;
- }
- break;
- case SENSOR_TYPE_HEART_BEAT:
- mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
- mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
- break;
- default:
- // Only pipe the stringType, requiredPermission and flags for custom sensors.
- if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
- mStringType = hwSensor.stringType;
- }
- if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) {
- mRequiredPermission = hwSensor.requiredPermission;
- if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) {
- AppOpsManager appOps;
- mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
- }
- }
-
- if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags = static_cast<uint32_t>(hwSensor.flags);
- } else {
- // This is an OEM defined sensor on an older HAL. Use minDelay to determine the
- // reporting mode of the sensor.
- if (mMinDelay > 0) {
- mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
- } else if (mMinDelay == 0) {
- mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- } else if (mMinDelay < 0) {
- mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
- }
- }
- break;
- }
-
- if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
- // Wake-up flag of HAL 1.3 and above is set here
- mFlags |= (hwSensor.flags & SENSOR_FLAG_WAKE_UP);
-
- // Log error if the reporting mode is not as expected, but respect HAL setting.
- int actualReportingMode = (hwSensor.flags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
- int expectedReportingMode = (mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
- if (actualReportingMode != expectedReportingMode) {
- ALOGE("Reporting Mode incorrect: sensor %s handle=%#010" PRIx32 " type=%" PRId32 " "
- "actual=%d expected=%d",
- mName.string(), mHandle, mType, actualReportingMode, expectedReportingMode);
- }
- }
-
- // 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));
- }
- // Set DATA_INJECTION flag here. Defined in HAL 1_4.
- if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) {
- mFlags |= (hwSensor.flags & DATA_INJECTION_MASK);
- }
-
- if (mRequiredPermission.length() > 0) {
- // If the sensor is protected by a permission we need to know if it is
- // a runtime one to determine whether we can use the permission cache.
- sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
- if (binder != 0) {
- sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
- mRequiredPermissionRuntime = permCtrl->isRuntimePermission(
- String16(mRequiredPermission));
- }
- }
-}
-
-Sensor::~Sensor() {
-}
-
-const String8& Sensor::getName() const {
- return mName;
-}
-
-const String8& Sensor::getVendor() const {
- return mVendor;
-}
-
-int32_t Sensor::getHandle() const {
- return mHandle;
-}
-
-int32_t Sensor::getType() const {
- return mType;
-}
-
-float Sensor::getMinValue() const {
- return mMinValue;
-}
-
-float Sensor::getMaxValue() const {
- return mMaxValue;
-}
-
-float Sensor::getResolution() const {
- return mResolution;
-}
-
-float Sensor::getPowerUsage() const {
- return mPower;
-}
-
-int32_t Sensor::getMinDelay() const {
- return mMinDelay;
-}
-
-nsecs_t Sensor::getMinDelayNs() const {
- return getMinDelay() * 1000;
-}
-
-int32_t Sensor::getVersion() const {
- return mVersion;
-}
-
-uint32_t Sensor::getFifoReservedEventCount() const {
- return mFifoReservedEventCount;
-}
-
-uint32_t Sensor::getFifoMaxEventCount() const {
- return mFifoMaxEventCount;
-}
-
-const String8& Sensor::getStringType() const {
- return mStringType;
-}
-
-const String8& Sensor::getRequiredPermission() const {
- return mRequiredPermission;
-}
-
-bool Sensor::isRequiredPermissionRuntime() const {
- return mRequiredPermissionRuntime;
-}
-
-int32_t Sensor::getRequiredAppOp() const {
- return mRequiredAppOp;
-}
-
-int32_t Sensor::getMaxDelay() const {
- return mMaxDelay;
-}
-
-uint32_t Sensor::getFlags() const {
- return mFlags;
-}
-
-bool Sensor::isWakeUpSensor() const {
- return (mFlags & SENSOR_FLAG_WAKE_UP) != 0;
-}
-
-bool Sensor::isDynamicSensor() const {
- return (mFlags & SENSOR_FLAG_DYNAMIC_SENSOR) != 0;
-}
-
-bool Sensor::hasAdditionalInfo() const {
- return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0;
-}
-
-int32_t Sensor::getReportingMode() const {
- return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
-}
-
-const Sensor::uuid_t& Sensor::getUuid() const {
- return mUuid;
-}
-
-void Sensor::setId(int32_t id) {
- mUuid.i64[0] = id;
- mUuid.i64[1] = 0;
-}
-
-int32_t Sensor::getId() const {
- return int32_t(mUuid.i64[0]);
-}
-
-size_t Sensor::getFlattenedSize() const {
- size_t fixedSize =
- sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) +
- sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) +
- sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) +
- sizeof(mFifoMaxEventCount) + sizeof(mRequiredPermissionRuntime) +
- sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid);
-
- size_t variableSize =
- sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) +
- sizeof(uint32_t) + FlattenableUtils::align<4>(mVendor.length()) +
- sizeof(uint32_t) + FlattenableUtils::align<4>(mStringType.length()) +
- sizeof(uint32_t) + FlattenableUtils::align<4>(mRequiredPermission.length());
-
- return fixedSize + variableSize;
-}
-
-status_t Sensor::flatten(void* buffer, size_t size) const {
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- flattenString8(buffer, size, mName);
- flattenString8(buffer, size, mVendor);
- FlattenableUtils::write(buffer, size, mVersion);
- FlattenableUtils::write(buffer, size, mHandle);
- FlattenableUtils::write(buffer, size, mType);
- FlattenableUtils::write(buffer, size, mMinValue);
- FlattenableUtils::write(buffer, size, mMaxValue);
- FlattenableUtils::write(buffer, size, mResolution);
- FlattenableUtils::write(buffer, size, mPower);
- FlattenableUtils::write(buffer, size, mMinDelay);
- FlattenableUtils::write(buffer, size, mFifoReservedEventCount);
- FlattenableUtils::write(buffer, size, mFifoMaxEventCount);
- flattenString8(buffer, size, mStringType);
- flattenString8(buffer, size, mRequiredPermission);
- FlattenableUtils::write(buffer, size, mRequiredPermissionRuntime);
- FlattenableUtils::write(buffer, size, mRequiredAppOp);
- FlattenableUtils::write(buffer, size, mMaxDelay);
- FlattenableUtils::write(buffer, size, mFlags);
- if (mUuid.i64[1] != 0) {
- // We should never hit this case with our current API, but we
- // could via a careless API change. If that happens,
- // this code will keep us from leaking our UUID (while probably
- // breaking dynamic sensors). See b/29547335.
- ALOGW("Sensor with UUID being flattened; sending 0. Expect "
- "bad dynamic sensor behavior");
- uuid_t tmpUuid; // default constructor makes this 0.
- FlattenableUtils::write(buffer, size, tmpUuid);
- } else {
- FlattenableUtils::write(buffer, size, mUuid);
- }
- return NO_ERROR;
-}
-
-status_t Sensor::unflatten(void const* buffer, size_t size) {
- if (!unflattenString8(buffer, size, mName)) {
- return NO_MEMORY;
- }
- if (!unflattenString8(buffer, size, mVendor)) {
- return NO_MEMORY;
- }
-
- size_t fixedSize1 =
- sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) + sizeof(mMinValue) +
- sizeof(mMaxValue) + sizeof(mResolution) + sizeof(mPower) + sizeof(mMinDelay) +
- sizeof(mFifoMaxEventCount) + sizeof(mFifoMaxEventCount);
- if (size < fixedSize1) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, mVersion);
- FlattenableUtils::read(buffer, size, mHandle);
- FlattenableUtils::read(buffer, size, mType);
- FlattenableUtils::read(buffer, size, mMinValue);
- FlattenableUtils::read(buffer, size, mMaxValue);
- FlattenableUtils::read(buffer, size, mResolution);
- FlattenableUtils::read(buffer, size, mPower);
- FlattenableUtils::read(buffer, size, mMinDelay);
- FlattenableUtils::read(buffer, size, mFifoReservedEventCount);
- FlattenableUtils::read(buffer, size, mFifoMaxEventCount);
-
- if (!unflattenString8(buffer, size, mStringType)) {
- return NO_MEMORY;
- }
- if (!unflattenString8(buffer, size, mRequiredPermission)) {
- return NO_MEMORY;
- }
-
- size_t fixedSize2 =
- sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) +
- sizeof(mFlags) + sizeof(mUuid);
- if (size < fixedSize2) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, mRequiredPermissionRuntime);
- FlattenableUtils::read(buffer, size, mRequiredAppOp);
- FlattenableUtils::read(buffer, size, mMaxDelay);
- FlattenableUtils::read(buffer, size, mFlags);
- FlattenableUtils::read(buffer, size, mUuid);
- return NO_ERROR;
-}
-
-void Sensor::flattenString8(void*& buffer, size_t& size,
- const String8& string8) {
- uint32_t len = static_cast<uint32_t>(string8.length());
- FlattenableUtils::write(buffer, size, len);
- memcpy(static_cast<char*>(buffer), string8.string(), len);
- FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
-}
-
-bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) {
- uint32_t len;
- if (size < sizeof(len)) {
- return false;
- }
- FlattenableUtils::read(buffer, size, len);
- if (size < len) {
- return false;
- }
- outputString8.setTo(static_cast<char const*>(buffer), len);
- FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
- return true;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
deleted file mode 100644
index 6d69839..0000000
--- a/libs/gui/SensorEventQueue.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Sensors"
-
-#include <algorithm>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <linux/errno.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Looper.h>
-
-#include <gui/Sensor.h>
-#include <gui/BitTube.h>
-#include <gui/SensorEventQueue.h>
-#include <gui/ISensorEventConnection.h>
-
-#include <android/sensor.h>
-
-using std::min;
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
- : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0),
- mNumAcksToSend(0) {
- mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
-}
-
-SensorEventQueue::~SensorEventQueue() {
- delete [] mRecBuffer;
-}
-
-void SensorEventQueue::onFirstRef()
-{
- mSensorChannel = mSensorEventConnection->getSensorChannel();
-}
-
-int SensorEventQueue::getFd() const
-{
- return mSensorChannel->getFd();
-}
-
-
-ssize_t SensorEventQueue::write(const sp<BitTube>& tube,
- ASensorEvent const* events, size_t numEvents) {
- return BitTube::sendObjects(tube, events, numEvents);
-}
-
-ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) {
- if (mAvailable == 0) {
- ssize_t err = BitTube::recvObjects(mSensorChannel,
- mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
- if (err < 0) {
- return err;
- }
- mAvailable = static_cast<size_t>(err);
- mConsumed = 0;
- }
- size_t count = min(numEvents, mAvailable);
- memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent));
- mAvailable -= count;
- mConsumed += count;
- return static_cast<ssize_t>(count);
-}
-
-sp<Looper> SensorEventQueue::getLooper() const
-{
- Mutex::Autolock _l(mLock);
- if (mLooper == 0) {
- mLooper = new Looper(true);
- mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
- }
- return mLooper;
-}
-
-status_t SensorEventQueue::waitForEvent() const
-{
- const int fd = getFd();
- sp<Looper> looper(getLooper());
-
- int events;
- int32_t result;
- do {
- result = looper->pollOnce(-1, NULL, &events, NULL);
- if (result == ALOOPER_POLL_ERROR) {
- ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
- result = -EPIPE; // unknown error, so we make up one
- break;
- }
- if (events & ALOOPER_EVENT_HANGUP) {
- // the other-side has died
- ALOGE("SensorEventQueue::waitForEvent error HANGUP");
- result = -EPIPE; // unknown error, so we make up one
- break;
- }
- } while (result != fd);
-
- return (result == fd) ? status_t(NO_ERROR) : result;
-}
-
-status_t SensorEventQueue::wake() const
-{
- sp<Looper> looper(getLooper());
- looper->wake();
- return NO_ERROR;
-}
-
-status_t SensorEventQueue::enableSensor(Sensor const* sensor) const {
- return enableSensor(sensor, SENSOR_DELAY_NORMAL);
-}
-
-status_t SensorEventQueue::enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const {
- return mSensorEventConnection->enableDisable(sensor->getHandle(), true,
- us2ns(samplingPeriodUs), 0, 0);
-}
-
-status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
- return mSensorEventConnection->enableDisable(sensor->getHandle(), false, 0, 0, 0);
-}
-
-status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs,
- int maxBatchReportLatencyUs, int reservedFlags) const {
- return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs),
- us2ns(maxBatchReportLatencyUs), reservedFlags);
-}
-
-status_t SensorEventQueue::flush() const {
- return mSensorEventConnection->flush();
-}
-
-status_t SensorEventQueue::disableSensor(int32_t handle) const {
- return mSensorEventConnection->enableDisable(handle, false, 0, 0, false);
-}
-
-status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const {
- return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
-}
-
-status_t SensorEventQueue::injectSensorEvent(const ASensorEvent& event) {
- do {
- // Blocking call.
- ssize_t size = ::send(mSensorChannel->getFd(), &event, sizeof(event), MSG_NOSIGNAL);
- if (size >= 0) {
- return NO_ERROR;
- } else if (size < 0 && errno == EAGAIN) {
- // If send is returning a "Try again" error, sleep for 100ms and try again. In all
- // other cases log a failure and exit.
- usleep(100000);
- } else {
- ALOGE("injectSensorEvent failure %s %zd", strerror(errno), size);
- return INVALID_OPERATION;
- }
- } while (true);
-}
-
-void SensorEventQueue::sendAck(const ASensorEvent* events, int count) {
- for (int i = 0; i < count; ++i) {
- if (events[i].flags & WAKE_UP_SENSOR_EVENT_NEEDS_ACK) {
- ++mNumAcksToSend;
- }
- }
- // Send mNumAcksToSend to acknowledge for the wake up sensor events received.
- if (mNumAcksToSend > 0) {
- ssize_t size = ::send(mSensorChannel->getFd(), &mNumAcksToSend, sizeof(mNumAcksToSend),
- MSG_DONTWAIT | MSG_NOSIGNAL);
- if (size < 0) {
- ALOGE("sendAck failure %zd %d", size, mNumAcksToSend);
- } else {
- mNumAcksToSend = 0;
- }
- }
- return;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
deleted file mode 100644
index 9fcf9ab..0000000
--- a/libs/gui/SensorManager.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Sensors"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Singleton.h>
-
-#include <binder/IBinder.h>
-#include <binder/IServiceManager.h>
-
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-#include <gui/Sensor.h>
-#include <gui/SensorManager.h>
-#include <gui/SensorEventQueue.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-android::Mutex android::SensorManager::sLock;
-std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
-
-SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) {
- Mutex::Autolock _l(sLock);
- SensorManager* sensorManager;
- std::map<String16, SensorManager*>::iterator iterator =
- sPackageInstances.find(packageName);
-
- if (iterator != sPackageInstances.end()) {
- sensorManager = iterator->second;
- } else {
- String16 opPackageName = packageName;
-
- // It is possible that the calling code has no access to the package name.
- // In this case we will get the packages for the calling UID and pick the
- // first one for attributing the app op. This will work correctly for
- // runtime permissions as for legacy apps we will toggle the app op for
- // all packages in the UID. The caveat is that the operation may be attributed
- // to the wrong package and stats based on app ops may be slightly off.
- if (opPackageName.size() <= 0) {
- sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
- if (binder != 0) {
- const uid_t uid = IPCThreadState::self()->getCallingUid();
- Vector<String16> packages;
- interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
- if (!packages.isEmpty()) {
- opPackageName = packages[0];
- } else {
- ALOGE("No packages for calling UID");
- }
- } else {
- ALOGE("Cannot get permission service");
- }
- }
-
- sensorManager = new SensorManager(opPackageName);
-
- // If we had no package name, we looked it up from the UID and the sensor
- // manager instance we created should also be mapped to the empty package
- // name, to avoid looking up the packages for a UID and get the same result.
- if (packageName.size() <= 0) {
- sPackageInstances.insert(std::make_pair(String16(), sensorManager));
- }
-
- // Stash the per package sensor manager.
- sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
- }
-
- return *sensorManager;
-}
-
-SensorManager::SensorManager(const String16& opPackageName)
- : mSensorList(0), mOpPackageName(opPackageName) {
- // okay we're not locked here, but it's not needed during construction
- assertStateLocked();
-}
-
-SensorManager::~SensorManager() {
- free(mSensorList);
-}
-
-void SensorManager::sensorManagerDied() {
- Mutex::Autolock _l(mLock);
- mSensorServer.clear();
- free(mSensorList);
- mSensorList = NULL;
- mSensors.clear();
-}
-
-status_t SensorManager::assertStateLocked() {
- bool initSensorManager = false;
- if (mSensorServer == NULL) {
- initSensorManager = true;
- } else {
- // Ping binder to check if sensorservice is alive.
- status_t err = IInterface::asBinder(mSensorServer)->pingBinder();
- if (err != NO_ERROR) {
- initSensorManager = true;
- }
- }
- if (initSensorManager) {
- // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ...
- const String16 name("sensorservice");
- for (int i = 0; i < 60; i++) {
- status_t err = getService(name, &mSensorServer);
- if (err == NAME_NOT_FOUND) {
- sleep(1);
- continue;
- }
- if (err != NO_ERROR) {
- return err;
- }
- break;
- }
-
- class DeathObserver : public IBinder::DeathRecipient {
- SensorManager& mSensorManager;
- virtual void binderDied(const wp<IBinder>& who) {
- ALOGW("sensorservice died [%p]", who.unsafe_get());
- mSensorManager.sensorManagerDied();
- }
- public:
- DeathObserver(SensorManager& mgr) : mSensorManager(mgr) { }
- };
-
- LOG_ALWAYS_FATAL_IF(mSensorServer.get() == NULL, "getService(SensorService) NULL");
-
- mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this));
- IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver);
-
- mSensors = mSensorServer->getSensorList(mOpPackageName);
- size_t count = mSensors.size();
- mSensorList =
- static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
- LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL");
-
- for (size_t i=0 ; i<count ; i++) {
- mSensorList[i] = mSensors.array() + i;
- }
- }
-
- return NO_ERROR;
-}
-
-ssize_t SensorManager::getSensorList(Sensor const* const** list) {
- Mutex::Autolock _l(mLock);
- status_t err = assertStateLocked();
- if (err < 0) {
- return static_cast<ssize_t>(err);
- }
- *list = mSensorList;
- return static_cast<ssize_t>(mSensors.size());
-}
-
-ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) {
- Mutex::Autolock _l(mLock);
- status_t err = assertStateLocked();
- if (err < 0) {
- return static_cast<ssize_t>(err);
- }
-
- dynamicSensors = mSensorServer->getDynamicSensorList(mOpPackageName);
- size_t count = dynamicSensors.size();
-
- return static_cast<ssize_t>(count);
-}
-
-Sensor const* SensorManager::getDefaultSensor(int type)
-{
- Mutex::Autolock _l(mLock);
- if (assertStateLocked() == NO_ERROR) {
- bool wakeUpSensor = false;
- // For the following sensor types, return a wake-up sensor. These types are by default
- // defined as wake-up sensors. For the rest of the sensor types defined in sensors.h return
- // a non_wake-up version.
- if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION ||
- type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
- type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
- type == SENSOR_TYPE_WRIST_TILT_GESTURE) {
- wakeUpSensor = true;
- }
- // For now we just return the first sensor of that type we find.
- // in the future it will make sense to let the SensorService make
- // that decision.
- for (size_t i=0 ; i<mSensors.size() ; i++) {
- if (mSensorList[i]->getType() == type &&
- mSensorList[i]->isWakeUpSensor() == wakeUpSensor) {
- return mSensorList[i];
- }
- }
- }
- return NULL;
-}
-
-sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
- sp<SensorEventQueue> queue;
-
- Mutex::Autolock _l(mLock);
- while (assertStateLocked() == NO_ERROR) {
- sp<ISensorEventConnection> connection =
- mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName);
- if (connection == NULL) {
- // SensorService just died or the app doesn't have required permissions.
- ALOGE("createEventQueue: connection is NULL.");
- return NULL;
- }
- queue = new SensorEventQueue(connection);
- break;
- }
- return queue;
-}
-
-bool SensorManager::isDataInjectionEnabled() {
- Mutex::Autolock _l(mLock);
- if (assertStateLocked() == NO_ERROR) {
- return mSensorServer->isDataInjectionEnabled();
- }
- return false;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 5a2ca8d..7b2b5c3 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -18,25 +18,28 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
-#include <android/native_window.h>
+#include <gui/Surface.h>
-#include <binder/Parcel.h>
+#include <android/native_window.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/NativeHandle.h>
+#include <ui/DisplayStatInfo.h>
#include <ui/Fence.h>
+#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
+#include <gui/BufferItem.h>
#include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <gui/GLConsumer.h>
-#include <gui/Surface.h>
+#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
namespace android {
Surface::Surface(
@@ -49,7 +52,10 @@
mAutoRefresh(false),
mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
mSharedBufferHasBeenQueued(false),
- mNextFrameNumber(1)
+ mQueriedSupportedTimestamps(false),
+ mFrameTimestampsSupportsPresent(false),
+ mEnableFrameTimestamps(false),
+ mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>())
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
@@ -93,6 +99,14 @@
}
}
+sp<ISurfaceComposer> Surface::composerService() const {
+ return ComposerService::getComposerService();
+}
+
+nsecs_t Surface::now() const {
+ return systemTime();
+}
+
sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
return mGraphicBufferProducer;
}
@@ -135,37 +149,224 @@
outTransformMatrix);
}
-bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
- nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
- nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) {
+ ATRACE_CALL();
+
+ DisplayStatInfo stats;
+ status_t err = composerService()->getDisplayStats(NULL, &stats);
+
+ *outRefreshDuration = stats.vsyncPeriod;
+
+ return NO_ERROR;
+}
+
+void Surface::enableFrameTimestamps(bool enable) {
+ Mutex::Autolock lock(mMutex);
+ // If going from disabled to enabled, get the initial values for
+ // compositor and display timing.
+ if (!mEnableFrameTimestamps && enable) {
+ FrameEventHistoryDelta delta;
+ mGraphicBufferProducer->getFrameTimestamps(&delta);
+ mFrameEventHistory->applyDelta(delta);
+ }
+ mEnableFrameTimestamps = enable;
+}
+
+status_t Surface::getCompositorTiming(
+ nsecs_t* compositeDeadline, nsecs_t* compositeInterval,
+ nsecs_t* compositeToPresentLatency) {
+ Mutex::Autolock lock(mMutex);
+ if (!mEnableFrameTimestamps) {
+ return INVALID_OPERATION;
+ }
+
+ if (compositeDeadline != nullptr) {
+ *compositeDeadline =
+ mFrameEventHistory->getNextCompositeDeadline(now());
+ }
+ if (compositeInterval != nullptr) {
+ *compositeInterval = mFrameEventHistory->getCompositeInterval();
+ }
+ if (compositeToPresentLatency != nullptr) {
+ *compositeToPresentLatency =
+ mFrameEventHistory->getCompositeToPresentLatency();
+ }
+ return NO_ERROR;
+}
+
+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* outGpuCompositionDoneTime,
+ const nsecs_t* outDisplayPresentTime,
+ const nsecs_t* outDequeueReadyTime,
+ const nsecs_t* outReleaseTime) {
+ bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo();
+ bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) &&
+ !e->hasFirstRefreshStartInfo();
+ bool checkForGpuCompositionDone = (outGpuCompositionDoneTime != nullptr) &&
+ !e->hasGpuCompositionDoneInfo();
+ bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) &&
+ !e->hasDisplayPresentInfo();
+
+ // LastRefreshStart, DequeueReady, and Release are never available for the
+ // last frame.
+ bool checkForLastRefreshStart = (outLastRefreshStartTime != nullptr) &&
+ !e->hasLastRefreshStartInfo() &&
+ (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 || checkForGpuCompositionDone ||
+ checkForDisplayPresent || checkForDequeueReady || checkForRelease;
+}
+
+static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) {
+ if (dst != nullptr) {
+ // We always get valid timestamps for these eventually.
+ *dst = (src == FrameEvents::TIMESTAMP_PENDING) ?
+ NATIVE_WINDOW_TIMESTAMP_PENDING : src;
+ }
+}
+
+static void getFrameTimestampFence(nsecs_t *dst,
+ const std::shared_ptr<FenceTime>& src, bool fenceShouldBeKnown) {
+ if (dst != nullptr) {
+ if (!fenceShouldBeKnown) {
+ *dst = NATIVE_WINDOW_TIMESTAMP_PENDING;
+ return;
+ }
+
+ nsecs_t signalTime = src->getSignalTime();
+ *dst = (signalTime == Fence::SIGNAL_TIME_PENDING) ?
+ NATIVE_WINDOW_TIMESTAMP_PENDING :
+ (signalTime == Fence::SIGNAL_TIME_INVALID) ?
+ NATIVE_WINDOW_TIMESTAMP_INVALID :
+ signalTime;
+ }
+}
+
+status_t Surface::getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+ nsecs_t* outLastRefreshStartTime, nsecs_t* outGpuCompositionDoneTime,
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
nsecs_t* outReleaseTime) {
ATRACE_CALL();
- FrameTimestamps timestamps;
- bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
- ×tamps);
- if (found) {
- if (outPostedTime) {
- *outPostedTime = timestamps.postedTime;
- }
- if (outAcquireTime) {
- *outAcquireTime = timestamps.acquireTime;
- }
- if (outRefreshStartTime) {
- *outRefreshStartTime = timestamps.refreshStartTime;
- }
- if (outGlCompositionDoneTime) {
- *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
- }
- if (outDisplayRetireTime) {
- *outDisplayRetireTime = timestamps.displayRetireTime;
- }
- if (outReleaseTime) {
- *outReleaseTime = timestamps.releaseTime;
- }
- return true;
+ Mutex::Autolock lock(mMutex);
+
+ if (!mEnableFrameTimestamps) {
+ return INVALID_OPERATION;
}
- return false;
+
+ // Verify the requested timestamps are supported.
+ querySupportedTimestampsLocked();
+ if (outDisplayPresentTime != nullptr && !mFrameTimestampsSupportsPresent) {
+ return BAD_VALUE;
+ }
+
+ 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;
+ }
+
+ // Update our cache of events if the requested events are not available.
+ if (checkConsumerForUpdates(events, mLastFrameNumber,
+ outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGpuCompositionDoneTime, outDisplayPresentTime,
+ 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(outLatchTime, events->latchTime);
+ getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime);
+ getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime);
+ getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime);
+
+ getFrameTimestampFence(outAcquireTime, events->acquireFence,
+ events->hasAcquireInfo());
+ getFrameTimestampFence(outGpuCompositionDoneTime,
+ events->gpuCompositionDoneFence,
+ events->hasGpuCompositionDoneInfo());
+ getFrameTimestampFence(outDisplayPresentTime, events->displayPresentFence,
+ events->hasDisplayPresentInfo());
+ getFrameTimestampFence(outReleaseTime, events->releaseFence,
+ events->hasReleaseInfo());
+
+ return NO_ERROR;
+}
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+status_t Surface::getWideColorSupport(bool* supported) {
+ ATRACE_CALL();
+
+ sp<IBinder> display(
+ composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ Vector<android_color_mode_t> colorModes;
+ status_t err =
+ composerService()->getDisplayColorModes(display, &colorModes);
+
+ if (err)
+ return err;
+
+ bool wideColorBoardConfig =
+ getBool<ISurfaceFlingerConfigs,
+ &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
+ *supported = false;
+ for (android_color_mode_t colorMode : colorModes) {
+ switch (colorMode) {
+ case HAL_COLOR_MODE_DISPLAY_P3:
+ case HAL_COLOR_MODE_ADOBE_RGB:
+ case HAL_COLOR_MODE_DCI_P3:
+ if (wideColorBoardConfig) {
+ *supported = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t Surface::getHdrSupport(bool* supported) {
+ ATRACE_CALL();
+
+ sp<IBinder> display(
+ composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ HdrCapabilities hdrCapabilities;
+ status_t err =
+ composerService()->getHdrCapabilities(display, &hdrCapabilities);
+
+ if (err)
+ return err;
+
+ *supported = !hdrCapabilities.getSupportedHdrTypes().empty();
+
+ return NO_ERROR;
}
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
@@ -271,9 +472,13 @@
uint32_t reqHeight;
PixelFormat reqFormat;
uint32_t reqUsage;
+ bool enableFrameTimestamps;
{
Mutex::Autolock lock(mMutex);
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.clear();
+ }
reqWidth = mReqWidth ? mReqWidth : mUserWidth;
reqHeight = mReqHeight ? mReqHeight : mUserHeight;
@@ -281,6 +486,8 @@
reqFormat = mReqFormat;
reqUsage = mReqUsage;
+ enableFrameTimestamps = mEnableFrameTimestamps;
+
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -294,10 +501,13 @@
int buf = -1;
sp<Fence> fence;
- nsecs_t now = systemTime();
+ nsecs_t startTime = systemTime();
+
+ FrameEventHistoryDelta frameTimestamps;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
- reqWidth, reqHeight, reqFormat, reqUsage);
- mLastDequeueDuration = systemTime() - now;
+ reqWidth, reqHeight, reqFormat, reqUsage,
+ enableFrameTimestamps ? &frameTimestamps : nullptr);
+ mLastDequeueDuration = systemTime() - startTime;
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
@@ -314,6 +524,9 @@
Mutex::Autolock lock(mMutex);
+ // Write this while holding the mutex
+ mLastDequeueStartTime = startTime;
+
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// this should never happen
@@ -323,7 +536,14 @@
freeAllBuffers();
}
- if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
+ if (enableFrameTimestamps) {
+ mFrameEventHistory->applyDelta(frameTimestamps);
+ }
+
+ if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
+ if (mReportRemovedBuffers && (gbuf != nullptr)) {
+ mRemovedBuffers.push_back(gbuf);
+ }
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
@@ -414,7 +634,7 @@
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
- timestamp / 1000000.f);
+ timestamp / 1000000.0);
} else {
timestamp = mTimestamp;
}
@@ -441,7 +661,7 @@
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
- fence, mStickyTransform);
+ fence, mStickyTransform, mEnableFrameTimestamps);
if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
input.setSurfaceDamage(Region::INVALID_REGION);
@@ -513,17 +733,31 @@
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
- uint32_t numPendingBuffers = 0;
- uint32_t hint = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers, &mNextFrameNumber);
+ if (mEnableFrameTimestamps) {
+ 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,
+ std::make_shared<FenceTime>(std::move(fence)));
+
+ // Cache timestamps of signaled fences so we can close their file
+ // descriptors.
+ mFrameEventHistory->updateSignalTimes();
+ }
+
+ mLastFrameNumber = mNextFrameNumber;
+
+ mDefaultWidth = output.width;
+ mDefaultHeight = output.height;
+ mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
- mTransformHint = hint;
+ mTransformHint = output.transformHint;
}
- mConsumerRunningBehind = (numPendingBuffers >= 2);
+ mConsumerRunningBehind = (output.numPendingBuffers >= 2);
if (!mConnectedToCpu) {
// Clear surface damage back to full-buffer
@@ -539,6 +773,29 @@
return err;
}
+void Surface::querySupportedTimestampsLocked() const {
+ // mMutex must be locked when calling this method.
+
+ if (mQueriedSupportedTimestamps) {
+ return;
+ }
+ mQueriedSupportedTimestamps = true;
+
+ std::vector<FrameEvent> supportedFrameTimestamps;
+ status_t err = composerService()->getSupportedFrameTimestamps(
+ &supportedFrameTimestamps);
+
+ if (err != NO_ERROR) {
+ return;
+ }
+
+ for (auto sft : supportedFrameTimestamps) {
+ if (sft == FrameEvent::DISPLAY_PRESENT) {
+ mFrameTimestampsSupportsPresent = true;
+ }
+ }
+}
+
int Surface::query(int what, int* value) const {
ATRACE_CALL();
ALOGV("Surface::query");
@@ -552,9 +809,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;
@@ -601,6 +857,15 @@
static_cast<int>(durationUs);
return NO_ERROR;
}
+ case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT: {
+ querySupportedTimestampsLocked();
+ *value = mFrameTimestampsSupportsPresent ? 1 : 0;
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_IS_VALID: {
+ *value = mGraphicBufferProducer != nullptr ? 1 : 0;
+ return NO_ERROR;
+ }
}
}
return mGraphicBufferProducer->query(what, value);
@@ -676,9 +941,27 @@
case NATIVE_WINDOW_SET_AUTO_REFRESH:
res = dispatchSetAutoRefresh(args);
break;
+ case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION:
+ res = dispatchGetDisplayRefreshCycleDuration(args);
+ break;
+ case NATIVE_WINDOW_GET_NEXT_FRAME_ID:
+ res = dispatchGetNextFrameId(args);
+ break;
+ case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS:
+ res = dispatchEnableFrameTimestamps(args);
+ break;
+ case NATIVE_WINDOW_GET_COMPOSITOR_TIMING:
+ res = dispatchGetCompositorTiming(args);
+ break;
case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
res = dispatchGetFrameTimestamps(args);
break;
+ case NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT:
+ res = dispatchGetWideColorSupport(args);
+ break;
+ case NATIVE_WINDOW_GET_HDR_SUPPORT:
+ res = dispatchGetHdrSupport(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -799,18 +1082,57 @@
return setAutoRefresh(autoRefresh);
}
+int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) {
+ nsecs_t* outRefreshDuration = va_arg(args, int64_t*);
+ return getDisplayRefreshCycleDuration(outRefreshDuration);
+}
+
+int Surface::dispatchGetNextFrameId(va_list args) {
+ uint64_t* nextFrameId = va_arg(args, uint64_t*);
+ *nextFrameId = getNextFrameNumber();
+ return NO_ERROR;
+}
+
+int Surface::dispatchEnableFrameTimestamps(va_list args) {
+ bool enable = va_arg(args, int);
+ enableFrameTimestamps(enable);
+ return NO_ERROR;
+}
+
+int Surface::dispatchGetCompositorTiming(va_list args) {
+ nsecs_t* compositeDeadline = va_arg(args, int64_t*);
+ nsecs_t* compositeInterval = va_arg(args, int64_t*);
+ nsecs_t* compositeToPresentLatency = va_arg(args, int64_t*);
+ return getCompositorTiming(compositeDeadline, compositeInterval,
+ compositeToPresentLatency);
+}
+
int Surface::dispatchGetFrameTimestamps(va_list args) {
- uint32_t framesAgo = va_arg(args, uint32_t);
- nsecs_t* outPostedTime = va_arg(args, int64_t*);
+ uint64_t frameId = va_arg(args, uint64_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* outGlCompositionDoneTime = va_arg(args, int64_t*);
- nsecs_t* outDisplayRetireTime = 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* outGpuCompositionDoneTime = va_arg(args, int64_t*);
+ nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
+ nsecs_t* outDequeueReadyTime = va_arg(args, int64_t*);
nsecs_t* outReleaseTime = va_arg(args, int64_t*);
- bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
- outPostedTime, outAcquireTime, outRefreshStartTime,
- outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
- return ret ? NO_ERROR : BAD_VALUE;
+ return getFrameTimestamps(frameId,
+ outRequestedPresentTime, outAcquireTime, outLatchTime,
+ outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGpuCompositionDoneTime, outDisplayPresentTime,
+ outDequeueReadyTime, outReleaseTime);
+}
+
+int Surface::dispatchGetWideColorSupport(va_list args) {
+ bool* outSupport = va_arg(args, bool*);
+ return getWideColorSupport(outSupport);
+}
+
+int Surface::dispatchGetHdrSupport(va_list args) {
+ bool* outSupport = va_arg(args, bool*);
+ return getHdrSupport(outSupport);
}
int Surface::connect(int api) {
@@ -819,23 +1141,28 @@
}
int Surface::connect(int api, const sp<IProducerListener>& listener) {
+ return connect(api, listener, false);
+}
+
+int Surface::connect(
+ int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
ATRACE_CALL();
ALOGV("Surface::connect");
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
+ mReportRemovedBuffers = reportBufferRemoval;
int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
if (err == NO_ERROR) {
- uint32_t numPendingBuffers = 0;
- uint32_t hint = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers, &mNextFrameNumber);
+ mDefaultWidth = output.width;
+ mDefaultHeight = output.height;
+ mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
- mTransformHint = hint;
+ mTransformHint = output.transformHint;
}
- mConsumerRunningBehind = (numPendingBuffers >= 2);
+ mConsumerRunningBehind = (output.numPendingBuffers >= 2);
}
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
@@ -854,6 +1181,7 @@
ATRACE_CALL();
ALOGV("Surface::disconnect");
Mutex::Autolock lock(mMutex);
+ mRemovedBuffers.clear();
mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
mSharedBufferHasBeenQueued = false;
freeAllBuffers();
@@ -885,6 +1213,9 @@
}
Mutex::Autolock lock(mMutex);
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.clear();
+ }
sp<GraphicBuffer> buffer(NULL);
sp<Fence> fence(NULL);
@@ -903,7 +1234,10 @@
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (mSlots[i].buffer != NULL &&
- mSlots[i].buffer->handle == buffer->handle) {
+ mSlots[i].buffer->getId() == buffer->getId()) {
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.push_back(mSlots[i].buffer);
+ }
mSlots[i].buffer = NULL;
}
}
@@ -917,6 +1251,9 @@
ALOGV("Surface::attachBuffer");
Mutex::Autolock lock(mMutex);
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.clear();
+ }
sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
uint32_t priorGeneration = graphicBuffer->mGenerationNumber;
@@ -929,6 +1266,9 @@
graphicBuffer->mGenerationNumber = priorGeneration;
return result;
}
+ if (mReportRemovedBuffers && (mSlots[attachedSlot].buffer != nullptr)) {
+ mRemovedBuffers.push_back(mSlots[attachedSlot].buffer);
+ }
mSlots[attachedSlot].buffer = graphicBuffer;
return NO_ERROR;
@@ -1178,7 +1518,8 @@
static status_t copyBlt(
const sp<GraphicBuffer>& dst,
const sp<GraphicBuffer>& src,
- const Region& reg)
+ const Region& reg,
+ int *dstFenceFd)
{
// src and dst with, height and format must be identical. no verification
// is done here.
@@ -1189,9 +1530,10 @@
ALOGE_IF(err, "error locking src buffer %s", strerror(-err));
uint8_t* dst_bits = NULL;
- err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(),
- reinterpret_cast<void**>(&dst_bits));
+ err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(),
+ reinterpret_cast<void**>(&dst_bits), *dstFenceFd);
ALOGE_IF(err, "error locking dst buffer %s", strerror(-err));
+ *dstFenceFd = -1;
Region::const_iterator head(reg.begin());
Region::const_iterator tail(reg.end());
@@ -1225,7 +1567,7 @@
src->unlock();
if (dst_bits)
- dst->unlock();
+ dst->unlockAsync(dstFenceFd);
return err;
}
@@ -1275,8 +1617,9 @@
if (canCopyBack) {
// copy the area that is invalid and not repainted this round
const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
- if (!copyback.isEmpty())
- copyBlt(backBuffer, frontBuffer, copyback);
+ if (!copyback.isEmpty()) {
+ copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
+ }
} else {
// if we can't copy-back anything, modify the user's dirty
// region to make sure they redraw the whole buffer
@@ -1359,70 +1702,21 @@
return mGraphicBufferProducer->getUniqueId(outId);
}
-namespace view {
-
-status_t Surface::writeToParcel(Parcel* parcel) const {
- return writeToParcel(parcel, false);
+nsecs_t Surface::getLastDequeueStartTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mLastDequeueStartTime;
}
-status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
- if (parcel == nullptr) return BAD_VALUE;
-
- status_t res = OK;
-
- if (!nameAlreadyWritten) {
- res = parcel->writeString16(name);
- if (res != OK) return res;
-
- /* isSingleBuffered defaults to no */
- res = parcel->writeInt32(0);
- if (res != OK) return res;
+status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) {
+ if (out == nullptr) {
+ ALOGE("%s: out must not be null!", __FUNCTION__);
+ return BAD_VALUE;
}
- res = parcel->writeStrongBinder(
- IGraphicBufferProducer::asBinder(graphicBufferProducer));
-
- return res;
-}
-
-status_t Surface::readFromParcel(const Parcel* parcel) {
- return readFromParcel(parcel, false);
-}
-
-status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
- if (parcel == nullptr) return BAD_VALUE;
-
- status_t res = OK;
- if (!nameAlreadyRead) {
- name = readMaybeEmptyString16(parcel);
- // Discard this for now
- int isSingleBuffered;
- res = parcel->readInt32(&isSingleBuffered);
- if (res != OK) {
- return res;
- }
- }
-
- sp<IBinder> binder;
-
- res = parcel->readStrongBinder(&binder);
- if (res != OK) return res;
-
- graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
-
+ Mutex::Autolock lock(mMutex);
+ *out = mRemovedBuffers;
+ mRemovedBuffers.clear();
return OK;
}
-String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
- size_t len;
- const char16_t* str = parcel->readString16Inplace(&len);
- if (str != nullptr) {
- return String16(str, len);
- } else {
- return String16();
- }
-}
-
-} // namespace view
-
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b78de2e..8c83843 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -26,17 +26,18 @@
#include <utils/String8.h>
#include <utils/threads.h>
-#include <binder/IMemory.h>
#include <binder/IServiceManager.h>
#include <system/graphics.h>
#include <ui/DisplayInfo.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
+#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
@@ -69,7 +70,7 @@
mComposerService.composerServiceDied();
}
public:
- DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
+ explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
};
mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
@@ -129,6 +130,8 @@
void openGlobalTransactionImpl();
void closeGlobalTransactionImpl(bool synchronous);
void setAnimationTransactionImpl();
+ status_t enableVSyncInjectionsImpl(bool enable);
+ status_t injectVSyncImpl(nsecs_t when);
layer_state_t* getLayerStateLocked(
const sp<SurfaceComposerClient>& client, const sp<IBinder>& id);
@@ -145,7 +148,9 @@
status_t setSize(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
uint32_t w, uint32_t h);
status_t setLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
- uint32_t z);
+ int32_t z);
+ status_t setRelativeLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
+ const sp<IBinder>& relativeTo, int32_t z);
status_t setFlags(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
uint32_t flags, uint32_t mask);
status_t setTransparentRegionHint(
@@ -154,7 +159,7 @@
status_t setAlpha(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
float alpha);
status_t setMatrix(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
- float dsdx, float dtdx, float dsdy, float dtdy);
+ float dsdx, float dtdx, float dtdy, float dsdy);
status_t setOrientation(int orientation);
status_t setCrop(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
const Rect& crop);
@@ -165,6 +170,14 @@
status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, const sp<IBinder>& handle,
uint64_t frameNumber);
+ status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const sp<Surface>& barrierSurface,
+ uint64_t frameNumber);
+ status_t reparentChildren(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle);
+ status_t detachChildren(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id);
status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, int32_t overrideScalingMode);
status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client,
@@ -190,6 +203,14 @@
static void closeGlobalTransaction(bool synchronous) {
Composer::getInstance().closeGlobalTransactionImpl(synchronous);
}
+
+ static status_t enableVSyncInjections(bool enable) {
+ return Composer::getInstance().enableVSyncInjectionsImpl(enable);
+ }
+
+ static status_t injectVSync(nsecs_t when) {
+ return Composer::getInstance().injectVSyncImpl(when);
+ }
};
ANDROID_SINGLETON_STATIC_INSTANCE(Composer);
@@ -253,6 +274,16 @@
sm->setTransactionState(transaction, displayTransaction, flags);
}
+status_t Composer::enableVSyncInjectionsImpl(bool enable) {
+ sp<ISurfaceComposer> sm(ComposerService::getComposerService());
+ return sm->enableVSyncInjections(enable);
+}
+
+status_t Composer::injectVSyncImpl(nsecs_t when) {
+ sp<ISurfaceComposer> sm(ComposerService::getComposerService());
+ return sm->injectVSync(when);
+}
+
void Composer::setAnimationTransactionImpl() {
Mutex::Autolock _l(mLock);
mAnimation = true;
@@ -304,7 +335,7 @@
}
status_t Composer::setLayer(const sp<SurfaceComposerClient>& client,
- const sp<IBinder>& id, uint32_t z) {
+ const sp<IBinder>& id, int32_t z) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -314,6 +345,20 @@
return NO_ERROR;
}
+status_t Composer::setRelativeLayer(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const sp<IBinder>& relativeTo,
+ int32_t z) {
+ Mutex::Autolock _l(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eRelativeLayerChanged;
+ s->relativeLayerHandle = relativeTo;
+ s->z = z;
+ return NO_ERROR;
+}
+
status_t Composer::setFlags(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, uint32_t flags,
uint32_t mask) {
@@ -368,7 +413,7 @@
status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, float dsdx, float dtdx,
- float dsdy, float dtdy) {
+ float dtdy, float dsdy) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -415,11 +460,51 @@
return BAD_INDEX;
}
s->what |= layer_state_t::eDeferTransaction;
- s->handle = handle;
+ s->barrierHandle = handle;
s->frameNumber = frameNumber;
return NO_ERROR;
}
+status_t Composer::deferTransactionUntil(
+ const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
+ const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+ Mutex::Autolock lock(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eDeferTransaction;
+ s->barrierGbp = barrierSurface->getIGraphicBufferProducer();
+ s->frameNumber = frameNumber;
+ return NO_ERROR;
+}
+
+status_t Composer::reparentChildren(
+ const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle) {
+ Mutex::Autolock lock(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eReparentChildren;
+ s->reparentHandle = newParentHandle;
+ return NO_ERROR;
+}
+
+status_t Composer::detachChildren(
+ const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id) {
+ Mutex::Autolock lock(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eDetachChildren;
+ return NO_ERROR;
+}
+
status_t Composer::setOverrideScalingMode(
const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, int32_t overrideScalingMode) {
@@ -529,10 +614,18 @@
{
}
+SurfaceComposerClient::SurfaceComposerClient(const sp<IGraphicBufferProducer>& root)
+ : mStatus(NO_INIT), mComposer(Composer::getInstance()), mParent(root)
+{
+}
+
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sm(ComposerService::getComposerService());
if (sm != 0) {
- sp<ISurfaceComposerClient> conn = sm->createConnection();
+ auto rootProducer = mParent.promote();
+ sp<ISurfaceComposerClient> conn;
+ conn = (rootProducer != nullptr) ? sm->createScopedConnection(rootProducer) :
+ sm->createConnection();
if (conn != 0) {
mClient = conn;
mStatus = NO_ERROR;
@@ -575,14 +668,22 @@
uint32_t w,
uint32_t h,
PixelFormat format,
- uint32_t flags)
+ uint32_t flags,
+ SurfaceControl* parent,
+ uint32_t windowType,
+ uint32_t ownerUid)
{
sp<SurfaceControl> sur;
if (mStatus == NO_ERROR) {
sp<IBinder> handle;
+ sp<IBinder> parentHandle;
sp<IGraphicBufferProducer> gbp;
- status_t err = mClient->createSurface(name, w, h, format, flags,
- &handle, &gbp);
+
+ if (parent != nullptr) {
+ parentHandle = parent->getHandle();
+ }
+ status_t err = mClient->createSurface(name, w, h, format, flags, parentHandle,
+ windowType, ownerUid, &handle, &gbp);
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
sur = new SurfaceControl(this, handle, gbp);
@@ -626,14 +727,6 @@
return mClient->getLayerFrameStats(token, outStats);
}
-status_t SurfaceComposerClient::getTransformToDisplayInverse(const sp<IBinder>& token,
- bool* outTransformToDisplayInverse) const {
- if (mStatus != NO_ERROR) {
- return mStatus;
- }
- return mClient->getTransformToDisplayInverse(token, outTransformToDisplayInverse);
-}
-
inline Composer& SurfaceComposerClient::getComposer() {
return mComposer;
}
@@ -652,6 +745,14 @@
Composer::setAnimationTransaction();
}
+status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
+ return Composer::enableVSyncInjections(enable);
+}
+
+status_t SurfaceComposerClient::injectVSync(nsecs_t when) {
+ return Composer::injectVSync(when);
+}
+
// ----------------------------------------------------------------------------
status_t SurfaceComposerClient::setCrop(const sp<IBinder>& id, const Rect& crop) {
@@ -671,10 +772,15 @@
return getComposer().setSize(this, id, w, h);
}
-status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, uint32_t z) {
+status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, int32_t z) {
return getComposer().setLayer(this, id, z);
}
+status_t SurfaceComposerClient::setRelativeLayer(const sp<IBinder>& id,
+ const sp<IBinder>& relativeTo, int32_t z) {
+ return getComposer().setRelativeLayer(this, id, relativeTo, z);
+}
+
status_t SurfaceComposerClient::hide(const sp<IBinder>& id) {
return getComposer().setFlags(this, id,
layer_state_t::eLayerHidden,
@@ -706,8 +812,8 @@
}
status_t SurfaceComposerClient::setMatrix(const sp<IBinder>& id, float dsdx, float dtdx,
- float dsdy, float dtdy) {
- return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy);
+ float dtdy, float dsdy) {
+ return getComposer().setMatrix(this, id, dsdx, dtdx, dtdy, dsdy);
}
status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
@@ -715,6 +821,20 @@
return getComposer().deferTransactionUntil(this, id, handle, frameNumber);
}
+status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
+ const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+ return getComposer().deferTransactionUntil(this, id, barrierSurface, frameNumber);
+}
+
+status_t SurfaceComposerClient::reparentChildren(const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle) {
+ return getComposer().reparentChildren(this, id, newParentHandle);
+}
+
+status_t SurfaceComposerClient::detachChildren(const sp<IBinder>& id) {
+ return getComposer().detachChildren(this, id);
+}
+
status_t SurfaceComposerClient::setOverrideScalingMode(
const sp<IBinder>& id, int32_t overrideScalingMode) {
return getComposer().setOverrideScalingMode(
@@ -824,13 +944,40 @@
const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ, bool useIdentityTransform) {
+ int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == NULL) return NO_INIT;
return s->captureScreen(display, producer, sourceCrop,
reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform);
}
+status_t ScreenshotClient::captureToBuffer(const sp<IBinder>& display,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ int32_t minLayerZ, int32_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));
@@ -852,7 +999,7 @@
status_t ScreenshotClient::update(const sp<IBinder>& display,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform, uint32_t rotation) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == NULL) return NO_INIT;
@@ -879,7 +1026,7 @@
status_t ScreenshotClient::update(const sp<IBinder>& display,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform) {
return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight,
@@ -888,14 +1035,16 @@
status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop,
bool useIdentityTransform) {
- return ScreenshotClient::update(display, sourceCrop, 0, 0, 0, -1U,
+ return ScreenshotClient::update(display, sourceCrop, 0, 0,
+ INT32_MIN, INT32_MAX,
useIdentityTransform, ISurfaceComposer::eRotateNone);
}
status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop,
uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform) {
return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight,
- 0, -1U, useIdentityTransform, ISurfaceComposer::eRotateNone);
+ INT32_MIN, INT32_MAX,
+ useIdentityTransform, ISurfaceComposer::eRotateNone);
}
void ScreenshotClient::release() {
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 33c1d90..58bd273 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -102,11 +102,19 @@
if (err < 0) return err;
return mClient->setLayerStack(mHandle, layerStack);
}
-status_t SurfaceControl::setLayer(uint32_t layer) {
+
+status_t SurfaceControl::setLayer(int32_t layer) {
status_t err = validate();
if (err < 0) return err;
return mClient->setLayer(mHandle, layer);
}
+
+status_t SurfaceControl::setRelativeLayer(const sp<IBinder>& relativeTo, int32_t layer) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->setRelativeLayer(mHandle, relativeTo, layer);
+}
+
status_t SurfaceControl::setPosition(float x, float y) {
status_t err = validate();
if (err < 0) return err;
@@ -147,10 +155,10 @@
if (err < 0) return err;
return mClient->setAlpha(mHandle, alpha);
}
-status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
status_t err = validate();
if (err < 0) return err;
- return mClient->setMatrix(mHandle, dsdx, dtdx, dsdy, dtdy);
+ return mClient->setMatrix(mHandle, dsdx, dtdx, dtdy, dsdy);
}
status_t SurfaceControl::setCrop(const Rect& crop) {
status_t err = validate();
@@ -163,13 +171,32 @@
return mClient->setFinalCrop(mHandle, crop);
}
-status_t SurfaceControl::deferTransactionUntil(sp<IBinder> handle,
+status_t SurfaceControl::deferTransactionUntil(const sp<IBinder>& handle,
uint64_t frameNumber) {
status_t err = validate();
if (err < 0) return err;
return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
}
+status_t SurfaceControl::deferTransactionUntil(const sp<Surface>& handle,
+ uint64_t frameNumber) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
+}
+
+status_t SurfaceControl::reparentChildren(const sp<IBinder>& newParentHandle) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->reparentChildren(mHandle, newParentHandle);
+}
+
+status_t SurfaceControl::detachChildren() {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->detachChildren(mHandle);
+}
+
status_t SurfaceControl::setOverrideScalingMode(int32_t overrideScalingMode) {
status_t err = validate();
if (err < 0) return err;
@@ -190,13 +217,6 @@
return client->getLayerFrameStats(mHandle, outStats);
}
-status_t SurfaceControl::getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->getTransformToDisplayInverse(mHandle, outTransformToDisplayInverse);
-}
-
status_t SurfaceControl::validate() const
{
if (mHandle==0 || mClient==0) {
@@ -217,17 +237,30 @@
return parcel->writeStrongBinder(IInterface::asBinder(bp));
}
+sp<Surface> SurfaceControl::generateSurfaceLocked() const
+{
+ // This surface is always consumed by SurfaceFlinger, so the
+ // producerControlledByApp value doesn't matter; using false.
+ mSurfaceData = new Surface(mGraphicBufferProducer, false);
+
+ return mSurfaceData;
+}
+
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
- // This surface is always consumed by SurfaceFlinger, so the
- // producerControlledByApp value doesn't matter; using false.
- mSurfaceData = new Surface(mGraphicBufferProducer, false);
+ return generateSurfaceLocked();
}
return mSurfaceData;
}
+sp<Surface> SurfaceControl::createSurface() const
+{
+ Mutex::Autolock _l(mLock);
+ return generateSurfaceLocked();
+}
+
sp<IBinder> SurfaceControl::getHandle() const
{
Mutex::Autolock lock(mLock);
diff --git a/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
new file mode 100644
index 0000000..a5f28cd
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#include <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+// B2HProducerListener
+B2HProducerListener::B2HProducerListener(
+ sp<BProducerListener> const& base):
+ mBase(base) {
+}
+
+Return<void> B2HProducerListener::onBufferReleased() {
+ mBase->onBufferReleased();
+ return Void();
+}
+
+Return<bool> B2HProducerListener::needsReleaseNotify() {
+ return mBase->needsReleaseNotify();
+}
+
+} // namespace utils
+} // namespace V1_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
new file mode 100644
index 0000000..fda5b94
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -0,0 +1,1234 @@
+/*
+ * 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 "H2BGraphicBufferProducer"
+
+#include <android-base/logging.h>
+
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using Status = HGraphicBufferProducer::Status;
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+typedef ::android::hardware::media::V1_0::Rect HRect;
+typedef ::android::hardware::media::V1_0::Region HRegion;
+
+// Conversion functions
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline native_handle_t* native_handle_create_from_fd(int fd) {
+ if (fd < 0) {
+ return native_handle_create(0, 0);
+ }
+ native_handle_t* nh = native_handle_create(1, 0);
+ if (nh == nullptr) {
+ return nullptr;
+ }
+ nh->data[0] = fd;
+ return nh;
+}
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) {
+ return ((nh == nullptr) || (nh->numFds == 0) ||
+ (nh->numFds <= index) || (index < 0)) ?
+ -1 : nh->data[index];
+}
+
+/**
+ * \brief Convert `Return<Status>` to `status_t`. This is for legacy binder
+ * calls.
+ *
+ * \param[in] t The source `Return<Status>`.
+ * \return The corresponding `status_t`.
+ *
+ * This function first check if \p t has a transport error. If it does, then the
+ * return value is the transport error code. Otherwise, the return value is
+ * converted from `Status` contained inside \p t.
+ *
+ * Note:
+ * - This `Status` is omx-specific. It is defined in `types.hal`.
+ * - The name of this function is not `convert`.
+ */
+// convert: Return<Status> -> status_t
+inline status_t toStatusT(Return<Status> const& t) {
+ return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+inline status_t toStatusT(Return<void> const& t) {
+ return t.isOk() ? OK : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
+ t->attr.width = l.getWidth();
+ t->attr.height = l.getHeight();
+ t->attr.stride = l.getStride();
+ t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
+ t->attr.layerCount = l.getLayerCount();
+ t->attr.usage = l.getUsage();
+ t->attr.id = l.getId();
+ t->attr.generationNumber = l.getGenerationNumber();
+ t->nativeHandle = hidl_handle(l.handle);
+}
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
+ native_handle_t* handle = t.nativeHandle == nullptr ?
+ nullptr : native_handle_clone(t.nativeHandle);
+
+ size_t const numInts = 12 +
+ static_cast<size_t>(handle ? handle->numInts : 0);
+ int32_t* ints = new int32_t[numInts];
+
+ size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
+ int* fds = new int[numFds];
+
+ ints[0] = 'GBFR';
+ ints[1] = static_cast<int32_t>(t.attr.width);
+ ints[2] = static_cast<int32_t>(t.attr.height);
+ ints[3] = static_cast<int32_t>(t.attr.stride);
+ ints[4] = static_cast<int32_t>(t.attr.format);
+ ints[5] = static_cast<int32_t>(t.attr.layerCount);
+ ints[6] = static_cast<int32_t>(t.attr.usage);
+ ints[7] = static_cast<int32_t>(t.attr.id >> 32);
+ ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
+ ints[9] = static_cast<int32_t>(t.attr.generationNumber);
+ ints[10] = 0;
+ ints[11] = 0;
+ if (handle) {
+ ints[10] = static_cast<int32_t>(handle->numFds);
+ ints[11] = static_cast<int32_t>(handle->numInts);
+ int* intsStart = handle->data + handle->numFds;
+ std::copy(handle->data, intsStart, fds);
+ std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
+ }
+
+ void const* constBuffer = static_cast<void const*>(ints);
+ size_t size = numInts * sizeof(int32_t);
+ int const* constFds = static_cast<int const*>(fds);
+ status_t status = l->unflatten(constBuffer, size, constFds, numFds);
+
+ delete [] fds;
+ delete [] ints;
+ native_handle_delete(handle);
+ return status == NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
+ return 4;
+};
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+inline size_t getFenceFdCount(hidl_handle const& fence) {
+ return native_handle_read_fd(fence) == -1 ? 0 : 1;
+}
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < 4) {
+ return NO_MEMORY;
+ }
+
+ uint32_t numFdsInHandle;
+ FlattenableUtils::read(buffer, size, numFdsInHandle);
+
+ if (numFdsInHandle > 1) {
+ return BAD_VALUE;
+ }
+
+ if (numFds < numFdsInHandle) {
+ return NO_MEMORY;
+ }
+
+ if (numFdsInHandle) {
+ *nh = native_handle_create_from_fd(*fds);
+ if (*nh == nullptr) {
+ return NO_MEMORY;
+ }
+ *fence = *nh;
+ ++fds;
+ --numFds;
+ } else {
+ *nh = nullptr;
+ *fence = hidl_handle();
+ }
+
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] fence The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t flattenFence(hidl_handle const& fence,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFenceFlattenedSize(fence) ||
+ numFds < getFenceFdCount(fence)) {
+ return NO_MEMORY;
+ }
+ // Cast to uint32_t since the size of a size_t can vary between 32- and
+ // 64-bit processes
+ FlattenableUtils::write(buffer, size,
+ static_cast<uint32_t>(getFenceFdCount(fence)));
+ int fd = native_handle_read_fd(fence);
+ if (fd != -1) {
+ *fds = fd;
+ ++fds;
+ --numFds;
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
+ != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+inline bool convertTo(Fence* l, hidl_handle const& t) {
+ int fd = native_handle_read_fd(t);
+ if (fd != -1) {
+ fd = dup(fd);
+ if (fd == -1) {
+ return false;
+ }
+ }
+ native_handle_t* nh = native_handle_create_from_fd(fd);
+ if (nh == nullptr) {
+ if (fd != -1) {
+ close(fd);
+ }
+ return false;
+ }
+
+ size_t const baseSize = getFenceFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ native_handle_delete(nh);
+ return false;
+ }
+
+ size_t const baseNumFds = getFenceFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ native_handle_delete(nh);
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
+ native_handle_delete(nh);
+ return false;
+ }
+ native_handle_delete(nh);
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Unflatten `HRegion`.
+ *
+ * \param[out] t The destination `HRegion`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t unflatten(HRegion* t, void const*& buffer, size_t& size) {
+ if (size < sizeof(uint32_t)) {
+ return NO_MEMORY;
+ }
+
+ uint32_t numRects = 0;
+ FlattenableUtils::read(buffer, size, numRects);
+ if (size < numRects * sizeof(HRect)) {
+ return NO_MEMORY;
+ }
+ if (numRects > (UINT32_MAX / sizeof(HRect))) {
+ return NO_MEMORY;
+ }
+
+ t->resize(numRects);
+ for (size_t r = 0; r < numRects; ++r) {
+ ::android::Rect rect(::android::Rect::EMPTY_RECT);
+ status_t status = rect.unflatten(buffer, size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ FlattenableUtils::advance(buffer, size, sizeof(rect));
+ (*t)[r] = HRect{
+ static_cast<int32_t>(rect.left),
+ static_cast<int32_t>(rect.top),
+ static_cast<int32_t>(rect.right),
+ static_cast<int32_t>(rect.bottom)};
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+// IGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return a lower bound on the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+ HGraphicBufferProducer::QueueBufferInput const& /* t */) {
+ return sizeof(int64_t) + // timestamp
+ sizeof(int) + // isAutoTimestamp
+ sizeof(android_dataspace) + // dataSpace
+ sizeof(::android::Rect) + // crop
+ sizeof(int) + // scalingMode
+ sizeof(uint32_t) + // transform
+ sizeof(uint32_t) + // stickyTransform
+ sizeof(bool); // getFrameTimestamps
+}
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflatten(
+ HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < minFlattenedSize(*t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, t->timestamp);
+ int lIsAutoTimestamp;
+ FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
+ t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
+ android_dataspace_t lDataSpace;
+ FlattenableUtils::read(buffer, size, lDataSpace);
+ t->dataSpace = static_cast<Dataspace>(lDataSpace);
+ ::android::Rect lCrop;
+ FlattenableUtils::read(buffer, size, lCrop);
+ t->crop = HRect{
+ static_cast<int32_t>(lCrop.left),
+ static_cast<int32_t>(lCrop.top),
+ static_cast<int32_t>(lCrop.right),
+ static_cast<int32_t>(lCrop.bottom)};
+ int lScalingMode;
+ FlattenableUtils::read(buffer, size, lScalingMode);
+ t->scalingMode = static_cast<int32_t>(lScalingMode);
+ FlattenableUtils::read(buffer, size, t->transform);
+ FlattenableUtils::read(buffer, size, t->stickyTransform);
+ FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
+
+ status_t status = unflattenFence(&(t->fence), nh,
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ return unflatten(&(t->surfaceDamage), buffer, size);
+}
+
+/**
+ * \brief Wrap `IGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `IGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline bool wrapAs(
+ HGraphicBufferProducer::QueueBufferInput* t,
+ native_handle_t** nh,
+ BGraphicBufferProducer::QueueBufferInput const& l) {
+
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = baseFds.get();
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+ HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+ constexpr size_t min = sizeof(t.state);
+ switch (t.state) {
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+ return min;
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+ return min + getFenceFlattenedSize(t.fence);
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+ return min + sizeof(
+ ::android::FenceTime::Snapshot::signalTime);
+ }
+ return 0;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+inline size_t getFdCount(
+ HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+ return t.state ==
+ HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
+ getFenceFdCount(t.fence) : 0;
+}
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[out] nh The cloned native handle, if necessary.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`, in which case \p nh will be returned.
+ */
+inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+ native_handle_t** nh,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ *nh = nullptr;
+ switch (t.state) {
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::EMPTY);
+ return NO_ERROR;
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::FENCE);
+ *nh = t.fence.getNativeHandle() == nullptr ?
+ nullptr : native_handle_clone(t.fence);
+ return flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
+ FlattenableUtils::write(buffer, size, t.signalTimeNs);
+ return NO_ERROR;
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return a lower bound on the size of the non-fd buffer required to
+ * flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+ HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
+ return sizeof(uint64_t) + // mFrameNumber
+ sizeof(uint8_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddRetireCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(nsecs_t) + // mPostedTime
+ sizeof(nsecs_t) + // mRequestedPresentTime
+ sizeof(nsecs_t) + // mLatchTime
+ sizeof(nsecs_t) + // mFirstRefreshStartTime
+ sizeof(nsecs_t); // mLastRefreshStartTime
+}
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventsDelta const& t) {
+ return minFlattenedSize(t) +
+ getFlattenedSize(t.gpuCompositionDoneFence) +
+ getFlattenedSize(t.displayPresentFence) +
+ getFlattenedSize(t.displayRetireFence) +
+ getFlattenedSize(t.releaseFence);
+};
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+ HGraphicBufferProducer::FrameEventsDelta const& t) {
+ return getFdCount(t.gpuCompositionDoneFence) +
+ getFdCount(t.displayPresentFence) +
+ getFdCount(t.displayRetireFence) +
+ getFdCount(t.releaseFence);
+};
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[out] nh The array of native handles that are cloned.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. These native handles will
+ * need to be closed by the caller.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+// FrameEventsDelta::flatten
+inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+ std::vector<native_handle_t*>* nh,
+ void*& buffer, size_t& size, int*& fds, size_t numFds) {
+ // Check that t.index is within a valid range.
+ if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
+ || t.index > std::numeric_limits<uint8_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, t.frameNumber);
+
+ // These are static_cast to uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addRetireCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, t.postedTimeNs);
+ FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
+ FlattenableUtils::write(buffer, size, t.latchTimeNs);
+ FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
+ FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
+ FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
+
+ // Fences
+ HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
+ tSnapshot[0] = &t.gpuCompositionDoneFence;
+ tSnapshot[1] = &t.displayPresentFence;
+ tSnapshot[2] = &t.displayRetireFence;
+ tSnapshot[3] = &t.releaseFence;
+ nh->resize(4);
+ for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+ status_t status = flatten(
+ *(tSnapshot[snapshotIndex]),
+ &((*nh)[snapshotIndex]),
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ while (snapshotIndex > 0) {
+ --snapshotIndex;
+ native_handle_close((*nh)[snapshotIndex]);
+ native_handle_delete((*nh)[snapshotIndex]);
+ (*nh)[snapshotIndex] = nullptr;
+ }
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+ size_t size = 4 + // mDeltas.size()
+ sizeof(t.compositorTiming);
+ for (size_t i = 0; i < t.deltas.size(); ++i) {
+ size += getFlattenedSize(t.deltas[i]);
+ }
+ return size;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+ size_t numFds = 0;
+ for (size_t i = 0; i < t.deltas.size(); ++i) {
+ numFds += getFdCount(t.deltas[i]);
+ }
+ return numFds;
+}
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[out] nh The array of arrays of cloned native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. Before making the call, \p
+ * nh should have enough space to store `n` pointers to arrays of native
+ * handles, where `n` is the length of `t.deltas`, and each `nh[i]` should have
+ * enough space to store `4` native handles.
+ */
+inline status_t flatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, t.compositorTiming);
+
+ FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
+ nh->resize(t.deltas.size());
+ for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
+ status_t status = flatten(
+ t.deltas[deltaIndex], &((*nh)[deltaIndex]),
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ while (deltaIndex > 0) {
+ --deltaIndex;
+ for (size_t snapshotIndex = 0;
+ snapshotIndex < 4; ++snapshotIndex) {
+ native_handle_close((*nh)[deltaIndex][snapshotIndex]);
+ native_handle_delete((*nh)[deltaIndex][snapshotIndex]);
+ (*nh)[deltaIndex][snapshotIndex] = nullptr;
+ }
+ }
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+inline bool convertTo(
+ ::android::FrameEventHistoryDelta* l,
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+
+ size_t const baseSize = getFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = getFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (flatten(t, &nhAA, buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ for (auto nhA : nhAA) {
+ for (auto nh : nhA) {
+ if (nh != nullptr) {
+ native_handle_close(nh);
+ native_handle_delete(nh);
+ }
+ }
+ }
+ return false;
+ }
+
+ for (auto nhA : nhAA) {
+ for (auto nh : nhA) {
+ if (nh != nullptr) {
+ native_handle_delete(nh);
+ }
+ }
+ }
+ return true;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+// IGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `IGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `IGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// IGraphicBufferProducer::QueueBufferOutput
+inline bool convertTo(
+ BGraphicBufferProducer::QueueBufferOutput* l,
+ HGraphicBufferProducer::QueueBufferOutput const& t) {
+ if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
+ return false;
+ }
+ l->width = t.width;
+ l->height = t.height;
+ l->transformHint = t.transformHint;
+ l->numPendingBuffers = t.numPendingBuffers;
+ l->nextFrameNumber = t.nextFrameNumber;
+ l->bufferReplaced = t.bufferReplaced;
+ return true;
+}
+
+/**
+ * \brief Convert `IGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `IGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode(
+ BGraphicBufferProducer::DisconnectMode l) {
+ switch (l) {
+ case BGraphicBufferProducer::DisconnectMode::Api:
+ return HGraphicBufferProducer::DisconnectMode::API;
+ case BGraphicBufferProducer::DisconnectMode::AllLocal:
+ return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
+ }
+ return HGraphicBufferProducer::DisconnectMode::API;
+}
+
+// H2BGraphicBufferProducer
+
+status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+ *buf = new GraphicBuffer();
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->requestBuffer(
+ static_cast<int32_t>(slot),
+ [&fnStatus, &buf] (Status status, AnwBuffer const& buffer) {
+ fnStatus = toStatusT(status);
+ if (!convertTo(buf->get(), buffer)) {
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount(
+ int maxDequeuedBuffers) {
+ return toStatusT(mBase->setMaxDequeuedBufferCount(
+ static_cast<int32_t>(maxDequeuedBuffers)));
+}
+
+status_t H2BGraphicBufferProducer::setAsyncMode(bool async) {
+ return toStatusT(mBase->setAsyncMode(async));
+}
+
+status_t H2BGraphicBufferProducer::dequeueBuffer(
+ int* slot, sp<Fence>* fence,
+ uint32_t w, uint32_t h, ::android::PixelFormat format,
+ uint32_t usage, FrameEventHistoryDelta* outTimestamps) {
+ *fence = new Fence();
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->dequeueBuffer(
+ w, h, static_cast<PixelFormat>(format), usage,
+ outTimestamps != nullptr,
+ [&fnStatus, slot, fence, outTimestamps] (
+ Status status,
+ int32_t tSlot,
+ hidl_handle const& tFence,
+ HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
+ fnStatus = toStatusT(status);
+ *slot = tSlot;
+ if (!convertTo(fence->get(), tFence)) {
+ ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+ "Invalid output fence");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ if (outTimestamps && !convertTo(outTimestamps, tTs)) {
+ ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+ "Invalid output timestamps");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::detachBuffer(int slot) {
+ return toStatusT(mBase->detachBuffer(static_cast<int>(slot)));
+}
+
+status_t H2BGraphicBufferProducer::detachNextBuffer(
+ sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+ *outBuffer = new GraphicBuffer();
+ *outFence = new Fence();
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->detachNextBuffer(
+ [&fnStatus, outBuffer, outFence] (
+ Status status,
+ AnwBuffer const& tBuffer,
+ hidl_handle const& tFence) {
+ fnStatus = toStatusT(status);
+ if (!convertTo(outFence->get(), tFence)) {
+ ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+ "Invalid output fence");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ if (!convertTo(outBuffer->get(), tBuffer)) {
+ ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+ "Invalid output buffer");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::attachBuffer(
+ int* outSlot, const sp<GraphicBuffer>& buffer) {
+ AnwBuffer tBuffer;
+ wrapAs(&tBuffer, *buffer);
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->attachBuffer(tBuffer,
+ [&fnStatus, outSlot] (Status status, int32_t slot) {
+ fnStatus = toStatusT(status);
+ *outSlot = slot;
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::queueBuffer(
+ int slot,
+ const QueueBufferInput& input,
+ QueueBufferOutput* output) {
+ HGraphicBufferProducer::QueueBufferInput tInput;
+ native_handle_t* nh;
+ if (!wrapAs(&tInput, &nh, input)) {
+ ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+ "Invalid input");
+ return BAD_VALUE;
+ }
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->queueBuffer(slot, tInput,
+ [&fnStatus, output] (
+ Status status,
+ HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+ fnStatus = toStatusT(status);
+ if (!convertTo(output, tOutput)) {
+ ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+ "Invalid output");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ native_handle_delete(nh);
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+ hidl_handle tFence;
+ native_handle_t* nh = nullptr;
+ if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) {
+ ALOGE("H2BGraphicBufferProducer::cancelBuffer - "
+ "Invalid input fence");
+ return BAD_VALUE;
+ }
+
+ status_t status = toStatusT(mBase->cancelBuffer(
+ static_cast<int32_t>(slot), tFence));
+ native_handle_delete(nh);
+ return status;
+}
+
+int H2BGraphicBufferProducer::query(int what, int* value) {
+ int result;
+ status_t transStatus = toStatusT(mBase->query(
+ static_cast<int32_t>(what),
+ [&result, value] (int32_t tResult, int32_t tValue) {
+ result = static_cast<int>(tResult);
+ *value = static_cast<int>(tValue);
+ }));
+ return transStatus == NO_ERROR ? result : static_cast<int>(transStatus);
+}
+
+status_t H2BGraphicBufferProducer::connect(
+ const sp<IProducerListener>& listener, int api,
+ bool producerControlledByApp, QueueBufferOutput* output) {
+ sp<HProducerListener> tListener = listener == nullptr ?
+ nullptr : new B2HProducerListener(listener);
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->connect(
+ tListener, static_cast<int32_t>(api), producerControlledByApp,
+ [&fnStatus, output] (
+ Status status,
+ HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+ fnStatus = toStatusT(status);
+ if (!convertTo(output, tOutput)) {
+ ALOGE("H2BGraphicBufferProducer::connect - "
+ "Invalid output");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) {
+ return toStatusT(mBase->disconnect(
+ static_cast<int32_t>(api), toHDisconnectMode(mode)));
+}
+
+status_t H2BGraphicBufferProducer::setSidebandStream(
+ const sp<NativeHandle>& stream) {
+ return toStatusT(mBase->setSidebandStream(stream == nullptr ? nullptr : stream->handle()));
+}
+
+void H2BGraphicBufferProducer::allocateBuffers(uint32_t width, uint32_t height,
+ ::android::PixelFormat format, uint32_t usage) {
+ mBase->allocateBuffers(
+ width, height, static_cast<PixelFormat>(format), usage);
+}
+
+status_t H2BGraphicBufferProducer::allowAllocation(bool allow) {
+ return toStatusT(mBase->allowAllocation(allow));
+}
+
+status_t H2BGraphicBufferProducer::setGenerationNumber(uint32_t generationNumber) {
+ return toStatusT(mBase->setGenerationNumber(generationNumber));
+}
+
+String8 H2BGraphicBufferProducer::getConsumerName() const {
+ String8 lName;
+ mBase->getConsumerName([&lName] (hidl_string const& name) {
+ lName = name.c_str();
+ });
+ return lName;
+}
+
+status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) {
+ return toStatusT(mBase->setSharedBufferMode(sharedBufferMode));
+}
+
+status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) {
+ return toStatusT(mBase->setAutoRefresh(autoRefresh));
+}
+
+status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) {
+ return toStatusT(mBase->setDequeueTimeout(static_cast<int64_t>(timeout)));
+}
+
+status_t H2BGraphicBufferProducer::getLastQueuedBuffer(
+ sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence,
+ float outTransformMatrix[16]) {
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->getLastQueuedBuffer(
+ [&fnStatus, outBuffer, outFence, &outTransformMatrix] (
+ Status status,
+ AnwBuffer const& buffer,
+ hidl_handle const& fence,
+ hidl_array<float, 16> const& transformMatrix) {
+ fnStatus = toStatusT(status);
+ *outBuffer = new GraphicBuffer();
+ if (!convertTo(outBuffer->get(), buffer)) {
+ ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+ "Invalid output buffer");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ *outFence = new Fence();
+ if (!convertTo(outFence->get(), fence)) {
+ ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+ "Invalid output fence");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ std::copy(transformMatrix.data(),
+ transformMatrix.data() + 16,
+ outTransformMatrix);
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ mBase->getFrameTimestamps([outDelta] (
+ HGraphicBufferProducer::FrameEventHistoryDelta const& tDelta) {
+ convertTo(outDelta, tDelta);
+ });
+}
+
+status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const {
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->getUniqueId(
+ [&fnStatus, outId] (Status status, uint64_t id) {
+ fnStatus = toStatusT(status);
+ *outId = id;
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+} // namespace utils
+} // namespace V1_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/libs/gui/include/private/gui/BitTube.h b/libs/gui/include/private/gui/BitTube.h
new file mode 100644
index 0000000..13c0162
--- /dev/null
+++ b/libs/gui/include/private/gui/BitTube.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcelable.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class Parcel;
+
+namespace gui {
+
+class BitTube : public Parcelable {
+public:
+ // creates an uninitialized BitTube (to unparcel into)
+ BitTube() = default;
+
+ // creates a BitTube with a a specified send and receive buffer size
+ explicit BitTube(size_t bufsize);
+
+ // creates a BitTube with a default (4KB) send buffer
+ struct DefaultSizeType {};
+ static constexpr DefaultSizeType DefaultSize{};
+ explicit BitTube(DefaultSizeType);
+
+ explicit BitTube(const Parcel& data);
+
+ virtual ~BitTube() = default;
+
+ // check state after construction
+ status_t initCheck() const;
+
+ // get receive file-descriptor
+ int getFd() const;
+
+ // get the send file-descriptor.
+ int getSendFd() const;
+
+ // moves the receive file descriptor out of this BitTube
+ base::unique_fd moveReceiveFd();
+
+ // resets this BitTube's receive file descriptor to receiveFd
+ void setReceiveFd(base::unique_fd&& receiveFd);
+
+ // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
+ template <typename T>
+ static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) {
+ return sendObjects(tube, events, count, sizeof(T));
+ }
+
+ // receive objects (sized blobs). If the receiving buffer isn't large enough, excess messages
+ // are silently discarded.
+ template <typename T>
+ static ssize_t recvObjects(BitTube* tube, T* events, size_t count) {
+ return recvObjects(tube, events, count, sizeof(T));
+ }
+
+ // implement the Parcelable protocol. Only parcels the receive file descriptor
+ status_t writeToParcel(Parcel* reply) const;
+ status_t readFromParcel(const Parcel* parcel);
+
+private:
+ void init(size_t rcvbuf, size_t sndbuf);
+
+ // send a message. The write is guaranteed to send the whole message or fail.
+ ssize_t write(void const* vaddr, size_t size);
+
+ // receive a message. the passed buffer must be at least as large as the write call used to send
+ // the message, excess data is silently discarded.
+ ssize_t read(void* vaddr, size_t size);
+
+ base::unique_fd mSendFd;
+ mutable base::unique_fd mReceiveFd;
+
+ static ssize_t sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize);
+
+ static ssize_t recvObjects(BitTube* tube, void* events, size_t count, size_t objSize);
+};
+
+} // namespace gui
+} // namespace android
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
new file mode 100644
index 0000000..7efdb14
--- /dev/null
+++ b/libs/gui/tests/Android.bp
@@ -0,0 +1,46 @@
+// Build the unit tests,
+
+// Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+// to integrate with auto-test framework.
+cc_test {
+ name: "libgui_test",
+
+ clang: true,
+
+ srcs: [
+ "BufferItemConsumer_test.cpp",
+ "BufferQueue_test.cpp",
+ "CpuConsumer_test.cpp",
+ "FillBuffer.cpp",
+ "GLTest.cpp",
+ "IGraphicBufferProducer_test.cpp",
+ "Malicious.cpp",
+ "MultiTextureConsumer_test.cpp",
+ "StreamSplitter_test.cpp",
+ "SurfaceTextureClient_test.cpp",
+ "SurfaceTextureFBO_test.cpp",
+ "SurfaceTextureGLThreadToGL_test.cpp",
+ "SurfaceTextureGLToGL_test.cpp",
+ "SurfaceTextureGL_test.cpp",
+ "SurfaceTextureMultiContextGL_test.cpp",
+ "Surface_test.cpp",
+ "TextureRenderer.cpp",
+ ],
+
+ shared_libs: [
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore-utils",
+ "liblog",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libbinder",
+ "libcutils",
+ "libgui",
+ "libhidlbase",
+ "libhidltransport",
+ "libui",
+ "libutils",
+ "libnativewindow"
+ ],
+}
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
deleted file mode 100644
index 6ad9986..0000000
--- a/libs/gui/tests/Android.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-# Build the unit tests,
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_CLANG := true
-
-LOCAL_MODULE := libgui_test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := \
- BufferQueue_test.cpp \
- CpuConsumer_test.cpp \
- FillBuffer.cpp \
- GLTest.cpp \
- IGraphicBufferProducer_test.cpp \
- MultiTextureConsumer_test.cpp \
- SRGB_test.cpp \
- StreamSplitter_test.cpp \
- SurfaceTextureClient_test.cpp \
- SurfaceTextureFBO_test.cpp \
- SurfaceTextureGLThreadToGL_test.cpp \
- SurfaceTextureGLToGL_test.cpp \
- SurfaceTextureGL_test.cpp \
- SurfaceTextureMultiContextGL_test.cpp \
- Surface_test.cpp \
- TextureRenderer.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- libEGL \
- libGLESv1_CM \
- libGLESv2 \
- libbinder \
- libcutils \
- libgui \
- libsync \
- libui \
- libutils \
-
-# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
-# to integrate with auto-test framework.
-include $(BUILD_NATIVE_TEST)
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
new file mode 100644
index 0000000..d64e530
--- /dev/null
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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 LOG_TAG "BufferItemConsumer_test"
+//#define LOG_NDEBUG 0
+
+#include <gtest/gtest.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+
+namespace android {
+
+static constexpr int kWidth = 100;
+static constexpr int kHeight = 100;
+static constexpr int kMaxLockedBuffers = 3;
+static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+static constexpr int kFrameSleepUs = 30 * 1000;
+
+class BufferItemConsumerTest : public ::testing::Test {
+ protected:
+ struct BufferFreedListener
+ : public BufferItemConsumer::BufferFreedListener {
+ explicit BufferFreedListener(BufferItemConsumerTest* test)
+ : mTest(test) {}
+ void onBufferFreed(const wp<GraphicBuffer>& /* gBuffer */) override {
+ mTest->HandleBufferFreed();
+ }
+ BufferItemConsumerTest* mTest;
+ };
+
+ void SetUp() override {
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mBIC =
+ new BufferItemConsumer(mConsumer, kFormat, kMaxLockedBuffers, true);
+ String8 name("BufferItemConsumer_Under_Test");
+ mBIC->setName(name);
+ mBFL = new BufferFreedListener(this);
+ mBIC->setBufferFreedListener(mBFL);
+
+ sp<IProducerListener> producerListener = new DummyProducerListener();
+ IGraphicBufferProducer::QueueBufferOutput bufferOutput;
+ ASSERT_EQ(NO_ERROR,
+ mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
+ true, &bufferOutput));
+ ASSERT_EQ(NO_ERROR,
+ mProducer->setMaxDequeuedBufferCount(kMaxLockedBuffers));
+ }
+
+ int GetFreedBufferCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mFreedBufferCount;
+ }
+
+ void HandleBufferFreed() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFreedBufferCount++;
+ ALOGV("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount);
+ }
+
+ void DequeueBuffer(int* outSlot) {
+ ASSERT_NE(outSlot, nullptr);
+
+ int slot;
+ sp<Fence> outFence;
+ status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth,
+ kHeight, 0, 0, nullptr);
+ ASSERT_GE(ret, 0);
+
+ ALOGV("dequeueBuffer: slot=%d", slot);
+ if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ ret = mProducer->requestBuffer(slot, &mBuffers[slot]);
+ ASSERT_EQ(NO_ERROR, ret);
+ }
+ *outSlot = slot;
+ }
+
+ void QueueBuffer(int slot) {
+ ALOGV("enqueueBuffer: slot=%d", slot);
+ IGraphicBufferProducer::QueueBufferInput bufferInput(
+ 0ULL, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ IGraphicBufferProducer::QueueBufferOutput bufferOutput;
+ status_t ret = mProducer->queueBuffer(slot, bufferInput, &bufferOutput);
+ ASSERT_EQ(NO_ERROR, ret);
+ }
+
+ void AcquireBuffer(int* outSlot) {
+ ASSERT_NE(outSlot, nullptr);
+ BufferItem buffer;
+ status_t ret = mBIC->acquireBuffer(&buffer, 0, false);
+ ASSERT_EQ(NO_ERROR, ret);
+
+ ALOGV("acquireBuffer: slot=%d", buffer.mSlot);
+ *outSlot = buffer.mSlot;
+ }
+
+ void ReleaseBuffer(int slot) {
+ ALOGV("releaseBuffer: slot=%d", slot);
+ BufferItem buffer;
+ buffer.mSlot = slot;
+ buffer.mGraphicBuffer = mBuffers[slot];
+ status_t ret = mBIC->releaseBuffer(buffer, Fence::NO_FENCE);
+ ASSERT_EQ(NO_ERROR, ret);
+ }
+
+
+ std::mutex mMutex;
+ int mFreedBufferCount{0};
+
+ sp<BufferItemConsumer> mBIC;
+ sp<BufferFreedListener> mBFL;
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+// Test that detaching buffer from consumer side triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) {
+ int slot;
+ // Producer: generate a dummy buffer.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+
+ ASSERT_EQ(0, GetFreedBufferCount());
+ // Consumer: acquire the buffer and then detach it.
+ AcquireBuffer(&slot);
+ status_t ret = mBIC->detachBuffer(slot);
+ ASSERT_EQ(NO_ERROR, ret);
+
+ // Sleep to give some time for callbacks to happen.
+ usleep(kFrameSleepUs);
+ ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that detaching buffer from producer side triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromProducer) {
+ int slot;
+ // Let buffer go through the cycle at least once.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+ ReleaseBuffer(slot);
+
+ ASSERT_EQ(0, GetFreedBufferCount());
+
+ // Producer: generate the buffer again.
+ DequeueBuffer(&slot);
+
+ // Producer: detach the buffer.
+ status_t ret = mProducer->detachBuffer(slot);
+ ASSERT_EQ(NO_ERROR, ret);
+
+ // Sleep to give some time for callbacks to happen.
+ usleep(kFrameSleepUs);
+ ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that abandoning BufferItemConsumer triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_AbandonBufferItemConsumer) {
+ int slot;
+ // Let buffer go through the cycle at least once.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+ ReleaseBuffer(slot);
+
+ // Abandon the BufferItemConsumer.
+ mBIC->abandon();
+
+ // Sleep to give some time for callbacks to happen.
+ usleep(kFrameSleepUs);
+ ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that delete BufferItemConsumer triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) {
+ int slot;
+ // Let buffer go through the cycle at least once.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+ ReleaseBuffer(slot);
+
+ // Delete the BufferItemConsumer.
+ mBIC.clear();
+
+ // Sleep to give some time for callbacks to happen.
+ usleep(kFrameSleepUs);
+ ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+} // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 8a9eeee..60c1277 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -98,7 +98,11 @@
// XXX: Tests that fork a process to hold the BufferQueue must run before tests
// that use a local BufferQueue, or else Binder will get unhappy
-TEST_F(BufferQueueTest, BufferQueueInAnotherProcess) {
+//
+// In one instance this was a crash in the createBufferQueue where the
+// binder call to create a buffer allocator apparently got garbage back.
+// See b/36592665.
+TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) {
const String16 PRODUCER_NAME = String16("BQTestProducer");
const String16 CONSUMER_NAME = String16("BQTestConsumer");
@@ -139,7 +143,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -183,7 +187,7 @@
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -191,7 +195,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
@@ -234,7 +238,7 @@
for (int i = 0; i < 3; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -270,7 +274,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -280,7 +284,7 @@
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -330,7 +334,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -379,7 +383,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0, false,
HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
@@ -415,7 +419,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataOut;
@@ -438,7 +442,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -487,13 +491,13 @@
// This should return an error since it would require an allocation
ASSERT_EQ(OK, mProducer->allowAllocation(false));
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, 0, 0,
- 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+ 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
// This should succeed, now that we've lifted the prohibition
ASSERT_EQ(OK, mProducer->allowAllocation(true));
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
// Release the previous buffer back to the BufferQueue
mProducer->cancelBuffer(slot, fence);
@@ -501,7 +505,7 @@
// This should fail since we're requesting a different size
ASSERT_EQ(OK, mProducer->allowAllocation(false));
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence,
- WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+ WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(BufferQueueTest, TestGenerationNumbers) {
@@ -518,7 +522,7 @@
int slot;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
@@ -561,7 +565,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -575,7 +579,8 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -612,7 +617,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -639,7 +644,8 @@
// always return the same one.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -678,7 +684,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Enable shared buffer mode
@@ -695,7 +701,8 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -730,7 +737,8 @@
for (int i = 0; i < 5; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0);
+ auto result = mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr);
if (i < 2) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
result);
@@ -757,7 +765,8 @@
for (int i = 0; i < 2; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0ull, true,
HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
@@ -768,7 +777,8 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
auto startTime = systemTime();
- ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_GE(systemTime() - startTime, TIMEOUT);
// We're technically attaching the same buffer multiple times (since we
@@ -789,7 +799,7 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> sourceFence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -812,7 +822,7 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> firstBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
@@ -824,7 +834,7 @@
// Dequeue a second buffer
slot = BufferQueue::INVALID_BUFFER_SLOT;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> secondBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
@@ -876,7 +886,7 @@
mProducer->setMaxDequeuedBufferCount(3);
for (size_t i = 0; i < 3; ++i) {
status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -889,7 +899,8 @@
// The first segment is a two-buffer segment, so we only put one buffer into
// the queue at a time
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -904,16 +915,17 @@
// two-buffer segment, but then at the end, we put two buffers in the queue
// at the same time before draining it.
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -928,10 +940,11 @@
// The third segment is a triple-buffer segment, so the queue is switching
// between one buffer and two buffers deep.
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -1012,7 +1025,7 @@
mProducer->setMaxDequeuedBufferCount(4);
for (size_t i = 0; i < 4; ++i) {
status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -1023,14 +1036,14 @@
// Get buffers in all states: dequeued, filled, acquired, free
// Fill 3 buffers
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Dequeue 1 buffer
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
// Acquire and free 1 buffer
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -1044,7 +1057,7 @@
// Check no free buffers in dump
String8 dumpString;
- mConsumer->dump(dumpString, nullptr);
+ mConsumer->dumpState(String8{}, &dumpString);
// Parse the dump to ensure that all buffer slots that are FREE also
// have a null GraphicBuffer
@@ -1067,4 +1080,122 @@
}
}
+TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ BufferItem item{};
+
+ // Preallocate, dequeue, request, and cancel 2 buffers so we don't get
+ // BUFFER_NEEDS_REALLOCATION below
+ int slots[2] = {};
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
+ for (size_t i = 0; i < 2; ++i) {
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+ 0, 0, 0, 0, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+ }
+ for (size_t i = 0; i < 2; ++i) {
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+ }
+
+ // Fill 2 buffers without consumer consuming them. Verify that all
+ // queued buffer returns proper bufferReplaced flag
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(false, output.bufferReplaced);
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(true, output.bufferReplaced);
+}
+
+TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<IProducerListener> dummyListener(new DummyProducerListener);
+ ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU,
+ true, &output));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+
+ // Dequeue, request, and queue one buffer
+ status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0,
+ nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Acquire and release the buffer. Upon acquiring, the buffer handle should
+ // be non-null since this is the first time we've acquired this slot.
+ BufferItem item;
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_NE(nullptr, item.mGraphicBuffer.get());
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Dequeue and queue the buffer again
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Acquire and release the buffer again. Upon acquiring, the buffer handle
+ // should be null since this is not the first time we've acquired this slot.
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_EQ(nullptr, item.mGraphicBuffer.get());
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Dequeue and queue the buffer again
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Disconnect the producer end. This should clear all of the slots and mark
+ // the buffer in the queue as stale.
+ ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+
+ // Acquire the buffer again. Upon acquiring, the buffer handle should not be
+ // null since the queued buffer should have been marked as stale, which
+ // should trigger the BufferQueue to resend the buffer handle.
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_NE(nullptr, item.mGraphicBuffer.get());
+}
+
+TEST_F(BufferQueueTest, TestProducerConnectDisconnect) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<IProducerListener> dummyListener(new DummyProducerListener);
+ ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(OK, mProducer->connect(
+ dummyListener, NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(BAD_VALUE, mProducer->connect(
+ dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output));
+
+ ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA));
+ ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
} // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 289cc74..5848c74 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -160,7 +160,7 @@
};
#define ASSERT_NO_ERROR(err, msg) \
- ASSERT_EQ(NO_ERROR, err) << msg << strerror(-err)
+ ASSERT_EQ(NO_ERROR, err) << (msg) << strerror(-(err))
void checkPixel(const CpuConsumer::LockedBuffer &buf,
uint32_t x, uint32_t y, uint32_t r, uint32_t g=0, uint32_t b=0) {
@@ -490,7 +490,7 @@
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
*stride = buf->getStride();
uint8_t* img = NULL;
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/tests/DummyConsumer.h
index 0511e16..502bdf9 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/tests/DummyConsumer.h
@@ -19,9 +19,9 @@
namespace android {
struct DummyConsumer : public BnConsumerListener {
- virtual void onFrameAvailable(const BufferItem& /* item */) {}
- virtual void onBuffersReleased() {}
- virtual void onSidebandStreamChanged() {}
+ void onFrameAvailable(const BufferItem& /* item */) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
};
} // namespace android
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp
index 079962c..ccd674f 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -95,7 +95,7 @@
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
uint8_t* img = NULL;
ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 9f33047..aa071f6 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "IGraphicBufferProducer_test"
//#define LOG_NDEBUG 0
+#include "DummyConsumer.h"
+
#include <gtest/gtest.h>
#include <utils/String8.h>
@@ -64,12 +66,6 @@
const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
}; // namespace anonymous
-struct DummyConsumer : public BnConsumerListener {
- virtual void onFrameAvailable(const BufferItem& /* item */) {}
- virtual void onBuffersReleased() {}
- virtual void onSidebandStreamChanged() {}
-};
-
class IGraphicBufferProducerTest : public ::testing::Test {
protected:
@@ -196,7 +192,7 @@
};
status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
- return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage);
+ return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr);
}
void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -210,7 +206,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)));
EXPECT_LE(0, *slot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot);
@@ -349,7 +345,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
EXPECT_LE(0, dequeuedSlot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
@@ -366,20 +362,12 @@
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
{
- uint32_t width;
- uint32_t height;
- uint32_t transformHint;
- uint32_t numPendingBuffers;
- uint64_t nextFrameNumber;
-
- output.deflate(&width, &height, &transformHint, &numPendingBuffers,
- &nextFrameNumber);
-
- EXPECT_EQ(DEFAULT_WIDTH, width);
- EXPECT_EQ(DEFAULT_HEIGHT, height);
- EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
- EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
- EXPECT_EQ(2u, nextFrameNumber);
+ EXPECT_EQ(DEFAULT_WIDTH, output.width);
+ EXPECT_EQ(DEFAULT_HEIGHT, output.height);
+ EXPECT_EQ(DEFAULT_TRANSFORM_HINT, output.transformHint);
+ // Since queueBuffer was called exactly once
+ EXPECT_EQ(1u, output.numPendingBuffers);
+ EXPECT_EQ(2u, output.nextFrameNumber);
}
// Buffer was not in the dequeued state
@@ -416,7 +404,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
// Slot was enqueued without requesting a buffer
{
@@ -485,7 +473,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
// No return code, but at least test that it doesn't blow up...
// TODO: add a return code
@@ -534,7 +522,7 @@
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)))
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
<< "iteration: " << i << ", slot: " << dequeuedSlot;
}
@@ -571,7 +559,7 @@
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)))
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
<< "slot: " << dequeuedSlot;
}
@@ -606,7 +594,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS))) << "slot : " << dequeuedSlot;
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
+ << "slot : " << dequeuedSlot;
ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
}
@@ -622,7 +611,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS))) << "slot: " << dequeuedSlot;
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
+ << "slot: " << dequeuedSlot;
}
// Abandon buffer queue
@@ -639,7 +629,7 @@
sp<Fence> fence;
ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr));
}
TEST_F(IGraphicBufferProducerTest,
@@ -659,7 +649,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
+ nullptr)));
EXPECT_LE(0, slot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot);
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
new file mode 100644
index 0000000..7ecf08c
--- /dev/null
+++ b/libs/gui/tests/Malicious.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferQueue.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+
+#include <android/native_window.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace test {
+
+class ProxyBQP : public BnGraphicBufferProducer {
+public:
+ ProxyBQP(const sp<IGraphicBufferProducer>& producer) : mProducer(producer) {}
+
+ // Pass through calls to mProducer
+ status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override {
+ return mProducer->requestBuffer(slot, buf);
+ }
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
+ return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+ }
+ status_t setAsyncMode(bool async) override { return mProducer->setAsyncMode(async); }
+ status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t usage, FrameEventHistoryDelta* outTimestamps) override {
+ return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outTimestamps);
+ }
+ status_t detachBuffer(int slot) override { return mProducer->detachBuffer(slot); }
+ status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
+ return mProducer->detachNextBuffer(outBuffer, outFence);
+ }
+ status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) override {
+ return mProducer->attachBuffer(outSlot, buffer);
+ }
+ status_t queueBuffer(int slot, const QueueBufferInput& input,
+ QueueBufferOutput* output) override {
+ return mProducer->queueBuffer(slot, input, output);
+ }
+ status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
+ return mProducer->cancelBuffer(slot, fence);
+ }
+ int query(int what, int* value) override { return mProducer->query(what, value); }
+ status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
+ QueueBufferOutput* output) override {
+ return mProducer->connect(listener, api, producerControlledByApp, output);
+ }
+ status_t disconnect(int api, DisconnectMode mode) override {
+ return mProducer->disconnect(api, mode);
+ }
+ status_t setSidebandStream(const sp<NativeHandle>& stream) override {
+ return mProducer->setSidebandStream(stream);
+ }
+ void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t usage) override {
+ mProducer->allocateBuffers(width, height, format, usage);
+ }
+ status_t allowAllocation(bool allow) override { return mProducer->allowAllocation(allow); }
+ status_t setGenerationNumber(uint32_t generationNumber) override {
+ return mProducer->setGenerationNumber(generationNumber);
+ }
+ String8 getConsumerName() const override { return mProducer->getConsumerName(); }
+ status_t setSharedBufferMode(bool sharedBufferMode) override {
+ return mProducer->setSharedBufferMode(sharedBufferMode);
+ }
+ status_t setAutoRefresh(bool autoRefresh) override {
+ return mProducer->setAutoRefresh(autoRefresh);
+ }
+ status_t setDequeueTimeout(nsecs_t timeout) override {
+ return mProducer->setDequeueTimeout(timeout);
+ }
+ status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+ float outTransformMatrix[16]) override {
+ return mProducer->getLastQueuedBuffer(outBuffer, outFence, outTransformMatrix);
+ }
+ void getFrameTimestamps(FrameEventHistoryDelta*) override {}
+ status_t getUniqueId(uint64_t* outId) const override { return mProducer->getUniqueId(outId); }
+
+protected:
+ sp<IGraphicBufferProducer> mProducer;
+};
+
+class MaliciousBQP : public ProxyBQP {
+public:
+ MaliciousBQP(const sp<IGraphicBufferProducer>& producer) : ProxyBQP(producer) {}
+
+ void beMalicious(int32_t value) { mMaliciousValue = value; }
+
+ void setExpectedSlot(int32_t slot) { mExpectedSlot = slot; }
+
+ // Override dequeueBuffer, optionally corrupting the returned slot number
+ status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) override {
+ EXPECT_EQ(BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(buf, fence, width, height, format, usage,
+ outTimestamps));
+ EXPECT_EQ(mExpectedSlot, *buf);
+ if (mMaliciousValue != 0) {
+ *buf = mMaliciousValue;
+ return NO_ERROR;
+ } else {
+ return BUFFER_NEEDS_REALLOCATION;
+ }
+ }
+
+private:
+ int32_t mMaliciousValue = 0;
+ int32_t mExpectedSlot = 0;
+};
+
+class DummyListener : public BnConsumerListener {
+public:
+ void onFrameAvailable(const BufferItem&) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+};
+
+sp<MaliciousBQP> getMaliciousBQP() {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ sp<IConsumerListener> listener = new DummyListener;
+ consumer->consumerConnect(listener, false);
+
+ sp<MaliciousBQP> malicious = new MaliciousBQP(producer);
+ return malicious;
+}
+
+TEST(Malicious, Bug36991414Max) {
+ sp<MaliciousBQP> malicious = getMaliciousBQP();
+ sp<Surface> surface = new Surface(malicious);
+
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+ ANativeWindow_Buffer buffer;
+ ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+ malicious->setExpectedSlot(1);
+ malicious->beMalicious(std::numeric_limits<int32_t>::max());
+ ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414Min) {
+ sp<MaliciousBQP> malicious = getMaliciousBQP();
+ sp<Surface> surface = new Surface(malicious);
+
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+ ANativeWindow_Buffer buffer;
+ ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+ malicious->setExpectedSlot(1);
+ malicious->beMalicious(std::numeric_limits<int32_t>::min());
+ ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414NegativeOne) {
+ sp<MaliciousBQP> malicious = getMaliciousBQP();
+ sp<Surface> surface = new Surface(malicious);
+
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+ ANativeWindow_Buffer buffer;
+ ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+ malicious->setExpectedSlot(1);
+ malicious->beMalicious(-1);
+ ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414NumSlots) {
+ sp<MaliciousBQP> malicious = getMaliciousBQP();
+ sp<Surface> surface = new Surface(malicious);
+
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+ ANativeWindow_Buffer buffer;
+ ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+ malicious->setExpectedSlot(1);
+ malicious->beMalicious(BufferQueueDefs::NUM_BUFFER_SLOTS);
+ ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/gui/tests/SRGB_test.cpp b/libs/gui/tests/SRGB_test.cpp
deleted file mode 100644
index 3b11b97..0000000
--- a/libs/gui/tests/SRGB_test.cpp
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#define LOG_TAG "SRGB_test"
-//#define LOG_NDEBUG 0
-
-// Ignore for this file because it flags every instance of
-// ASSERT_EQ(GL_NO_ERROR, glGetError());
-#pragma clang diagnostic ignored "-Wsign-compare"
-
-#include "GLTest.h"
-
-#include <math.h>
-
-#include <gui/CpuConsumer.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-
-#include <android/native_window.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-
-class SRGBTest : public ::testing::Test {
-protected:
- // Class constants
- enum {
- DISPLAY_WIDTH = 512,
- DISPLAY_HEIGHT = 512,
- PIXEL_SIZE = 4, // bytes or components
- DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT * PIXEL_SIZE,
- ALPHA_VALUE = 223, // should be in [0, 255]
- TOLERANCE = 1,
- };
- static const char SHOW_DEBUG_STRING[];
-
- SRGBTest() :
- mInputSurface(), mCpuConsumer(), mLockedBuffer(),
- mEglDisplay(EGL_NO_DISPLAY), mEglConfig(),
- mEglContext(EGL_NO_CONTEXT), mEglSurface(EGL_NO_SURFACE),
- mComposerClient(), mSurfaceControl(), mOutputSurface() {
- }
-
- virtual ~SRGBTest() {
- if (mEglDisplay != EGL_NO_DISPLAY) {
- if (mEglSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEglDisplay, mEglSurface);
- }
- if (mEglContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEglDisplay, mEglContext);
- }
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT);
- eglTerminate(mEglDisplay);
- }
- }
-
- virtual void SetUp() {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize(
- DISPLAY_WIDTH, DISPLAY_HEIGHT));
- mCpuConsumer = new CpuConsumer(consumer, 1);
- String8 name("CpuConsumer_for_SRGBTest");
- mCpuConsumer->setName(name);
- mInputSurface = new Surface(producer);
-
- ASSERT_NO_FATAL_FAILURE(createEGLSurface(mInputSurface.get()));
- ASSERT_NO_FATAL_FAILURE(createDebugSurface());
- }
-
- virtual void TearDown() {
- ASSERT_NO_FATAL_FAILURE(copyToDebugSurface());
- ASSERT_TRUE(mLockedBuffer.data != NULL);
- ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
- }
-
- static float linearToSRGB(float l) {
- if (l <= 0.0031308f) {
- return l * 12.92f;
- } else {
- return 1.055f * pow(l, (1 / 2.4f)) - 0.055f;
- }
- }
-
- static float srgbToLinear(float s) {
- if (s <= 0.04045) {
- return s / 12.92f;
- } else {
- return pow(((s + 0.055f) / 1.055f), 2.4f);
- }
- }
-
- static uint8_t srgbToLinear(uint8_t u) {
- float f = u / 255.0f;
- return static_cast<uint8_t>(srgbToLinear(f) * 255.0f + 0.5f);
- }
-
- void fillTexture(bool writeAsSRGB) {
- uint8_t* textureData = new uint8_t[DISPLAY_SIZE];
-
- for (int y = 0; y < DISPLAY_HEIGHT; ++y) {
- for (int x = 0; x < DISPLAY_WIDTH; ++x) {
- float realValue = static_cast<float>(x) / (DISPLAY_WIDTH - 1);
- realValue *= ALPHA_VALUE / 255.0f; // Premultiply by alpha
- if (writeAsSRGB) {
- realValue = linearToSRGB(realValue);
- }
-
- int offset = (y * DISPLAY_WIDTH + x) * PIXEL_SIZE;
- for (int c = 0; c < 3; ++c) {
- uint8_t intValue = static_cast<uint8_t>(
- realValue * 255.0f + 0.5f);
- textureData[offset + c] = intValue;
- }
- textureData[offset + 3] = ALPHA_VALUE;
- }
- }
-
- glTexImage2D(GL_TEXTURE_2D, 0, writeAsSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- textureData);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
-
- delete[] textureData;
- }
-
- void initShaders() {
- static const char vertexSource[] =
- "attribute vec4 vPosition;\n"
- "varying vec2 texCoords;\n"
- "void main() {\n"
- " texCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
- " gl_Position = vPosition;\n"
- "}\n";
-
- static const char fragmentSource[] =
- "precision mediump float;\n"
- "uniform sampler2D texSampler;\n"
- "varying vec2 texCoords;\n"
- "void main() {\n"
- " gl_FragColor = texture2D(texSampler, texCoords);\n"
- "}\n";
-
- GLuint program;
- {
- SCOPED_TRACE("Creating shader program");
- ASSERT_NO_FATAL_FAILURE(GLTest::createProgram(
- vertexSource, fragmentSource, &program));
- }
-
- GLint positionHandle = glGetAttribLocation(program, "vPosition");
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- ASSERT_NE(-1, positionHandle);
-
- GLint samplerHandle = glGetUniformLocation(program, "texSampler");
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- ASSERT_NE(-1, samplerHandle);
-
- static const GLfloat vertices[] = {
- -1.0f, 1.0f,
- -1.0f, -1.0f,
- 1.0f, -1.0f,
- 1.0f, 1.0f,
- };
-
- glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glEnableVertexAttribArray(positionHandle);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
-
- glUseProgram(program);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glUniform1i(samplerHandle, 0);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
-
- GLuint textureHandle;
- glGenTextures(1, &textureHandle);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glBindTexture(GL_TEXTURE_2D, textureHandle);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- }
-
- void drawTexture(bool asSRGB, GLint x, GLint y, GLsizei width,
- GLsizei height) {
- ASSERT_NO_FATAL_FAILURE(fillTexture(asSRGB));
- glViewport(x, y, width, height);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- }
-
- void checkLockedBuffer(PixelFormat format, android_dataspace dataSpace) {
- ASSERT_EQ(mLockedBuffer.format, format);
- ASSERT_EQ(mLockedBuffer.width, DISPLAY_WIDTH);
- ASSERT_EQ(mLockedBuffer.height, DISPLAY_HEIGHT);
- ASSERT_EQ(mLockedBuffer.dataSpace, dataSpace);
- }
-
- static bool withinTolerance(int a, int b) {
- int diff = a - b;
- return diff >= 0 ? diff <= TOLERANCE : -diff <= TOLERANCE;
- }
-
- // Primary producer and consumer
- sp<Surface> mInputSurface;
- sp<CpuConsumer> mCpuConsumer;
- CpuConsumer::LockedBuffer mLockedBuffer;
-
- EGLDisplay mEglDisplay;
- EGLConfig mEglConfig;
- EGLContext mEglContext;
- EGLSurface mEglSurface;
-
- // Auxiliary display output
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mSurfaceControl;
- sp<Surface> mOutputSurface;
-
-private:
- void createEGLSurface(Surface* inputSurface) {
- mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
-
- EXPECT_TRUE(eglInitialize(mEglDisplay, NULL, NULL));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- static const EGLint configAttribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_NONE };
-
- EGLint numConfigs = 0;
- EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1,
- &numConfigs));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_GT(numConfigs, 0);
-
- static const EGLint contextAttribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 3,
- EGL_NONE } ;
-
- mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
- contextAttribs);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
-
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
- inputSurface, NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- }
-
- void createDebugSurface() {
- if (getenv(SHOW_DEBUG_STRING) == NULL) return;
-
- mComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- mSurfaceControl = mComposerClient->createSurface(
- String8("SRGBTest Surface"), DISPLAY_WIDTH, DISPLAY_HEIGHT,
- PIXEL_FORMAT_RGBA_8888);
-
- ASSERT_TRUE(mSurfaceControl != NULL);
- ASSERT_TRUE(mSurfaceControl->isValid());
-
- SurfaceComposerClient::openGlobalTransaction();
- ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
- ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
- SurfaceComposerClient::closeGlobalTransaction();
-
- ANativeWindow_Buffer outBuffer;
- ARect inOutDirtyBounds;
- mOutputSurface = mSurfaceControl->getSurface();
- mOutputSurface->lock(&outBuffer, &inOutDirtyBounds);
- uint8_t* bytePointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
- for (int y = 0; y < outBuffer.height; ++y) {
- int rowOffset = y * outBuffer.stride; // pixels
- for (int x = 0; x < outBuffer.width; ++x) {
- int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
- for (int c = 0; c < PIXEL_SIZE; ++c) {
- int offset = colOffset + c;
- bytePointer[offset] = ((c + 1) * 56) - 1;
- }
- }
- }
- mOutputSurface->unlockAndPost();
- }
-
- void copyToDebugSurface() {
- if (!mOutputSurface.get()) return;
-
- size_t bufferSize = mLockedBuffer.height * mLockedBuffer.stride *
- PIXEL_SIZE;
-
- ANativeWindow_Buffer outBuffer;
- ARect outBufferBounds;
- mOutputSurface->lock(&outBuffer, &outBufferBounds);
- ASSERT_EQ(mLockedBuffer.width, static_cast<uint32_t>(outBuffer.width));
- ASSERT_EQ(mLockedBuffer.height, static_cast<uint32_t>(outBuffer.height));
- ASSERT_EQ(mLockedBuffer.stride, static_cast<uint32_t>(outBuffer.stride));
-
- if (mLockedBuffer.format == outBuffer.format) {
- memcpy(outBuffer.bits, mLockedBuffer.data, bufferSize);
- } else {
- ASSERT_EQ(mLockedBuffer.format, PIXEL_FORMAT_RGBA_8888);
- ASSERT_EQ(mLockedBuffer.dataSpace, HAL_DATASPACE_SRGB);
- ASSERT_EQ(outBuffer.format, PIXEL_FORMAT_RGBA_8888);
- uint8_t* outPointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
- for (int y = 0; y < outBuffer.height; ++y) {
- int rowOffset = y * outBuffer.stride; // pixels
- for (int x = 0; x < outBuffer.width; ++x) {
- int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
-
- // RGB are converted
- for (int c = 0; c < (PIXEL_SIZE - 1); ++c) {
- outPointer[colOffset + c] = srgbToLinear(
- mLockedBuffer.data[colOffset + c]);
- }
-
- // Alpha isn't converted
- outPointer[colOffset + 3] =
- mLockedBuffer.data[colOffset + 3];
- }
- }
- }
- mOutputSurface->unlockAndPost();
-
- int sleepSeconds = atoi(getenv(SHOW_DEBUG_STRING));
- sleep(sleepSeconds);
- }
-};
-
-const char SRGBTest::SHOW_DEBUG_STRING[] = "DEBUG_OUTPUT_SECONDS";
-
-TEST_F(SRGBTest, GLRenderFromSRGBTexture) {
- ASSERT_NO_FATAL_FAILURE(initShaders());
-
- // The RGB texture is displayed in the top half
- ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, DISPLAY_HEIGHT / 2,
- DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
-
- // The SRGB texture is displayed in the bottom half
- ASSERT_NO_FATAL_FAILURE(drawTexture(true, 0, 0,
- DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
-
- eglSwapBuffers(mEglDisplay, mEglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Lock
- ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
- ASSERT_NO_FATAL_FAILURE(
- checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
-
- // Compare a pixel in the middle of each texture
- int midSRGBOffset = (DISPLAY_HEIGHT / 4) * mLockedBuffer.stride *
- PIXEL_SIZE;
- int midRGBOffset = midSRGBOffset * 3;
- midRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
- midSRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
- for (int c = 0; c < PIXEL_SIZE; ++c) {
- int expectedValue = mLockedBuffer.data[midRGBOffset + c];
- int actualValue = mLockedBuffer.data[midSRGBOffset + c];
- ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
- }
-
- // mLockedBuffer is unlocked in TearDown so we can copy data from it to
- // the debug surface if necessary
-}
-
-// XXX: Disabled since we don't currently expect this to work
-TEST_F(SRGBTest, DISABLED_RenderToSRGBSurface) {
- ASSERT_NO_FATAL_FAILURE(initShaders());
-
- // By default, the first buffer we write into will be RGB
-
- // Render an RGB texture across the whole surface
- ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
- DISPLAY_WIDTH, DISPLAY_HEIGHT));
- eglSwapBuffers(mEglDisplay, mEglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Lock
- ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
- ASSERT_NO_FATAL_FAILURE(
- checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
-
- // Save the values of the middle pixel for later comparison against SRGB
- uint8_t values[PIXEL_SIZE] = {};
- int middleOffset = (DISPLAY_HEIGHT / 2) * mLockedBuffer.stride *
- PIXEL_SIZE;
- middleOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
- for (int c = 0; c < PIXEL_SIZE; ++c) {
- values[c] = mLockedBuffer.data[middleOffset + c];
- }
-
- // Unlock
- ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
-
- // Switch to SRGB window surface
-#define EGL_GL_COLORSPACE_KHR EGL_VG_COLORSPACE
-#define EGL_GL_COLORSPACE_SRGB_KHR EGL_VG_COLORSPACE_sRGB
-
- static const int srgbAttribs[] = {
- EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
- EGL_NONE,
- };
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
- mInputSurface.get(), srgbAttribs);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Render the texture again
- ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
- DISPLAY_WIDTH, DISPLAY_HEIGHT));
- eglSwapBuffers(mEglDisplay, mEglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Lock
- ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
-
- // Make sure we actually got the SRGB buffer on the consumer side
- ASSERT_NO_FATAL_FAILURE(
- checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_SRGB));
-
- // Verify that the stored value is the same, accounting for RGB/SRGB
- for (int c = 0; c < PIXEL_SIZE; ++c) {
- // The alpha value should be equivalent before linear->SRGB
- float rgbAsSRGB = (c == 3) ? values[c] / 255.0f :
- linearToSRGB(values[c] / 255.0f);
- int expectedValue = rgbAsSRGB * 255.0f + 0.5f;
- int actualValue = mLockedBuffer.data[middleOffset + c];
- ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
- }
-
- // mLockedBuffer is unlocked in TearDown so we can copy data from it to
- // the debug surface if necessary
-}
-
-} // namespace android
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 498492e..80e30da 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -81,7 +81,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -115,7 +115,7 @@
// received the buffer back from the output BufferQueue
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
@@ -153,7 +153,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -190,7 +190,7 @@
// received the buffer back from the output BufferQueues
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(StreamSplitterTest, OutputAbandonment) {
@@ -217,7 +217,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
// Abandon the output
@@ -230,7 +230,7 @@
// Input should be abandoned
ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
} // namespace android
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index a1578f6..bd598e4 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -23,6 +23,7 @@
#include <gtest/gtest.h>
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
+#include <gui/BufferQueue.h>
#include <system/graphics.h>
#include <utils/Log.h>
#include <utils/Thread.h>
@@ -535,7 +536,7 @@
return false;
}
public:
- MyThread(const sp<GLConsumer>& mST)
+ explicit MyThread(const sp<GLConsumer>& mST)
: mST(mST), mBufferRetired(false) {
ctx = eglGetCurrentContext();
sur = eglGetCurrentSurface(EGL_DRAW);
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index 0606839..0134273 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -41,7 +41,7 @@
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with green
uint8_t* img = NULL;
@@ -65,7 +65,7 @@
&anb));
ASSERT_TRUE(anb != NULL);
- buf = new GraphicBuffer(anb, false);
+ buf = GraphicBuffer::from(anb);
// Fill the buffer with red
ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 5311c59..c6745d0 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -42,7 +42,7 @@
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with the a checkerboard pattern
uint8_t* img = NULL;
@@ -92,7 +92,7 @@
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with the a checkerboard pattern
uint8_t* img = NULL;
@@ -157,7 +157,7 @@
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
uint8_t* img = NULL;
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
@@ -238,7 +238,7 @@
return false;
}
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
const int yuvTexOffsetY = 0;
int stride = buf->getStride();
@@ -437,7 +437,7 @@
class ProducerThread : public Thread {
public:
- ProducerThread(const sp<ANativeWindow>& anw):
+ explicit ProducerThread(const sp<ANativeWindow>& anw):
mANW(anw) {
}
@@ -620,7 +620,7 @@
TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
class ProducerThread : public Thread {
public:
- ProducerThread(const sp<ANativeWindow>& anw):
+ explicit ProducerThread(const sp<ANativeWindow>& anw):
mANW(anw),
mDequeueError(NO_ERROR) {
}
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0de60c9..e18af17 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -18,19 +18,38 @@
#include <gtest/gtest.h>
-#include <binder/IMemory.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <binder/ProcessState.h>
+#include <configstore/Utils.h>
+#include <cutils/properties.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IDisplayEventConnection.h>
+#include <gui/IProducerListener.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;
+// retrieve wide-color and hdr settings from configstore
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+static bool hasWideColorDisplay =
+ getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
+class FakeSurfaceComposer;
+class FakeProducerFrameEventHistory;
+
+static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
+
class SurfaceTest : public ::testing::Test {
protected:
@@ -42,6 +61,8 @@
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+ // TODO(brianderson): The following sometimes fails and is a source of
+ // test flakiness.
mSurfaceControl = mComposerClient->createSurface(
String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
@@ -77,6 +98,8 @@
TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) {
mSurfaceControl.clear();
+ // Wait for the async clean-up to complete.
+ std::this_thread::sleep_for(50ms);
sp<ANativeWindow> anw(mSurface);
int result = -123;
@@ -96,7 +119,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));
@@ -140,6 +164,14 @@
EXPECT_EQ(NATIVE_WINDOW_SURFACE, result);
}
+TEST_F(SurfaceTest, LayerCountIsOne) {
+ sp<ANativeWindow> anw(mSurface);
+ int result = -123;
+ int err = anw->query(anw.get(), NATIVE_WINDOW_LAYER_COUNT, &result);
+ EXPECT_EQ(NO_ERROR, err);
+ EXPECT_EQ(1, result);
+}
+
TEST_F(SurfaceTest, QueryConsumerUsage) {
const int TEST_USAGE_FLAGS =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
@@ -232,6 +264,37 @@
EXPECT_STREQ("TestConsumer", surface->getConsumerName().string());
}
+TEST_F(SurfaceTest, GetWideColorSupport) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+ consumer->consumerConnect(dummyConsumer, false);
+ consumer->setConsumerName(String8("TestConsumer"));
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+
+ bool supported;
+ surface->getWideColorSupport(&supported);
+
+ // NOTE: This test assumes that device that supports
+ // wide-color (as indicated by BoardConfig) must also
+ // have a wide-color primary display.
+ // That assumption allows this test to cover devices
+ // that advertised a wide-color color mode without
+ // actually supporting wide-color to pass this test
+ // as well as the case of a device that does support
+ // wide-color (via BoardConfig) and has a wide-color
+ // primary display.
+ // NOT covered at this time is a device that supports
+ // wide color in the BoardConfig but does not support
+ // a wide-color color mode on the primary display.
+ ASSERT_EQ(hasWideColorDisplay, supported);
+}
+
TEST_F(SurfaceTest, DynamicSetBufferCount) {
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
@@ -258,4 +321,1268 @@
ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
}
+TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+ consumer->consumerConnect(dummyConsumer, false);
+ consumer->setConsumerName(String8("TestConsumer"));
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ sp<DummyProducerListener> listener = new DummyProducerListener();
+ ASSERT_EQ(OK, surface->connect(
+ NATIVE_WINDOW_API_CPU,
+ /*listener*/listener,
+ /*reportBufferRemoval*/true));
+ const int BUFFER_COUNT = 4;
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+ sp<GraphicBuffer> detachedBuffer;
+ sp<Fence> outFence;
+ int fences[BUFFER_COUNT];
+ ANativeWindowBuffer* buffers[BUFFER_COUNT];
+ // Allocate buffers because detachNextBuffer requires allocated buffers
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i]));
+ }
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i]));
+ }
+
+ // Test detached buffer is correctly reported
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ std::vector<sp<GraphicBuffer>> removedBuffers;
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ ASSERT_EQ(1u, removedBuffers.size());
+ ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle);
+ // Test the list is flushed one getAndFlushRemovedBuffers returns
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ ASSERT_EQ(0u, removedBuffers.size());
+
+
+ // Test removed buffer list is cleanup after next dequeueBuffer call
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[0], &fences[0]));
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ ASSERT_EQ(0u, removedBuffers.size());
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[0], fences[0]));
+
+ // Test removed buffer list is cleanup after next detachNextBuffer call
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ ASSERT_EQ(1u, removedBuffers.size());
+ ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle);
+
+ // Re-allocate buffers since all buffers are detached up to now
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i]));
+ }
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i]));
+ }
+
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ ASSERT_EQ(NO_ERROR, surface->attachBuffer(detachedBuffer.get()));
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ // Depends on which slot GraphicBufferProducer impl pick, the attach call might
+ // get 0 or 1 buffer removed.
+ ASSERT_LE(removedBuffers.size(), 1u);
}
+
+TEST_F(SurfaceTest, TestGetLastDequeueStartTime) {
+ sp<ANativeWindow> anw(mSurface);
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
+
+ ANativeWindowBuffer* buffer = nullptr;
+ int32_t fenceFd = -1;
+
+ nsecs_t before = systemTime(CLOCK_MONOTONIC);
+ anw->dequeueBuffer(anw.get(), &buffer, &fenceFd);
+ nsecs_t after = systemTime(CLOCK_MONOTONIC);
+
+ nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime();
+ ASSERT_LE(before, lastDequeueTime);
+ ASSERT_GE(after, lastDequeueTime);
+}
+
+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 setSupportsPresent(bool supportsPresent) {
+ mSupportsPresent = supportsPresent;
+ }
+
+ sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
+ sp<ISurfaceComposerClient> createScopedConnection(
+ const sp<IGraphicBufferProducer>& /* parent */) override {
+ return nullptr;
+ }
+ sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource)
+ 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::GPU_COMPOSITION_DONE,
+ FrameEvent::DEQUEUE_READY,
+ FrameEvent::RELEASE
+ };
+ if (mSupportsPresent) {
+ outSupported->push_back(
+ FrameEvent::DISPLAY_PRESENT);
+ }
+ 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*/,
+ int32_t /*minLayerZ*/, int32_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};
+};
+
+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;
+ }
+
+ nsecs_t now() const override {
+ return mNow;
+ }
+
+ void setNow(nsecs_t now) {
+ mNow = now;
+ }
+
+public:
+ sp<FakeSurfaceComposer> mFakeSurfaceComposer;
+ nsecs_t mNow = 0;
+
+ // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory,
+ // but this raw pointer gives access to test functionality.
+ FakeProducerFrameEventHistory* mFakeFrameEventHistory;
+};
+
+
+class GetFrameTimestampsTest : public ::testing::Test {
+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),
+ kCompositorTiming(
+ {refreshStart, refreshStart + 1, refreshStart + 2 }),
+ kStartTime(refreshStart + 3),
+ kGpuCompositionDoneTime(refreshStart + 4),
+ kPresentTime(refreshStart + 5) {}
+
+ void signalPostCompositeFences() {
+ mFenceMap.signalAllForTest(
+ mGpuCompositionDone.mFence, kGpuCompositionDoneTime);
+ mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime);
+ }
+
+ FenceToFenceTimeMap& mFenceMap;
+
+ FenceAndFenceTime mGpuCompositionDone { mFenceMap };
+ FenceAndFenceTime mPresent { mFenceMap };
+
+ const CompositorTiming kCompositorTiming;
+
+ 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),
+ kReleaseTime(frameStartTime + 700),
+ 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(mRelease.mFence, kReleaseTime);
+ }
+
+ FenceToFenceTimeMap& mFenceMap;
+
+ FenceAndFenceTime mAcquireConsumer { mFenceMap };
+ FenceAndFenceTime mAcquireProducer { 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 kReleaseTime;
+
+ RefreshEvents mRefreshes[3];
+ };
+
+ GetFrameTimestampsTest() {}
+
+ virtual void 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 disableFrameTimestamps() {
+ mFakeConsumer->mGetFrameTimestampsEnabled = false;
+ native_window_enable_frame_timestamps(mWindow.get(), 0);
+ mFrameTimestampsEnabled = false;
+ }
+
+ void enableFrameTimestamps() {
+ mFakeConsumer->mGetFrameTimestampsEnabled = true;
+ native_window_enable_frame_timestamps(mWindow.get(), 1);
+ mFrameTimestampsEnabled = true;
+ }
+
+ int getAllFrameTimestamps(uint64_t frameId) {
+ return native_window_get_frame_timestamps(mWindow.get(), frameId,
+ &outRequestedPresentTime, &outAcquireTime, &outLatchTime,
+ &outFirstRefreshStartTime, &outLastRefreshStartTime,
+ &outGpuCompositionDoneTime, &outDisplayPresentTime,
+ &outDequeueReadyTime, &outReleaseTime);
+ }
+
+ void resetTimestamps() {
+ outRequestedPresentTime = -1;
+ outAcquireTime = -1;
+ outLatchTime = -1;
+ outFirstRefreshStartTime = -1;
+ outLastRefreshStartTime = -1;
+ outGpuCompositionDoneTime = -1;
+ outDisplayPresentTime = -1;
+ outDequeueReadyTime = -1;
+ outReleaseTime = -1;
+ }
+
+ uint64_t getNextFrameId() {
+ uint64_t frameId = -1;
+ int status = native_window_get_next_frame_id(mWindow.get(), &frameId);
+ EXPECT_EQ(status, NO_ERROR);
+ return frameId;
+ }
+
+ 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, 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,
+ oldFrame->mRefreshes[2].kCompositorTiming);
+ }
+
+ // 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,
+ newFrame->mRefreshes[0].kCompositorTiming);
+
+ 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,
+ newFrame->mRefreshes[1].kCompositorTiming);
+ }
+
+ 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 outDequeueReadyTime = -1;
+ int64_t outReleaseTime = -1;
+
+ FrameEvents mFrames[3] {
+ { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } };
+};
+
+
+// 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);
+
+ const uint64_t fId = getNextFrameId();
+
+ // 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.
+ int result = getAllFrameTimestamps(fId);
+ EXPECT_EQ(INVALID_OPERATION, result);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify compositor timing query fails.
+ nsecs_t compositeDeadline = 0;
+ nsecs_t compositeInterval = 0;
+ nsecs_t compositeToPresentLatency = 0;
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(INVALID_OPERATION, result);
+}
+
+// This test verifies that the frame timestamps are retrieved if explicitly
+// enabled via native_window_enable_frame_timestamps.
+TEST_F(GetFrameTimestampsTest, EnabledSimple) {
+ CompositorTiming initialCompositorTiming {
+ 1000000000, // 1s deadline
+ 16666667, // 16ms interval
+ 50000000, // 50ms present latency
+ };
+ mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+ enableFrameTimestamps();
+
+ // Verify the compositor timing query gets the initial compositor values
+ // after timststamps are enabled; even before the first frame is queued
+ // or dequeued.
+ nsecs_t compositeDeadline = 0;
+ nsecs_t compositeInterval = 0;
+ nsecs_t compositeToPresentLatency = 0;
+ mSurface->setNow(initialCompositorTiming.deadline - 1);
+ int result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+ EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+ EXPECT_EQ(initialCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ int fence;
+ ANativeWindowBuffer* buffer;
+
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount);
+
+ const uint64_t fId1 = getNextFrameId();
+
+ // Verify getFrameTimestamps is piggybacked on dequeue.
+ ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(2, 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(3, 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.
+ result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentSupported) {
+ bool displayPresentSupported = true;
+ mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported);
+
+ // Verify supported bits are forwarded.
+ int supportsPresent = -1;
+ mWindow.get()->query(mWindow.get(),
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+ EXPECT_EQ(displayPresentSupported, supportsPresent);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentNotSupported) {
+ bool displayPresentSupported = false;
+ mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported);
+
+ // Verify supported bits are forwarded.
+ int supportsPresent = -1;
+ mWindow.get()->query(mWindow.get(),
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+ EXPECT_EQ(displayPresentSupported, supportsPresent);
+}
+
+TEST_F(GetFrameTimestampsTest, SnapToNextTickBasic) {
+ nsecs_t phase = 4000;
+ nsecs_t interval = 1000;
+
+ // Timestamp in previous interval.
+ nsecs_t timestamp = 3500;
+ EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp in next interval.
+ timestamp = 4500;
+ EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp multiple intervals before.
+ timestamp = 2500;
+ EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp multiple intervals after.
+ timestamp = 6500;
+ EXPECT_EQ(7000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp on previous interval.
+ timestamp = 3000;
+ EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp on next interval.
+ timestamp = 5000;
+ EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp equal to phase.
+ timestamp = 4000;
+ EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+}
+
+// int(big_timestamp / interval) < 0, which can cause a crash or invalid result
+// if the number of intervals elapsed is internally stored in an int.
+TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) {
+ nsecs_t phase = 0;
+ nsecs_t interval = 4000;
+ nsecs_t big_timestamp = 8635916564000;
+ int32_t intervals = big_timestamp / interval;
+
+ EXPECT_LT(intervals, 0);
+ EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick(
+ big_timestamp, phase, interval));
+ EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick(
+ big_timestamp, big_timestamp, interval));
+}
+
+// This verifies the compositor timing is updated by refresh events
+// and piggy backed on a queue, dequeue, and enabling of timestamps..
+TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) {
+ CompositorTiming initialCompositorTiming {
+ 1000000000, // 1s deadline
+ 16666667, // 16ms interval
+ 50000000, // 50ms present latency
+ };
+ mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+ enableFrameTimestamps();
+
+ // We get the initial values before any frames are submitted.
+ nsecs_t compositeDeadline = 0;
+ nsecs_t compositeInterval = 0;
+ nsecs_t compositeToPresentLatency = 0;
+ mSurface->setNow(initialCompositorTiming.deadline - 1);
+ int result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+ EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+ EXPECT_EQ(initialCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+
+ // Still get the initial values because the frame events for frame 0
+ // didn't get a chance to piggyback on a queue or dequeue yet.
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+ EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+ EXPECT_EQ(initialCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ const uint64_t fId2 = getNextFrameId();
+ dequeueAndQueue(1);
+ addFrameEvents(true, 0, 1);
+
+ // Now expect the composite values associated with frame 1.
+ mSurface->setNow(mFrames[0].mRefreshes[1].kCompositorTiming.deadline);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.deadline,
+ compositeDeadline);
+ EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.interval,
+ compositeInterval);
+ EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ dequeueAndQueue(2);
+ addFrameEvents(true, 1, 2);
+
+ // Now expect the composite values associated with frame 2.
+ mSurface->setNow(mFrames[1].mRefreshes[1].kCompositorTiming.deadline);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.deadline,
+ compositeDeadline);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.interval,
+ compositeInterval);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ // Re-enabling frame timestamps should get the latest values.
+ disableFrameTimestamps();
+ enableFrameTimestamps();
+
+ // Now expect the composite values associated with frame 3.
+ mSurface->setNow(mFrames[2].mRefreshes[1].kCompositorTiming.deadline);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.deadline,
+ compositeDeadline);
+ EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.interval,
+ compositeInterval);
+ EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+}
+
+// This verifies the compositor deadline properly snaps to the the next
+// deadline based on the current time.
+TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) {
+ CompositorTiming initialCompositorTiming {
+ 1000000000, // 1s deadline
+ 16666667, // 16ms interval
+ 50000000, // 50ms present latency
+ };
+ mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+ enableFrameTimestamps();
+
+ nsecs_t compositeDeadline = 0;
+ nsecs_t compositeInterval = 0;
+ nsecs_t compositeToPresentLatency = 0;
+
+ // A "now" just before the deadline snaps to the deadline.
+ mSurface->setNow(initialCompositorTiming.deadline - 1);
+ int result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+ nsecs_t expectedDeadline = initialCompositorTiming.deadline;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+
+ // A "now" just after the deadline snaps properly.
+ mSurface->setNow(initialCompositorTiming.deadline + 1);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ expectedDeadline =
+ initialCompositorTiming.deadline +initialCompositorTiming.interval;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+ const uint64_t fId2 = getNextFrameId();
+ dequeueAndQueue(1);
+ addFrameEvents(true, 0, 1);
+
+ // A "now" just after the next interval snaps properly.
+ mSurface->setNow(
+ mFrames[0].mRefreshes[1].kCompositorTiming.deadline +
+ mFrames[0].mRefreshes[1].kCompositorTiming.interval + 1);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ expectedDeadline =
+ mFrames[0].mRefreshes[1].kCompositorTiming.deadline +
+ mFrames[0].mRefreshes[1].kCompositorTiming.interval * 2;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+ dequeueAndQueue(2);
+ addFrameEvents(true, 1, 2);
+
+ // A "now" over 1 interval before the deadline snaps properly.
+ mSurface->setNow(
+ mFrames[1].mRefreshes[1].kCompositorTiming.deadline -
+ mFrames[1].mRefreshes[1].kCompositorTiming.interval - 1);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ expectedDeadline =
+ mFrames[1].mRefreshes[1].kCompositorTiming.deadline -
+ mFrames[1].mRefreshes[1].kCompositorTiming.interval;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+ // Re-enabling frame timestamps should get the latest values.
+ disableFrameTimestamps();
+ enableFrameTimestamps();
+
+ // A "now" over 2 intervals before the deadline snaps properly.
+ mSurface->setNow(
+ mFrames[2].mRefreshes[1].kCompositorTiming.deadline -
+ mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2 - 1);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ expectedDeadline =
+ mFrames[2].mRefreshes[1].kCompositorTiming.deadline -
+ mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+}
+
+// This verifies the timestamps recorded in the consumer's
+// FrameTimestampsHistory are properly retrieved by the producer for the
+// correct frames.
+TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) {
+ enableFrameTimestamps();
+
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ const uint64_t fId2 = getNextFrameId();
+ 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.
+ resetTimestamps();
+ int result = getAllFrameTimestamps(fId1);
+ 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].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+
+ // Verify timestamps are correct for frame 2.
+ resetTimestamps();
+ result = getAllFrameTimestamps(fId2);
+ 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(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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();
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ 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(), fId1,
+ &outRequestedPresentTime, &outAcquireTime, 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(NATIVE_WINDOW_TIMESTAMP_PENDING, 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(), fId1,
+ &outRequestedPresentTime, &outAcquireTime, 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.
+ const uint64_t fId2 = getNextFrameId();
+ 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(), fId2,
+ &outRequestedPresentTime, &outAcquireTime, 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(NATIVE_WINDOW_TIMESTAMP_PENDING, 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(), fId2,
+ &outRequestedPresentTime, &outAcquireTime, 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();
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ const uint64_t fId2 = getNextFrameId();
+ 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.
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+ 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();
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ 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.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(fId1);
+ 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(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(fId1);
+ 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(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+ // Signal the fences for frame 1.
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+
+ // Verify all timestamps are available without a sync call.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(fId1);
+ 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].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();
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ 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(fId1);
+ 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(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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(fId1);
+ 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(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ 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, NoReleaseNoSync) {
+ enableFrameTimestamps();
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ const uint64_t fId2 = getNextFrameId();
+ 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(fId1);
+ 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(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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, 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.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(fId2);
+ 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(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+}
+
+// This test verifies there are no sync calls for present times
+// when they aren't supported and that an error is returned.
+
+TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportsPresent(false);
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+
+ // Verify a query for the Present times do not trigger a sync call if they
+ // are not supported.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ &outDisplayPresentTime, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(BAD_VALUE, result);
+ EXPECT_EQ(-1, outDisplayPresentTime);
+}
+
+} // namespace android
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
new file mode 100644
index 0000000..5ed3d3b
--- /dev/null
+++ b/libs/gui/view/Surface.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Surface"
+
+#include <gui/view/Surface.h>
+
+#include <binder/Parcel.h>
+
+#include <utils/Log.h>
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+namespace view {
+
+status_t Surface::writeToParcel(Parcel* parcel) const {
+ return writeToParcel(parcel, false);
+}
+
+status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
+ if (parcel == nullptr) return BAD_VALUE;
+
+ status_t res = OK;
+
+ if (!nameAlreadyWritten) {
+ res = parcel->writeString16(name);
+ if (res != OK) return res;
+
+ /* isSingleBuffered defaults to no */
+ res = parcel->writeInt32(0);
+ if (res != OK) return res;
+ }
+
+ res = parcel->writeStrongBinder(
+ IGraphicBufferProducer::asBinder(graphicBufferProducer));
+
+ return res;
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel) {
+ return readFromParcel(parcel, false);
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
+ if (parcel == nullptr) return BAD_VALUE;
+
+ status_t res = OK;
+ if (!nameAlreadyRead) {
+ name = readMaybeEmptyString16(parcel);
+ // Discard this for now
+ int isSingleBuffered;
+ res = parcel->readInt32(&isSingleBuffered);
+ if (res != OK) {
+ ALOGE("Can't read isSingleBuffered");
+ return res;
+ }
+ }
+
+ sp<IBinder> binder;
+
+ res = parcel->readNullableStrongBinder(&binder);
+ if (res != OK) {
+ ALOGE("%s: Can't read strong binder", __FUNCTION__);
+ return res;
+ }
+
+ graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
+
+ return OK;
+}
+
+String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
+ size_t len;
+ const char16_t* str = parcel->readString16Inplace(&len);
+ if (str != nullptr) {
+ return String16(str, len);
+ } else {
+ return String16();
+ }
+}
+
+} // namespace view
+} // namespace android
diff --git a/libs/hwc2on1adapter/Android.bp b/libs/hwc2on1adapter/Android.bp
new file mode 100644
index 0000000..ec9cbf8
--- /dev/null
+++ b/libs/hwc2on1adapter/Android.bp
@@ -0,0 +1,72 @@
+// Copyright 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libhwc2on1adapter",
+ vendor: true,
+
+ clang: true,
+ cppflags: [
+ "-Weverything",
+ "-Wall",
+ "-Wunused",
+ "-Wunreachable-code",
+
+ // The static constructors and destructors in this library have not been noted to
+ // introduce significant overheads
+ "-Wno-exit-time-destructors",
+ "-Wno-global-constructors",
+
+ // We only care about compiling as C++14
+ "-Wno-c++98-compat-pedantic",
+
+ // android/sensors.h uses nested anonymous unions and anonymous structs
+ "-Wno-nested-anon-types",
+ "-Wno-gnu-anonymous-struct",
+
+ // Don't warn about struct padding
+ "-Wno-padded",
+
+ // hwcomposer2.h features switch covering all cases.
+ "-Wno-covered-switch-default",
+
+ // hwcomposer.h features zero size array.
+ "-Wno-zero-length-array",
+
+ // Disabling warning specific to hwc2on1adapter code
+ "-Wno-double-promotion",
+ "-Wno-sign-conversion",
+ "-Wno-switch-enum",
+ "-Wno-float-equal",
+ "-Wno-shorten-64-to-32",
+ "-Wno-sign-compare",
+ "-Wno-missing-prototypes",
+ ],
+
+ srcs: [
+ "HWC2On1Adapter.cpp",
+ "MiniFence.cpp",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "libcutils",
+ "liblog",
+ "libhardware",
+ ],
+
+ export_include_dirs: ["include"],
+
+ export_shared_lib_headers: ["libutils"],
+}
diff --git a/libs/hwc2on1adapter/CleanSpec.mk b/libs/hwc2on1adapter/CleanSpec.mk
new file mode 100644
index 0000000..7fc2216
--- /dev/null
+++ b/libs/hwc2on1adapter/CleanSpec.mk
@@ -0,0 +1,52 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhwc2on1adapter_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwc2on1adapter.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libhwc2on1adapter.so)
diff --git a/libs/hwc2on1adapter/HWC2On1Adapter.cpp b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
new file mode 100644
index 0000000..8c6ef69
--- /dev/null
+++ b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
@@ -0,0 +1,2620 @@
+/*
+ * Copyright 2015 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 "hwc2on1adapter/HWC2On1Adapter.h"
+
+//#define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "HWC2On1Adapter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+
+#include <inttypes.h>
+
+#include <chrono>
+#include <cstdlib>
+#include <sstream>
+
+#include <hardware/hwcomposer.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+using namespace std::chrono_literals;
+
+static uint8_t getMinorVersion(struct hwc_composer_device_1* device)
+{
+ auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
+ return (version >> 16) & 0xF;
+}
+
+template <typename PFN, typename T>
+static hwc2_function_pointer_t asFP(T function)
+{
+ static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
+ return reinterpret_cast<hwc2_function_pointer_t>(function);
+}
+
+using namespace HWC2;
+
+static constexpr Attribute ColorMode = static_cast<Attribute>(6);
+
+namespace android {
+
+class HWC2On1Adapter::Callbacks : public hwc_procs_t {
+ public:
+ explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
+ invalidate = &invalidateHook;
+ vsync = &vsyncHook;
+ hotplug = &hotplugHook;
+ }
+
+ static void invalidateHook(const hwc_procs_t* procs) {
+ auto callbacks = static_cast<const Callbacks*>(procs);
+ callbacks->mAdapter.hwc1Invalidate();
+ }
+
+ static void vsyncHook(const hwc_procs_t* procs, int display,
+ int64_t timestamp) {
+ auto callbacks = static_cast<const Callbacks*>(procs);
+ callbacks->mAdapter.hwc1Vsync(display, timestamp);
+ }
+
+ static void hotplugHook(const hwc_procs_t* procs, int display,
+ int connected) {
+ auto callbacks = static_cast<const Callbacks*>(procs);
+ callbacks->mAdapter.hwc1Hotplug(display, connected);
+ }
+
+ private:
+ HWC2On1Adapter& mAdapter;
+};
+
+static int closeHook(hw_device_t* /*device*/)
+{
+ // Do nothing, since the real work is done in the class destructor, but we
+ // need to provide a valid function pointer for hwc2_close to call
+ return 0;
+}
+
+HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device)
+ : mDumpString(),
+ mHwc1Device(hwc1Device),
+ mHwc1MinorVersion(getMinorVersion(hwc1Device)),
+ mHwc1SupportsVirtualDisplays(false),
+ mHwc1SupportsBackgroundColor(false),
+ mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
+ mCapabilities(),
+ mLayers(),
+ mHwc1VirtualDisplay(),
+ mStateMutex(),
+ mCallbacks(),
+ mHasPendingInvalidate(false),
+ mPendingVsyncs(),
+ mPendingHotplugs(),
+ mDisplays(),
+ mHwc1DisplayMap()
+{
+ common.close = closeHook;
+ getCapabilities = getCapabilitiesHook;
+ getFunction = getFunctionHook;
+ populateCapabilities();
+ populatePrimary();
+ mHwc1Device->registerProcs(mHwc1Device,
+ static_cast<const hwc_procs_t*>(mHwc1Callbacks.get()));
+}
+
+HWC2On1Adapter::~HWC2On1Adapter() {
+ hwc_close_1(mHwc1Device);
+}
+
+void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount,
+ int32_t* outCapabilities) {
+ if (outCapabilities == nullptr) {
+ *outCount = mCapabilities.size();
+ return;
+ }
+
+ auto capabilityIter = mCapabilities.cbegin();
+ for (size_t written = 0; written < *outCount; ++written) {
+ if (capabilityIter == mCapabilities.cend()) {
+ return;
+ }
+ outCapabilities[written] = static_cast<int32_t>(*capabilityIter);
+ ++capabilityIter;
+ }
+}
+
+hwc2_function_pointer_t HWC2On1Adapter::doGetFunction(
+ FunctionDescriptor descriptor) {
+ switch (descriptor) {
+ // Device functions
+ case FunctionDescriptor::CreateVirtualDisplay:
+ return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(
+ createVirtualDisplayHook);
+ case FunctionDescriptor::DestroyVirtualDisplay:
+ return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(
+ destroyVirtualDisplayHook);
+ case FunctionDescriptor::Dump:
+ return asFP<HWC2_PFN_DUMP>(dumpHook);
+ case FunctionDescriptor::GetMaxVirtualDisplayCount:
+ return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(
+ getMaxVirtualDisplayCountHook);
+ case FunctionDescriptor::RegisterCallback:
+ return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook);
+
+ // Display functions
+ case FunctionDescriptor::AcceptDisplayChanges:
+ return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
+ displayHook<decltype(&Display::acceptChanges),
+ &Display::acceptChanges>);
+ case FunctionDescriptor::CreateLayer:
+ return asFP<HWC2_PFN_CREATE_LAYER>(
+ displayHook<decltype(&Display::createLayer),
+ &Display::createLayer, hwc2_layer_t*>);
+ case FunctionDescriptor::DestroyLayer:
+ return asFP<HWC2_PFN_DESTROY_LAYER>(
+ displayHook<decltype(&Display::destroyLayer),
+ &Display::destroyLayer, hwc2_layer_t>);
+ case FunctionDescriptor::GetActiveConfig:
+ return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>(
+ displayHook<decltype(&Display::getActiveConfig),
+ &Display::getActiveConfig, hwc2_config_t*>);
+ case FunctionDescriptor::GetChangedCompositionTypes:
+ return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
+ displayHook<decltype(&Display::getChangedCompositionTypes),
+ &Display::getChangedCompositionTypes, uint32_t*,
+ hwc2_layer_t*, int32_t*>);
+ case FunctionDescriptor::GetColorModes:
+ return asFP<HWC2_PFN_GET_COLOR_MODES>(
+ displayHook<decltype(&Display::getColorModes),
+ &Display::getColorModes, uint32_t*, int32_t*>);
+ case FunctionDescriptor::GetDisplayAttribute:
+ return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
+ getDisplayAttributeHook);
+ case FunctionDescriptor::GetDisplayConfigs:
+ return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>(
+ displayHook<decltype(&Display::getConfigs),
+ &Display::getConfigs, uint32_t*, hwc2_config_t*>);
+ case FunctionDescriptor::GetDisplayName:
+ return asFP<HWC2_PFN_GET_DISPLAY_NAME>(
+ displayHook<decltype(&Display::getName),
+ &Display::getName, uint32_t*, char*>);
+ case FunctionDescriptor::GetDisplayRequests:
+ return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>(
+ displayHook<decltype(&Display::getRequests),
+ &Display::getRequests, int32_t*, uint32_t*, hwc2_layer_t*,
+ int32_t*>);
+ case FunctionDescriptor::GetDisplayType:
+ return asFP<HWC2_PFN_GET_DISPLAY_TYPE>(
+ displayHook<decltype(&Display::getType),
+ &Display::getType, int32_t*>);
+ case FunctionDescriptor::GetDozeSupport:
+ return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(
+ displayHook<decltype(&Display::getDozeSupport),
+ &Display::getDozeSupport, int32_t*>);
+ case FunctionDescriptor::GetHdrCapabilities:
+ return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>(
+ displayHook<decltype(&Display::getHdrCapabilities),
+ &Display::getHdrCapabilities, uint32_t*, int32_t*, float*,
+ float*, float*>);
+ case FunctionDescriptor::GetReleaseFences:
+ return asFP<HWC2_PFN_GET_RELEASE_FENCES>(
+ displayHook<decltype(&Display::getReleaseFences),
+ &Display::getReleaseFences, uint32_t*, hwc2_layer_t*,
+ int32_t*>);
+ case FunctionDescriptor::PresentDisplay:
+ return asFP<HWC2_PFN_PRESENT_DISPLAY>(
+ displayHook<decltype(&Display::present),
+ &Display::present, int32_t*>);
+ case FunctionDescriptor::SetActiveConfig:
+ return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>(
+ displayHook<decltype(&Display::setActiveConfig),
+ &Display::setActiveConfig, hwc2_config_t>);
+ case FunctionDescriptor::SetClientTarget:
+ return asFP<HWC2_PFN_SET_CLIENT_TARGET>(
+ displayHook<decltype(&Display::setClientTarget),
+ &Display::setClientTarget, buffer_handle_t, int32_t,
+ int32_t, hwc_region_t>);
+ case FunctionDescriptor::SetColorMode:
+ return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook);
+ case FunctionDescriptor::SetColorTransform:
+ return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook);
+ case FunctionDescriptor::SetOutputBuffer:
+ return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(
+ displayHook<decltype(&Display::setOutputBuffer),
+ &Display::setOutputBuffer, buffer_handle_t, int32_t>);
+ case FunctionDescriptor::SetPowerMode:
+ return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook);
+ case FunctionDescriptor::SetVsyncEnabled:
+ return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook);
+ case FunctionDescriptor::ValidateDisplay:
+ return asFP<HWC2_PFN_VALIDATE_DISPLAY>(
+ displayHook<decltype(&Display::validate),
+ &Display::validate, uint32_t*, uint32_t*>);
+ case FunctionDescriptor::GetClientTargetSupport:
+ return asFP<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(
+ displayHook<decltype(&Display::getClientTargetSupport),
+ &Display::getClientTargetSupport, uint32_t, uint32_t,
+ int32_t, int32_t>);
+
+ // Layer functions
+ case FunctionDescriptor::SetCursorPosition:
+ return asFP<HWC2_PFN_SET_CURSOR_POSITION>(
+ layerHook<decltype(&Layer::setCursorPosition),
+ &Layer::setCursorPosition, int32_t, int32_t>);
+ case FunctionDescriptor::SetLayerBuffer:
+ return asFP<HWC2_PFN_SET_LAYER_BUFFER>(
+ layerHook<decltype(&Layer::setBuffer), &Layer::setBuffer,
+ buffer_handle_t, int32_t>);
+ case FunctionDescriptor::SetLayerSurfaceDamage:
+ return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
+ layerHook<decltype(&Layer::setSurfaceDamage),
+ &Layer::setSurfaceDamage, hwc_region_t>);
+
+ // Layer state functions
+ case FunctionDescriptor::SetLayerBlendMode:
+ return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>(
+ setLayerBlendModeHook);
+ case FunctionDescriptor::SetLayerColor:
+ return asFP<HWC2_PFN_SET_LAYER_COLOR>(
+ layerHook<decltype(&Layer::setColor), &Layer::setColor,
+ hwc_color_t>);
+ case FunctionDescriptor::SetLayerCompositionType:
+ return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
+ setLayerCompositionTypeHook);
+ case FunctionDescriptor::SetLayerDataspace:
+ return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerDataspaceHook);
+ case FunctionDescriptor::SetLayerDisplayFrame:
+ return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
+ layerHook<decltype(&Layer::setDisplayFrame),
+ &Layer::setDisplayFrame, hwc_rect_t>);
+ case FunctionDescriptor::SetLayerPlaneAlpha:
+ return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
+ layerHook<decltype(&Layer::setPlaneAlpha),
+ &Layer::setPlaneAlpha, float>);
+ case FunctionDescriptor::SetLayerSidebandStream:
+ return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(
+ layerHook<decltype(&Layer::setSidebandStream),
+ &Layer::setSidebandStream, const native_handle_t*>);
+ case FunctionDescriptor::SetLayerSourceCrop:
+ return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
+ layerHook<decltype(&Layer::setSourceCrop),
+ &Layer::setSourceCrop, hwc_frect_t>);
+ case FunctionDescriptor::SetLayerTransform:
+ return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerTransformHook);
+ case FunctionDescriptor::SetLayerVisibleRegion:
+ return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
+ layerHook<decltype(&Layer::setVisibleRegion),
+ &Layer::setVisibleRegion, hwc_region_t>);
+ case FunctionDescriptor::SetLayerZOrder:
+ return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerZOrderHook);
+
+ default:
+ ALOGE("doGetFunction: Unknown function descriptor: %d (%s)",
+ static_cast<int32_t>(descriptor),
+ to_string(descriptor).c_str());
+ return nullptr;
+ }
+}
+
+// Device functions
+
+Error HWC2On1Adapter::createVirtualDisplay(uint32_t width,
+ uint32_t height, hwc2_display_t* outDisplay) {
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ if (mHwc1VirtualDisplay) {
+ // We have already allocated our only HWC1 virtual display
+ ALOGE("createVirtualDisplay: HWC1 virtual display already allocated");
+ return Error::NoResources;
+ }
+
+ mHwc1VirtualDisplay = std::make_shared<HWC2On1Adapter::Display>(*this,
+ HWC2::DisplayType::Virtual);
+ mHwc1VirtualDisplay->populateConfigs(width, height);
+ const auto displayId = mHwc1VirtualDisplay->getId();
+ mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL] = displayId;
+ mHwc1VirtualDisplay->setHwc1Id(HWC_DISPLAY_VIRTUAL);
+ mDisplays.emplace(displayId, mHwc1VirtualDisplay);
+ *outDisplay = displayId;
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId) {
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) {
+ return Error::BadDisplay;
+ }
+
+ mHwc1VirtualDisplay.reset();
+ mHwc1DisplayMap.erase(HWC_DISPLAY_VIRTUAL);
+ mDisplays.erase(displayId);
+
+ return Error::None;
+}
+
+void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer) {
+ if (outBuffer != nullptr) {
+ auto copiedBytes = mDumpString.copy(outBuffer, *outSize);
+ *outSize = static_cast<uint32_t>(copiedBytes);
+ return;
+ }
+
+ std::stringstream output;
+
+ output << "-- HWC2On1Adapter --\n";
+
+ output << "Adapting to a HWC 1." << static_cast<int>(mHwc1MinorVersion) <<
+ " device\n";
+
+ // Attempt to acquire the lock for 1 second, but proceed without the lock
+ // after that, so we can still get some information if we're deadlocked
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex,
+ std::defer_lock);
+ lock.try_lock_for(1s);
+
+ if (mCapabilities.empty()) {
+ output << "Capabilities: None\n";
+ } else {
+ output << "Capabilities:\n";
+ for (auto capability : mCapabilities) {
+ output << " " << to_string(capability) << '\n';
+ }
+ }
+
+ output << "Displays:\n";
+ for (const auto& element : mDisplays) {
+ const auto& display = element.second;
+ output << display->dump();
+ }
+ output << '\n';
+
+ // Release the lock before calling into HWC1, and since we no longer require
+ // mutual exclusion to access mCapabilities or mDisplays
+ lock.unlock();
+
+ if (mHwc1Device->dump) {
+ output << "HWC1 dump:\n";
+ std::vector<char> hwc1Dump(4096);
+ // Call with size - 1 to preserve a null character at the end
+ mHwc1Device->dump(mHwc1Device, hwc1Dump.data(),
+ static_cast<int>(hwc1Dump.size() - 1));
+ output << hwc1Dump.data();
+ }
+
+ mDumpString = output.str();
+ *outSize = static_cast<uint32_t>(mDumpString.size());
+}
+
+uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount() {
+ return mHwc1SupportsVirtualDisplays ? 1 : 0;
+}
+
+static bool isValid(Callback descriptor) {
+ switch (descriptor) {
+ case Callback::Hotplug: // Fall-through
+ case Callback::Refresh: // Fall-through
+ case Callback::Vsync: return true;
+ default: return false;
+ }
+}
+
+Error HWC2On1Adapter::registerCallback(Callback descriptor,
+ hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) {
+ if (!isValid(descriptor)) {
+ return Error::BadParameter;
+ }
+
+ ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(),
+ callbackData, pointer);
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ mCallbacks[descriptor] = {callbackData, pointer};
+
+ bool hasPendingInvalidate = false;
+ std::vector<hwc2_display_t> displayIds;
+ std::vector<std::pair<hwc2_display_t, int64_t>> pendingVsyncs;
+ std::vector<std::pair<hwc2_display_t, int>> pendingHotplugs;
+
+ if (descriptor == Callback::Refresh) {
+ hasPendingInvalidate = mHasPendingInvalidate;
+ if (hasPendingInvalidate) {
+ for (auto& displayPair : mDisplays) {
+ displayIds.emplace_back(displayPair.first);
+ }
+ }
+ mHasPendingInvalidate = false;
+ } else if (descriptor == Callback::Vsync) {
+ for (auto pending : mPendingVsyncs) {
+ auto hwc1DisplayId = pending.first;
+ if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+ ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d",
+ hwc1DisplayId);
+ continue;
+ }
+ auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+ auto timestamp = pending.second;
+ pendingVsyncs.emplace_back(displayId, timestamp);
+ }
+ mPendingVsyncs.clear();
+ } else if (descriptor == Callback::Hotplug) {
+ // Hotplug the primary display
+ pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY],
+ static_cast<int32_t>(Connection::Connected));
+
+ for (auto pending : mPendingHotplugs) {
+ auto hwc1DisplayId = pending.first;
+ if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+ ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d",
+ hwc1DisplayId);
+ continue;
+ }
+ auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+ auto connected = pending.second;
+ pendingHotplugs.emplace_back(displayId, connected);
+ }
+ }
+
+ // Call pending callbacks without the state lock held
+ lock.unlock();
+
+ if (hasPendingInvalidate) {
+ auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(pointer);
+ for (auto displayId : displayIds) {
+ refresh(callbackData, displayId);
+ }
+ }
+ if (!pendingVsyncs.empty()) {
+ auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
+ for (auto& pendingVsync : pendingVsyncs) {
+ vsync(callbackData, pendingVsync.first, pendingVsync.second);
+ }
+ }
+ if (!pendingHotplugs.empty()) {
+ auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer);
+ for (auto& pendingHotplug : pendingHotplugs) {
+ hotplug(callbackData, pendingHotplug.first, pendingHotplug.second);
+ }
+ }
+ return Error::None;
+}
+
+// Display functions
+
+std::atomic<hwc2_display_t> HWC2On1Adapter::Display::sNextId(1);
+
+HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type)
+ : mId(sNextId++),
+ mDevice(device),
+ mStateMutex(),
+ mHwc1RequestedContents(nullptr),
+ mRetireFence(),
+ mChanges(),
+ mHwc1Id(-1),
+ mConfigs(),
+ mActiveConfig(nullptr),
+ mActiveColorMode(static_cast<android_color_mode_t>(-1)),
+ mName(),
+ mType(type),
+ mPowerMode(PowerMode::Off),
+ mVsyncEnabled(Vsync::Invalid),
+ mClientTarget(),
+ mOutputBuffer(),
+ mHasColorTransform(false),
+ mLayers(),
+ mHwc1LayerMap(),
+ mNumAvailableRects(0),
+ mNextAvailableRect(nullptr),
+ mGeometryChanged(false)
+ {}
+
+Error HWC2On1Adapter::Display::acceptChanges() {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges) {
+ ALOGV("[%" PRIu64 "] acceptChanges failed, not validated", mId);
+ return Error::NotValidated;
+ }
+
+ ALOGV("[%" PRIu64 "] acceptChanges", mId);
+
+ for (auto& change : mChanges->getTypeChanges()) {
+ auto layerId = change.first;
+ auto type = change.second;
+ if (mDevice.mLayers.count(layerId) == 0) {
+ // This should never happen but somehow does.
+ ALOGW("Cannot accept change for unknown layer (%" PRIu64 ")",
+ layerId);
+ continue;
+ }
+ auto layer = mDevice.mLayers[layerId];
+ layer->setCompositionType(type);
+ }
+
+ mChanges->clearTypeChanges();
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto layer = *mLayers.emplace(std::make_shared<Layer>(*this));
+ mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer));
+ *outLayerId = layer->getId();
+ ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId);
+ markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ const auto mapLayer = mDevice.mLayers.find(layerId);
+ if (mapLayer == mDevice.mLayers.end()) {
+ ALOGV("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer",
+ mId, layerId);
+ return Error::BadLayer;
+ }
+ const auto layer = mapLayer->second;
+ mDevice.mLayers.erase(mapLayer);
+ const auto zRange = mLayers.equal_range(layer);
+ for (auto current = zRange.first; current != zRange.second; ++current) {
+ if (**current == *layer) {
+ current = mLayers.erase(current);
+ break;
+ }
+ }
+ ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId);
+ markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mActiveConfig) {
+ ALOGV("[%" PRIu64 "] getActiveConfig --> %s", mId,
+ to_string(Error::BadConfig).c_str());
+ return Error::BadConfig;
+ }
+ auto configId = mActiveConfig->getId();
+ ALOGV("[%" PRIu64 "] getActiveConfig --> %u", mId, configId);
+ *outConfig = configId;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId,
+ Attribute attribute, int32_t* outValue) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
+ ALOGV("[%" PRIu64 "] getAttribute failed: bad config (%u)", mId,
+ configId);
+ return Error::BadConfig;
+ }
+ *outValue = mConfigs[configId]->getAttribute(attribute);
+ ALOGV("[%" PRIu64 "] getAttribute(%u, %s) --> %d", mId, configId,
+ to_string(attribute).c_str(), *outValue);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getChangedCompositionTypes(
+ uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges) {
+ ALOGE("[%" PRIu64 "] getChangedCompositionTypes failed: not validated",
+ mId);
+ return Error::NotValidated;
+ }
+
+ if ((outLayers == nullptr) || (outTypes == nullptr)) {
+ *outNumElements = mChanges->getTypeChanges().size();
+ return Error::None;
+ }
+
+ uint32_t numWritten = 0;
+ for (const auto& element : mChanges->getTypeChanges()) {
+ if (numWritten == *outNumElements) {
+ break;
+ }
+ auto layerId = element.first;
+ auto intType = static_cast<int32_t>(element.second);
+ ALOGV("Adding %" PRIu64 " %s", layerId,
+ to_string(element.second).c_str());
+ outLayers[numWritten] = layerId;
+ outTypes[numWritten] = intType;
+ ++numWritten;
+ }
+ *outNumElements = numWritten;
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getColorModes(uint32_t* outNumModes,
+ int32_t* outModes) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!outModes) {
+ *outNumModes = mColorModes.size();
+ return Error::None;
+ }
+ uint32_t numModes = std::min(*outNumModes,
+ static_cast<uint32_t>(mColorModes.size()));
+ std::copy_n(mColorModes.cbegin(), numModes, outModes);
+ *outNumModes = numModes;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs,
+ hwc2_config_t* outConfigs) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!outConfigs) {
+ *outNumConfigs = mConfigs.size();
+ return Error::None;
+ }
+ uint32_t numWritten = 0;
+ for (const auto& config : mConfigs) {
+ if (numWritten == *outNumConfigs) {
+ break;
+ }
+ outConfigs[numWritten] = config->getId();
+ ++numWritten;
+ }
+ *outNumConfigs = numWritten;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) {
+ *outSupport = 0;
+ } else {
+ *outSupport = 1;
+ }
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getHdrCapabilities(uint32_t* outNumTypes,
+ int32_t* /*outTypes*/, float* /*outMaxLuminance*/,
+ float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
+ // This isn't supported on HWC1, so per the HWC2 header, return numTypes = 0
+ *outNumTypes = 0;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!outName) {
+ *outSize = mName.size();
+ return Error::None;
+ }
+ auto numCopied = mName.copy(outName, *outSize);
+ *outSize = numCopied;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements,
+ hwc2_layer_t* outLayers, int32_t* outFences) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ uint32_t numWritten = 0;
+ bool outputsNonNull = (outLayers != nullptr) && (outFences != nullptr);
+ for (const auto& layer : mLayers) {
+ if (outputsNonNull && (numWritten == *outNumElements)) {
+ break;
+ }
+
+ auto releaseFence = layer->getReleaseFence();
+ if (releaseFence != MiniFence::NO_FENCE) {
+ if (outputsNonNull) {
+ outLayers[numWritten] = layer->getId();
+ outFences[numWritten] = releaseFence->dup();
+ }
+ ++numWritten;
+ }
+ }
+ *outNumElements = numWritten;
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests,
+ uint32_t* outNumElements, hwc2_layer_t* outLayers,
+ int32_t* outLayerRequests) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges) {
+ return Error::NotValidated;
+ }
+
+ if (outLayers == nullptr || outLayerRequests == nullptr) {
+ *outNumElements = mChanges->getNumLayerRequests();
+ return Error::None;
+ }
+
+ // Display requests (HWC2::DisplayRequest) are not supported by hwc1:
+ // A hwc1 has always zero requests for the client.
+ *outDisplayRequests = 0;
+
+ uint32_t numWritten = 0;
+ for (const auto& request : mChanges->getLayerRequests()) {
+ if (numWritten == *outNumElements) {
+ break;
+ }
+ outLayers[numWritten] = request.first;
+ outLayerRequests[numWritten] = static_cast<int32_t>(request.second);
+ ++numWritten;
+ }
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getType(int32_t* outType) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ *outType = static_cast<int32_t>(mType);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::present(int32_t* outRetireFence) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (mChanges) {
+ Error error = mDevice.setAllDisplays();
+ if (error != Error::None) {
+ ALOGE("[%" PRIu64 "] present: setAllDisplaysFailed (%s)", mId,
+ to_string(error).c_str());
+ return error;
+ }
+ }
+
+ *outRetireFence = mRetireFence.get()->dup();
+ ALOGV("[%" PRIu64 "] present returning retire fence %d", mId,
+ *outRetireFence);
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto config = getConfig(configId);
+ if (!config) {
+ return Error::BadConfig;
+ }
+ if (config == mActiveConfig) {
+ return Error::None;
+ }
+
+ if (mDevice.mHwc1MinorVersion >= 4) {
+ uint32_t hwc1Id = 0;
+ auto error = config->getHwc1IdForColorMode(mActiveColorMode, &hwc1Id);
+ if (error != Error::None) {
+ return error;
+ }
+
+ int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+ mHwc1Id, static_cast<int>(hwc1Id));
+ if (intError != 0) {
+ ALOGE("setActiveConfig: Failed to set active config on HWC1 (%d)",
+ intError);
+ return Error::BadConfig;
+ }
+ mActiveConfig = config;
+ }
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target,
+ int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence);
+ mClientTarget.setBuffer(target);
+ mClientTarget.setFence(acquireFence);
+ // dataspace and damage can't be used by HWC1, so ignore them
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode) {
+ std::unique_lock<std::recursive_mutex> lock (mStateMutex);
+
+ ALOGV("[%" PRIu64 "] setColorMode(%d)", mId, mode);
+
+ if (mode == mActiveColorMode) {
+ return Error::None;
+ }
+ if (mColorModes.count(mode) == 0) {
+ ALOGE("[%" PRIu64 "] Mode %d not found in mColorModes", mId, mode);
+ return Error::Unsupported;
+ }
+
+ uint32_t hwc1Config = 0;
+ auto error = mActiveConfig->getHwc1IdForColorMode(mode, &hwc1Config);
+ if (error != Error::None) {
+ return error;
+ }
+
+ ALOGV("[%" PRIu64 "] Setting HWC1 config %u", mId, hwc1Config);
+ int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+ mHwc1Id, hwc1Config);
+ if (intError != 0) {
+ ALOGE("[%" PRIu64 "] Failed to set HWC1 config (%d)", mId, intError);
+ return Error::Unsupported;
+ }
+
+ mActiveColorMode = mode;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ ALOGV("%" PRIu64 "] setColorTransform(%d)", mId,
+ static_cast<int32_t>(hint));
+ mHasColorTransform = (hint != HAL_COLOR_TRANSFORM_IDENTITY);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer,
+ int32_t releaseFence) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence);
+ mOutputBuffer.setBuffer(buffer);
+ mOutputBuffer.setFence(releaseFence);
+ return Error::None;
+}
+
+static bool isValid(PowerMode mode) {
+ switch (mode) {
+ case PowerMode::Off: // Fall-through
+ case PowerMode::DozeSuspend: // Fall-through
+ case PowerMode::Doze: // Fall-through
+ case PowerMode::On: return true;
+ }
+}
+
+static int getHwc1PowerMode(PowerMode mode) {
+ switch (mode) {
+ case PowerMode::Off: return HWC_POWER_MODE_OFF;
+ case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND;
+ case PowerMode::Doze: return HWC_POWER_MODE_DOZE;
+ case PowerMode::On: return HWC_POWER_MODE_NORMAL;
+ }
+}
+
+Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode) {
+ if (!isValid(mode)) {
+ return Error::BadParameter;
+ }
+ if (mode == mPowerMode) {
+ return Error::None;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ int error = 0;
+ if (mDevice.mHwc1MinorVersion < 4) {
+ error = mDevice.mHwc1Device->blank(mDevice.mHwc1Device, mHwc1Id,
+ mode == PowerMode::Off);
+ } else {
+ error = mDevice.mHwc1Device->setPowerMode(mDevice.mHwc1Device,
+ mHwc1Id, getHwc1PowerMode(mode));
+ }
+ ALOGE_IF(error != 0, "setPowerMode: Failed to set power mode on HWC1 (%d)",
+ error);
+
+ ALOGV("[%" PRIu64 "] setPowerMode(%s)", mId, to_string(mode).c_str());
+ mPowerMode = mode;
+ return Error::None;
+}
+
+static bool isValid(Vsync enable) {
+ switch (enable) {
+ case Vsync::Enable: // Fall-through
+ case Vsync::Disable: return true;
+ case Vsync::Invalid: return false;
+ }
+}
+
+Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable) {
+ if (!isValid(enable)) {
+ return Error::BadParameter;
+ }
+ if (enable == mVsyncEnabled) {
+ return Error::None;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ int error = mDevice.mHwc1Device->eventControl(mDevice.mHwc1Device,
+ mHwc1Id, HWC_EVENT_VSYNC, enable == Vsync::Enable);
+ ALOGE_IF(error != 0, "setVsyncEnabled: Failed to set vsync on HWC1 (%d)",
+ error);
+
+ mVsyncEnabled = enable;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges) {
+ if (!mDevice.prepareAllDisplays()) {
+ return Error::BadDisplay;
+ }
+ } else {
+ ALOGE("Validate was called more than once!");
+ }
+
+ *outNumTypes = mChanges->getNumTypes();
+ *outNumRequests = mChanges->getNumLayerRequests();
+ ALOGV("[%" PRIu64 "] validate --> %u types, %u requests", mId, *outNumTypes,
+ *outNumRequests);
+ for (auto request : mChanges->getTypeChanges()) {
+ ALOGV("Layer %" PRIu64 " --> %s", request.first,
+ to_string(request.second).c_str());
+ }
+ return *outNumTypes > 0 ? Error::HasChanges : Error::None;
+}
+
+Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ const auto mapLayer = mDevice.mLayers.find(layerId);
+ if (mapLayer == mDevice.mLayers.end()) {
+ ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", mId);
+ return Error::BadLayer;
+ }
+
+ const auto layer = mapLayer->second;
+ const auto zRange = mLayers.equal_range(layer);
+ bool layerOnDisplay = false;
+ for (auto current = zRange.first; current != zRange.second; ++current) {
+ if (**current == *layer) {
+ if ((*current)->getZ() == z) {
+ // Don't change anything if the Z hasn't changed
+ return Error::None;
+ }
+ current = mLayers.erase(current);
+ layerOnDisplay = true;
+ break;
+ }
+ }
+
+ if (!layerOnDisplay) {
+ ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display",
+ mId);
+ return Error::BadLayer;
+ }
+
+ layer->setZ(z);
+ mLayers.emplace(std::move(layer));
+ markGeometryChanged();
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getClientTargetSupport(uint32_t width, uint32_t height,
+ int32_t format, int32_t dataspace){
+ if (mActiveConfig == nullptr) {
+ return Error::Unsupported;
+ }
+
+ if (width == mActiveConfig->getAttribute(Attribute::Width) &&
+ height == mActiveConfig->getAttribute(Attribute::Height) &&
+ format == HAL_PIXEL_FORMAT_RGBA_8888 &&
+ dataspace == HAL_DATASPACE_UNKNOWN) {
+ return Error::None;
+ }
+
+ return Error::Unsupported;
+}
+
+static constexpr uint32_t ATTRIBUTES_WITH_COLOR[] = {
+ HWC_DISPLAY_VSYNC_PERIOD,
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_DPI_Y,
+ HWC_DISPLAY_COLOR_TRANSFORM,
+ HWC_DISPLAY_NO_ATTRIBUTE,
+};
+
+static constexpr uint32_t ATTRIBUTES_WITHOUT_COLOR[] = {
+ HWC_DISPLAY_VSYNC_PERIOD,
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_DPI_Y,
+ HWC_DISPLAY_NO_ATTRIBUTE,
+};
+
+static constexpr size_t NUM_ATTRIBUTES_WITH_COLOR =
+ sizeof(ATTRIBUTES_WITH_COLOR) / sizeof(uint32_t);
+static_assert(sizeof(ATTRIBUTES_WITH_COLOR) > sizeof(ATTRIBUTES_WITHOUT_COLOR),
+ "Attribute tables have unexpected sizes");
+
+static constexpr uint32_t ATTRIBUTE_MAP_WITH_COLOR[] = {
+ 6, // HWC_DISPLAY_NO_ATTRIBUTE = 0
+ 0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
+ 1, // HWC_DISPLAY_WIDTH = 2,
+ 2, // HWC_DISPLAY_HEIGHT = 3,
+ 3, // HWC_DISPLAY_DPI_X = 4,
+ 4, // HWC_DISPLAY_DPI_Y = 5,
+ 5, // HWC_DISPLAY_COLOR_TRANSFORM = 6,
+};
+
+static constexpr uint32_t ATTRIBUTE_MAP_WITHOUT_COLOR[] = {
+ 5, // HWC_DISPLAY_NO_ATTRIBUTE = 0
+ 0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
+ 1, // HWC_DISPLAY_WIDTH = 2,
+ 2, // HWC_DISPLAY_HEIGHT = 3,
+ 3, // HWC_DISPLAY_DPI_X = 4,
+ 4, // HWC_DISPLAY_DPI_Y = 5,
+};
+
+template <uint32_t attribute>
+static constexpr bool attributesMatch()
+{
+ bool match = (attribute ==
+ ATTRIBUTES_WITH_COLOR[ATTRIBUTE_MAP_WITH_COLOR[attribute]]);
+ if (attribute == HWC_DISPLAY_COLOR_TRANSFORM) {
+ return match;
+ }
+
+ return match && (attribute ==
+ ATTRIBUTES_WITHOUT_COLOR[ATTRIBUTE_MAP_WITHOUT_COLOR[attribute]]);
+}
+static_assert(attributesMatch<HWC_DISPLAY_VSYNC_PERIOD>(),
+ "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_WIDTH>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_HEIGHT>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_DPI_X>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_DPI_Y>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_COLOR_TRANSFORM>(),
+ "Tables out of sync");
+
+void HWC2On1Adapter::Display::populateConfigs() {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ ALOGV("[%" PRIu64 "] populateConfigs", mId);
+
+ if (mHwc1Id == -1) {
+ ALOGE("populateConfigs: HWC1 ID not set");
+ return;
+ }
+
+ const size_t MAX_NUM_CONFIGS = 128;
+ uint32_t configs[MAX_NUM_CONFIGS] = {};
+ size_t numConfigs = MAX_NUM_CONFIGS;
+ mDevice.mHwc1Device->getDisplayConfigs(mDevice.mHwc1Device, mHwc1Id,
+ configs, &numConfigs);
+
+ for (size_t c = 0; c < numConfigs; ++c) {
+ uint32_t hwc1ConfigId = configs[c];
+ auto newConfig = std::make_shared<Config>(*this);
+
+ int32_t values[NUM_ATTRIBUTES_WITH_COLOR] = {};
+ bool hasColor = true;
+ auto result = mDevice.mHwc1Device->getDisplayAttributes(
+ mDevice.mHwc1Device, mHwc1Id, hwc1ConfigId,
+ ATTRIBUTES_WITH_COLOR, values);
+ if (result != 0) {
+ mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device,
+ mHwc1Id, hwc1ConfigId, ATTRIBUTES_WITHOUT_COLOR, values);
+ hasColor = false;
+ }
+
+ auto attributeMap = hasColor ?
+ ATTRIBUTE_MAP_WITH_COLOR : ATTRIBUTE_MAP_WITHOUT_COLOR;
+
+ newConfig->setAttribute(Attribute::VsyncPeriod,
+ values[attributeMap[HWC_DISPLAY_VSYNC_PERIOD]]);
+ newConfig->setAttribute(Attribute::Width,
+ values[attributeMap[HWC_DISPLAY_WIDTH]]);
+ newConfig->setAttribute(Attribute::Height,
+ values[attributeMap[HWC_DISPLAY_HEIGHT]]);
+ newConfig->setAttribute(Attribute::DpiX,
+ values[attributeMap[HWC_DISPLAY_DPI_X]]);
+ newConfig->setAttribute(Attribute::DpiY,
+ values[attributeMap[HWC_DISPLAY_DPI_Y]]);
+ if (hasColor) {
+ // In HWC1, color modes are referred to as color transforms. To avoid confusion with
+ // the HWC2 concept of color transforms, we internally refer to them as color modes for
+ // both HWC1 and 2.
+ newConfig->setAttribute(ColorMode,
+ values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]);
+ }
+
+ // We can only do this after attempting to read the color mode
+ newConfig->setHwc1Id(hwc1ConfigId);
+
+ for (auto& existingConfig : mConfigs) {
+ if (existingConfig->merge(*newConfig)) {
+ ALOGV("Merged config %d with existing config %u: %s",
+ hwc1ConfigId, existingConfig->getId(),
+ existingConfig->toString().c_str());
+ newConfig.reset();
+ break;
+ }
+ }
+
+ // If it wasn't merged with any existing config, add it to the end
+ if (newConfig) {
+ newConfig->setId(static_cast<hwc2_config_t>(mConfigs.size()));
+ ALOGV("Found new config %u: %s", newConfig->getId(),
+ newConfig->toString().c_str());
+ mConfigs.emplace_back(std::move(newConfig));
+ }
+ }
+
+ initializeActiveConfig();
+ populateColorModes();
+}
+
+void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ mConfigs.emplace_back(std::make_shared<Config>(*this));
+ auto& config = mConfigs[0];
+
+ config->setAttribute(Attribute::Width, static_cast<int32_t>(width));
+ config->setAttribute(Attribute::Height, static_cast<int32_t>(height));
+ config->setHwc1Id(0);
+ config->setId(0);
+ mActiveConfig = config;
+}
+
+bool HWC2On1Adapter::Display::prepare() {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ // Only prepare display contents for displays HWC1 knows about
+ if (mHwc1Id == -1) {
+ return true;
+ }
+
+ // It doesn't make sense to prepare a display for which there is no active
+ // config, so return early
+ if (!mActiveConfig) {
+ ALOGE("[%" PRIu64 "] Attempted to prepare, but no config active", mId);
+ return false;
+ }
+
+ allocateRequestedContents();
+ assignHwc1LayerIds();
+
+ mHwc1RequestedContents->retireFenceFd = -1;
+ mHwc1RequestedContents->flags = 0;
+ if (mGeometryChanged) {
+ mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED;
+ }
+ mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer();
+ mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence();
+
+ // +1 is for framebuffer target layer.
+ mHwc1RequestedContents->numHwLayers = mLayers.size() + 1;
+ for (auto& layer : mLayers) {
+ auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
+ hwc1Layer.releaseFenceFd = -1;
+ hwc1Layer.acquireFenceFd = -1;
+ ALOGV("Applying states for layer %" PRIu64 " ", layer->getId());
+ layer->applyState(hwc1Layer);
+ }
+
+ prepareFramebufferTarget();
+
+ resetGeometryMarker();
+
+ return true;
+}
+
+void HWC2On1Adapter::Display::generateChanges() {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ mChanges.reset(new Changes);
+
+ size_t numLayers = mHwc1RequestedContents->numHwLayers;
+ for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
+ const auto& receivedLayer = mHwc1RequestedContents->hwLayers[hwc1Id];
+ if (mHwc1LayerMap.count(hwc1Id) == 0) {
+ ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET,
+ "generateChanges: HWC1 layer %zd doesn't have a"
+ " matching HWC2 layer, and isn't the framebuffer target",
+ hwc1Id);
+ continue;
+ }
+
+ Layer& layer = *mHwc1LayerMap[hwc1Id];
+ updateTypeChanges(receivedLayer, layer);
+ updateLayerRequests(receivedLayer, layer);
+ }
+}
+
+bool HWC2On1Adapter::Display::hasChanges() const {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+ return mChanges != nullptr;
+}
+
+Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges || (mChanges->getNumTypes() > 0)) {
+ ALOGE("[%" PRIu64 "] set failed: not validated", mId);
+ return Error::NotValidated;
+ }
+
+ // Set up the client/framebuffer target
+ auto numLayers = hwcContents.numHwLayers;
+
+ // Close acquire fences on FRAMEBUFFER layers, since they will not be used
+ // by HWC
+ for (size_t l = 0; l < numLayers - 1; ++l) {
+ auto& layer = hwcContents.hwLayers[l];
+ if (layer.compositionType == HWC_FRAMEBUFFER) {
+ ALOGV("Closing fence %d for layer %zd", layer.acquireFenceFd, l);
+ close(layer.acquireFenceFd);
+ layer.acquireFenceFd = -1;
+ }
+ }
+
+ auto& clientTargetLayer = hwcContents.hwLayers[numLayers - 1];
+ if (clientTargetLayer.compositionType == HWC_FRAMEBUFFER_TARGET) {
+ clientTargetLayer.handle = mClientTarget.getBuffer();
+ clientTargetLayer.acquireFenceFd = mClientTarget.getFence();
+ } else {
+ ALOGE("[%" PRIu64 "] set: last HWC layer wasn't FRAMEBUFFER_TARGET",
+ mId);
+ }
+
+ mChanges.reset();
+
+ return Error::None;
+}
+
+void HWC2On1Adapter::Display::addRetireFence(int fenceFd) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+ mRetireFence.add(fenceFd);
+}
+
+void HWC2On1Adapter::Display::addReleaseFences(
+ const hwc_display_contents_1_t& hwcContents) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ size_t numLayers = hwcContents.numHwLayers;
+ for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
+ const auto& receivedLayer = hwcContents.hwLayers[hwc1Id];
+ if (mHwc1LayerMap.count(hwc1Id) == 0) {
+ if (receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET) {
+ ALOGE("addReleaseFences: HWC1 layer %zd doesn't have a"
+ " matching HWC2 layer, and isn't the framebuffer"
+ " target", hwc1Id);
+ }
+ // Close the framebuffer target release fence since we will use the
+ // display retire fence instead
+ if (receivedLayer.releaseFenceFd != -1) {
+ close(receivedLayer.releaseFenceFd);
+ }
+ continue;
+ }
+
+ Layer& layer = *mHwc1LayerMap[hwc1Id];
+ ALOGV("Adding release fence %d to layer %" PRIu64,
+ receivedLayer.releaseFenceFd, layer.getId());
+ layer.addReleaseFence(receivedLayer.releaseFenceFd);
+ }
+}
+
+bool HWC2On1Adapter::Display::hasColorTransform() const {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+ return mHasColorTransform;
+}
+
+static std::string hwc1CompositionString(int32_t type) {
+ switch (type) {
+ case HWC_FRAMEBUFFER: return "Framebuffer";
+ case HWC_OVERLAY: return "Overlay";
+ case HWC_BACKGROUND: return "Background";
+ case HWC_FRAMEBUFFER_TARGET: return "FramebufferTarget";
+ case HWC_SIDEBAND: return "Sideband";
+ case HWC_CURSOR_OVERLAY: return "CursorOverlay";
+ default:
+ return std::string("Unknown (") + std::to_string(type) + ")";
+ }
+}
+
+static std::string hwc1TransformString(int32_t transform) {
+ switch (transform) {
+ case 0: return "None";
+ case HWC_TRANSFORM_FLIP_H: return "FlipH";
+ case HWC_TRANSFORM_FLIP_V: return "FlipV";
+ case HWC_TRANSFORM_ROT_90: return "Rotate90";
+ case HWC_TRANSFORM_ROT_180: return "Rotate180";
+ case HWC_TRANSFORM_ROT_270: return "Rotate270";
+ case HWC_TRANSFORM_FLIP_H_ROT_90: return "FlipHRotate90";
+ case HWC_TRANSFORM_FLIP_V_ROT_90: return "FlipVRotate90";
+ default:
+ return std::string("Unknown (") + std::to_string(transform) + ")";
+ }
+}
+
+static std::string hwc1BlendModeString(int32_t mode) {
+ switch (mode) {
+ case HWC_BLENDING_NONE: return "None";
+ case HWC_BLENDING_PREMULT: return "Premultiplied";
+ case HWC_BLENDING_COVERAGE: return "Coverage";
+ default:
+ return std::string("Unknown (") + std::to_string(mode) + ")";
+ }
+}
+
+static std::string rectString(hwc_rect_t rect) {
+ std::stringstream output;
+ output << "[" << rect.left << ", " << rect.top << ", ";
+ output << rect.right << ", " << rect.bottom << "]";
+ return output.str();
+}
+
+static std::string approximateFloatString(float f) {
+ if (static_cast<int32_t>(f) == f) {
+ return std::to_string(static_cast<int32_t>(f));
+ }
+ int32_t truncated = static_cast<int32_t>(f * 10);
+ bool approximate = (static_cast<float>(truncated) != f * 10);
+ const size_t BUFFER_SIZE = 32;
+ char buffer[BUFFER_SIZE] = {};
+ auto bytesWritten = snprintf(buffer, BUFFER_SIZE,
+ "%s%.1f", approximate ? "~" : "", f);
+ return std::string(buffer, bytesWritten);
+}
+
+static std::string frectString(hwc_frect_t frect) {
+ std::stringstream output;
+ output << "[" << approximateFloatString(frect.left) << ", ";
+ output << approximateFloatString(frect.top) << ", ";
+ output << approximateFloatString(frect.right) << ", ";
+ output << approximateFloatString(frect.bottom) << "]";
+ return output.str();
+}
+
+static std::string colorString(hwc_color_t color) {
+ std::stringstream output;
+ output << "RGBA [";
+ output << static_cast<int32_t>(color.r) << ", ";
+ output << static_cast<int32_t>(color.g) << ", ";
+ output << static_cast<int32_t>(color.b) << ", ";
+ output << static_cast<int32_t>(color.a) << "]";
+ return output.str();
+}
+
+static std::string alphaString(float f) {
+ const size_t BUFFER_SIZE = 8;
+ char buffer[BUFFER_SIZE] = {};
+ auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f);
+ return std::string(buffer, bytesWritten);
+}
+
+static std::string to_string(const hwc_layer_1_t& hwcLayer,
+ int32_t hwc1MinorVersion) {
+ const char* fill = " ";
+
+ std::stringstream output;
+
+ output << " Composition: " <<
+ hwc1CompositionString(hwcLayer.compositionType);
+
+ if (hwcLayer.compositionType == HWC_BACKGROUND) {
+ output << " Color: " << colorString(hwcLayer.backgroundColor) << '\n';
+ } else if (hwcLayer.compositionType == HWC_SIDEBAND) {
+ output << " Stream: " << hwcLayer.sidebandStream << '\n';
+ } else {
+ output << " Buffer: " << hwcLayer.handle << "/" <<
+ hwcLayer.acquireFenceFd << '\n';
+ }
+
+ output << fill << "Display frame: " << rectString(hwcLayer.displayFrame) <<
+ '\n';
+
+ output << fill << "Source crop: ";
+ if (hwc1MinorVersion >= 3) {
+ output << frectString(hwcLayer.sourceCropf) << '\n';
+ } else {
+ output << rectString(hwcLayer.sourceCropi) << '\n';
+ }
+
+ output << fill << "Transform: " << hwc1TransformString(hwcLayer.transform);
+ output << " Blend mode: " << hwc1BlendModeString(hwcLayer.blending);
+ if (hwcLayer.planeAlpha != 0xFF) {
+ output << " Alpha: " << alphaString(hwcLayer.planeAlpha / 255.0f);
+ }
+ output << '\n';
+
+ if (hwcLayer.hints != 0) {
+ output << fill << "Hints:";
+ if ((hwcLayer.hints & HWC_HINT_TRIPLE_BUFFER) != 0) {
+ output << " TripleBuffer";
+ }
+ if ((hwcLayer.hints & HWC_HINT_CLEAR_FB) != 0) {
+ output << " ClearFB";
+ }
+ output << '\n';
+ }
+
+ if (hwcLayer.flags != 0) {
+ output << fill << "Flags:";
+ if ((hwcLayer.flags & HWC_SKIP_LAYER) != 0) {
+ output << " SkipLayer";
+ }
+ if ((hwcLayer.flags & HWC_IS_CURSOR_LAYER) != 0) {
+ output << " IsCursorLayer";
+ }
+ output << '\n';
+ }
+
+ return output.str();
+}
+
+static std::string to_string(const hwc_display_contents_1_t& hwcContents,
+ int32_t hwc1MinorVersion) {
+ const char* fill = " ";
+
+ std::stringstream output;
+ output << fill << "Geometry changed: " <<
+ ((hwcContents.flags & HWC_GEOMETRY_CHANGED) != 0 ? "Y\n" : "N\n");
+
+ output << fill << hwcContents.numHwLayers << " Layer" <<
+ ((hwcContents.numHwLayers == 1) ? "\n" : "s\n");
+ for (size_t layer = 0; layer < hwcContents.numHwLayers; ++layer) {
+ output << fill << " Layer " << layer;
+ output << to_string(hwcContents.hwLayers[layer], hwc1MinorVersion);
+ }
+
+ if (hwcContents.outbuf != nullptr) {
+ output << fill << "Output buffer: " << hwcContents.outbuf << "/" <<
+ hwcContents.outbufAcquireFenceFd << '\n';
+ }
+
+ return output.str();
+}
+
+std::string HWC2On1Adapter::Display::dump() const {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ std::stringstream output;
+
+ output << " Display " << mId << ": ";
+ output << to_string(mType) << " ";
+ output << "HWC1 ID: " << mHwc1Id << " ";
+ output << "Power mode: " << to_string(mPowerMode) << " ";
+ output << "Vsync: " << to_string(mVsyncEnabled) << '\n';
+
+ output << " Color modes [active]:";
+ for (const auto& mode : mColorModes) {
+ if (mode == mActiveColorMode) {
+ output << " [" << mode << ']';
+ } else {
+ output << " " << mode;
+ }
+ }
+ output << '\n';
+
+ output << " " << mConfigs.size() << " Config" <<
+ (mConfigs.size() == 1 ? "" : "s") << " (* active)\n";
+ for (const auto& config : mConfigs) {
+ output << (config == mActiveConfig ? " * " : " ");
+ output << config->toString(true) << '\n';
+ }
+
+ output << " " << mLayers.size() << " Layer" <<
+ (mLayers.size() == 1 ? "" : "s") << '\n';
+ for (const auto& layer : mLayers) {
+ output << layer->dump();
+ }
+
+ output << " Client target: " << mClientTarget.getBuffer() << '\n';
+
+ if (mOutputBuffer.getBuffer() != nullptr) {
+ output << " Output buffer: " << mOutputBuffer.getBuffer() << '\n';
+ }
+
+ if (mHwc1RequestedContents) {
+ output << " Last requested HWC1 state\n";
+ output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion);
+ }
+
+ return output.str();
+}
+
+hwc_rect_t* HWC2On1Adapter::Display::GetRects(size_t numRects) {
+ if (numRects == 0) {
+ return nullptr;
+ }
+
+ if (numRects > mNumAvailableRects) {
+ // This should NEVER happen since we calculated how many rects the
+ // display would need.
+ ALOGE("Rect allocation failure! SF is likely to crash soon!");
+ return nullptr;
+
+ }
+ hwc_rect_t* rects = mNextAvailableRect;
+ mNextAvailableRect += numRects;
+ mNumAvailableRects -= numRects;
+ return rects;
+}
+
+hwc_display_contents_1* HWC2On1Adapter::Display::getDisplayContents() {
+ return mHwc1RequestedContents.get();
+}
+
+void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute,
+ int32_t value) {
+ mAttributes[attribute] = value;
+}
+
+int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const {
+ if (mAttributes.count(attribute) == 0) {
+ return -1;
+ }
+ return mAttributes.at(attribute);
+}
+
+void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id) {
+ android_color_mode_t colorMode = static_cast<android_color_mode_t>(getAttribute(ColorMode));
+ mHwc1Ids.emplace(colorMode, id);
+}
+
+bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const {
+ for (const auto& idPair : mHwc1Ids) {
+ if (id == idPair.second) {
+ return true;
+ }
+ }
+ return false;
+}
+
+Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id(
+ uint32_t id, android_color_mode_t* outMode) const {
+ for (const auto& idPair : mHwc1Ids) {
+ if (id == idPair.second) {
+ *outMode = idPair.first;
+ return Error::None;
+ }
+ }
+ ALOGE("Unable to find color mode for HWC ID %" PRIu32 " on config %u", id, mId);
+ return Error::BadParameter;
+}
+
+Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode,
+ uint32_t* outId) const {
+ for (const auto& idPair : mHwc1Ids) {
+ if (mode == idPair.first) {
+ *outId = idPair.second;
+ return Error::None;
+ }
+ }
+ ALOGE("Unable to find HWC1 ID for color mode %d on config %u", mode, mId);
+ return Error::BadParameter;
+}
+
+bool HWC2On1Adapter::Display::Config::merge(const Config& other) {
+ auto attributes = {HWC2::Attribute::Width, HWC2::Attribute::Height,
+ HWC2::Attribute::VsyncPeriod, HWC2::Attribute::DpiX,
+ HWC2::Attribute::DpiY};
+ for (auto attribute : attributes) {
+ if (getAttribute(attribute) != other.getAttribute(attribute)) {
+ return false;
+ }
+ }
+ android_color_mode_t otherColorMode =
+ static_cast<android_color_mode_t>(other.getAttribute(ColorMode));
+ if (mHwc1Ids.count(otherColorMode) != 0) {
+ ALOGE("Attempted to merge two configs (%u and %u) which appear to be "
+ "identical", mHwc1Ids.at(otherColorMode),
+ other.mHwc1Ids.at(otherColorMode));
+ return false;
+ }
+ mHwc1Ids.emplace(otherColorMode,
+ other.mHwc1Ids.at(otherColorMode));
+ return true;
+}
+
+std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const {
+ std::set<android_color_mode_t> colorModes;
+ for (const auto& idPair : mHwc1Ids) {
+ colorModes.emplace(idPair.first);
+ }
+ return colorModes;
+}
+
+std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const {
+ std::string output;
+
+ const size_t BUFFER_SIZE = 100;
+ char buffer[BUFFER_SIZE] = {};
+ auto writtenBytes = snprintf(buffer, BUFFER_SIZE,
+ "%u x %u", mAttributes.at(HWC2::Attribute::Width),
+ mAttributes.at(HWC2::Attribute::Height));
+ output.append(buffer, writtenBytes);
+
+ if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) {
+ std::memset(buffer, 0, BUFFER_SIZE);
+ writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz",
+ 1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod));
+ output.append(buffer, writtenBytes);
+ }
+
+ if (mAttributes.count(HWC2::Attribute::DpiX) != 0 &&
+ mAttributes.at(HWC2::Attribute::DpiX) != -1) {
+ std::memset(buffer, 0, BUFFER_SIZE);
+ writtenBytes = snprintf(buffer, BUFFER_SIZE,
+ ", DPI: %.1f x %.1f",
+ mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f,
+ mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f);
+ output.append(buffer, writtenBytes);
+ }
+
+ std::memset(buffer, 0, BUFFER_SIZE);
+ if (splitLine) {
+ writtenBytes = snprintf(buffer, BUFFER_SIZE,
+ "\n HWC1 ID/Color transform:");
+ } else {
+ writtenBytes = snprintf(buffer, BUFFER_SIZE,
+ ", HWC1 ID/Color transform:");
+ }
+ output.append(buffer, writtenBytes);
+
+
+ for (const auto& id : mHwc1Ids) {
+ android_color_mode_t colorMode = id.first;
+ uint32_t hwc1Id = id.second;
+ std::memset(buffer, 0, BUFFER_SIZE);
+ if (colorMode == mDisplay.mActiveColorMode) {
+ writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id,
+ colorMode);
+ } else {
+ writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id,
+ colorMode);
+ }
+ output.append(buffer, writtenBytes);
+ }
+
+ return output;
+}
+
+std::shared_ptr<const HWC2On1Adapter::Display::Config>
+ HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const {
+ if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
+ return nullptr;
+ }
+ return mConfigs[configId];
+}
+
+void HWC2On1Adapter::Display::populateColorModes() {
+ mColorModes = mConfigs[0]->getColorModes();
+ for (const auto& config : mConfigs) {
+ std::set<android_color_mode_t> intersection;
+ auto configModes = config->getColorModes();
+ std::set_intersection(mColorModes.cbegin(), mColorModes.cend(),
+ configModes.cbegin(), configModes.cend(),
+ std::inserter(intersection, intersection.begin()));
+ std::swap(intersection, mColorModes);
+ }
+}
+
+void HWC2On1Adapter::Display::initializeActiveConfig() {
+ if (mDevice.mHwc1Device->getActiveConfig == nullptr) {
+ ALOGV("getActiveConfig is null, choosing config 0");
+ mActiveConfig = mConfigs[0];
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ return;
+ }
+
+ auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
+ mDevice.mHwc1Device, mHwc1Id);
+
+ // Some devices startup without an activeConfig:
+ // We need to set one ourselves.
+ if (activeConfig == HWC_ERROR) {
+ ALOGV("There is no active configuration: Picking the first one: 0.");
+ const int defaultIndex = 0;
+ mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, defaultIndex);
+ activeConfig = defaultIndex;
+ }
+
+ for (const auto& config : mConfigs) {
+ if (config->hasHwc1Id(activeConfig)) {
+ ALOGE("Setting active config to %d for HWC1 config %u", config->getId(), activeConfig);
+ mActiveConfig = config;
+ if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) {
+ // This should never happen since we checked for the config's presence before
+ // setting it as active.
+ ALOGE("Unable to find color mode for active HWC1 config %d", config->getId());
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ }
+ break;
+ }
+ }
+ if (!mActiveConfig) {
+ ALOGV("Unable to find active HWC1 config %u, defaulting to "
+ "config 0", activeConfig);
+ mActiveConfig = mConfigs[0];
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ }
+
+
+
+
+}
+
+void HWC2On1Adapter::Display::allocateRequestedContents() {
+ // What needs to be allocated:
+ // 1 hwc_display_contents_1_t
+ // 1 hwc_layer_1_t for each layer
+ // 1 hwc_rect_t for each layer's surfaceDamage
+ // 1 hwc_rect_t for each layer's visibleRegion
+ // 1 hwc_layer_1_t for the framebuffer
+ // 1 hwc_rect_t for the framebuffer's visibleRegion
+
+ // Count # of surfaceDamage
+ size_t numSurfaceDamages = 0;
+ for (const auto& layer : mLayers) {
+ numSurfaceDamages += layer->getNumSurfaceDamages();
+ }
+
+ // Count # of visibleRegions (start at 1 for mandatory framebuffer target
+ // region)
+ size_t numVisibleRegion = 1;
+ for (const auto& layer : mLayers) {
+ numVisibleRegion += layer->getNumVisibleRegions();
+ }
+
+ size_t numRects = numVisibleRegion + numSurfaceDamages;
+ auto numLayers = mLayers.size() + 1;
+ size_t size = sizeof(hwc_display_contents_1_t) +
+ sizeof(hwc_layer_1_t) * numLayers +
+ sizeof(hwc_rect_t) * numRects;
+ auto contents = static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1));
+ mHwc1RequestedContents.reset(contents);
+ mNextAvailableRect = reinterpret_cast<hwc_rect_t*>(&contents->hwLayers[numLayers]);
+ mNumAvailableRects = numRects;
+}
+
+void HWC2On1Adapter::Display::assignHwc1LayerIds() {
+ mHwc1LayerMap.clear();
+ size_t nextHwc1Id = 0;
+ for (auto& layer : mLayers) {
+ mHwc1LayerMap[nextHwc1Id] = layer;
+ layer->setHwc1Id(nextHwc1Id++);
+ }
+}
+
+void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer,
+ const Layer& layer) {
+ auto layerId = layer.getId();
+ switch (hwc1Layer.compositionType) {
+ case HWC_FRAMEBUFFER:
+ if (layer.getCompositionType() != Composition::Client) {
+ mChanges->addTypeChange(layerId, Composition::Client);
+ }
+ break;
+ case HWC_OVERLAY:
+ if (layer.getCompositionType() != Composition::Device) {
+ mChanges->addTypeChange(layerId, Composition::Device);
+ }
+ break;
+ case HWC_BACKGROUND:
+ ALOGE_IF(layer.getCompositionType() != Composition::SolidColor,
+ "updateTypeChanges: HWC1 requested BACKGROUND, but HWC2"
+ " wasn't expecting SolidColor");
+ break;
+ case HWC_FRAMEBUFFER_TARGET:
+ // Do nothing, since it shouldn't be modified by HWC1
+ break;
+ case HWC_SIDEBAND:
+ ALOGE_IF(layer.getCompositionType() != Composition::Sideband,
+ "updateTypeChanges: HWC1 requested SIDEBAND, but HWC2"
+ " wasn't expecting Sideband");
+ break;
+ case HWC_CURSOR_OVERLAY:
+ ALOGE_IF(layer.getCompositionType() != Composition::Cursor,
+ "updateTypeChanges: HWC1 requested CURSOR_OVERLAY, but"
+ " HWC2 wasn't expecting Cursor");
+ break;
+ }
+}
+
+void HWC2On1Adapter::Display::updateLayerRequests(
+ const hwc_layer_1_t& hwc1Layer, const Layer& layer) {
+ if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) {
+ mChanges->addLayerRequest(layer.getId(),
+ LayerRequest::ClearClientTarget);
+ }
+}
+
+void HWC2On1Adapter::Display::prepareFramebufferTarget() {
+ // We check that mActiveConfig is valid in Display::prepare
+ int32_t width = mActiveConfig->getAttribute(Attribute::Width);
+ int32_t height = mActiveConfig->getAttribute(Attribute::Height);
+
+ auto& hwc1Target = mHwc1RequestedContents->hwLayers[mLayers.size()];
+ hwc1Target.compositionType = HWC_FRAMEBUFFER_TARGET;
+ hwc1Target.releaseFenceFd = -1;
+ hwc1Target.hints = 0;
+ hwc1Target.flags = 0;
+ hwc1Target.transform = 0;
+ hwc1Target.blending = HWC_BLENDING_PREMULT;
+ if (mDevice.getHwc1MinorVersion() < 3) {
+ hwc1Target.sourceCropi = {0, 0, width, height};
+ } else {
+ hwc1Target.sourceCropf = {0.0f, 0.0f, static_cast<float>(width),
+ static_cast<float>(height)};
+ }
+ hwc1Target.displayFrame = {0, 0, width, height};
+ hwc1Target.planeAlpha = 255;
+
+ hwc1Target.visibleRegionScreen.numRects = 1;
+ hwc_rect_t* rects = GetRects(1);
+ rects[0].left = 0;
+ rects[0].top = 0;
+ rects[0].right = width;
+ rects[0].bottom = height;
+ hwc1Target.visibleRegionScreen.rects = rects;
+
+ // We will set this to the correct value in set
+ hwc1Target.acquireFenceFd = -1;
+}
+
+// Layer functions
+
+std::atomic<hwc2_layer_t> HWC2On1Adapter::Layer::sNextId(1);
+
+HWC2On1Adapter::Layer::Layer(Display& display)
+ : mId(sNextId++),
+ mDisplay(display),
+ mBuffer(),
+ mSurfaceDamage(),
+ mBlendMode(BlendMode::None),
+ mColor({0, 0, 0, 0}),
+ mCompositionType(Composition::Invalid),
+ mDisplayFrame({0, 0, -1, -1}),
+ mPlaneAlpha(0.0f),
+ mSidebandStream(nullptr),
+ mSourceCrop({0.0f, 0.0f, -1.0f, -1.0f}),
+ mTransform(Transform::None),
+ mVisibleRegion(),
+ mZ(0),
+ mReleaseFence(),
+ mHwc1Id(0),
+ mHasUnsupportedPlaneAlpha(false) {}
+
+bool HWC2On1Adapter::SortLayersByZ::operator()(
+ const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs) {
+ return lhs->getZ() < rhs->getZ();
+}
+
+Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer,
+ int32_t acquireFence) {
+ ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId);
+ mBuffer.setBuffer(buffer);
+ mBuffer.setFence(acquireFence);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y) {
+ if (mCompositionType != Composition::Cursor) {
+ return Error::BadLayer;
+ }
+
+ if (mDisplay.hasChanges()) {
+ return Error::NotValidated;
+ }
+
+ auto displayId = mDisplay.getHwc1Id();
+ auto hwc1Device = mDisplay.getDevice().getHwc1Device();
+ hwc1Device->setCursorPositionAsync(hwc1Device, displayId, x, y);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage) {
+ // HWC1 supports surface damage starting only with version 1.5.
+ if (mDisplay.getDevice().mHwc1MinorVersion < 5) {
+ return Error::None;
+ }
+ mSurfaceDamage.resize(damage.numRects);
+ std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin());
+ return Error::None;
+}
+
+// Layer state functions
+
+Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode) {
+ mBlendMode = mode;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setColor(hwc_color_t color) {
+ mColor = color;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setCompositionType(Composition type) {
+ mCompositionType = type;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t) {
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame) {
+ mDisplayFrame = frame;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha) {
+ mPlaneAlpha = alpha;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream) {
+ mSidebandStream = stream;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop) {
+ mSourceCrop = crop;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setTransform(Transform transform) {
+ mTransform = transform;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) {
+ mVisibleRegion.resize(visible.numRects);
+ std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin());
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setZ(uint32_t z) {
+ mZ = z;
+ return Error::None;
+}
+
+void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd) {
+ ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId);
+ mReleaseFence.add(fenceFd);
+}
+
+const sp<MiniFence>& HWC2On1Adapter::Layer::getReleaseFence() const {
+ return mReleaseFence.get();
+}
+
+void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer) {
+ applyCommonState(hwc1Layer);
+ applyCompositionType(hwc1Layer);
+ switch (mCompositionType) {
+ case Composition::SolidColor : applySolidColorState(hwc1Layer); break;
+ case Composition::Sideband : applySidebandState(hwc1Layer); break;
+ default: applyBufferState(hwc1Layer); break;
+ }
+}
+
+static std::string regionStrings(const std::vector<hwc_rect_t>& visibleRegion,
+ const std::vector<hwc_rect_t>& surfaceDamage) {
+ std::string regions;
+ regions += " Visible Region";
+ regions.resize(40, ' ');
+ regions += "Surface Damage\n";
+
+ size_t numPrinted = 0;
+ size_t maxSize = std::max(visibleRegion.size(), surfaceDamage.size());
+ while (numPrinted < maxSize) {
+ std::string line(" ");
+ if (visibleRegion.empty() && numPrinted == 0) {
+ line += "None";
+ } else if (numPrinted < visibleRegion.size()) {
+ line += rectString(visibleRegion[numPrinted]);
+ }
+ line.resize(40, ' ');
+ if (surfaceDamage.empty() && numPrinted == 0) {
+ line += "None";
+ } else if (numPrinted < surfaceDamage.size()) {
+ line += rectString(surfaceDamage[numPrinted]);
+ }
+ line += '\n';
+ regions += line;
+ ++numPrinted;
+ }
+ return regions;
+}
+
+std::string HWC2On1Adapter::Layer::dump() const {
+ std::stringstream output;
+ const char* fill = " ";
+
+ output << fill << to_string(mCompositionType);
+ output << " Layer HWC2/1: " << mId << "/" << mHwc1Id << " ";
+ output << "Z: " << mZ;
+ if (mCompositionType == HWC2::Composition::SolidColor) {
+ output << " " << colorString(mColor);
+ } else if (mCompositionType == HWC2::Composition::Sideband) {
+ output << " Handle: " << mSidebandStream << '\n';
+ } else {
+ output << " Buffer: " << mBuffer.getBuffer() << "/" <<
+ mBuffer.getFence() << '\n';
+ output << fill << " Display frame [LTRB]: " <<
+ rectString(mDisplayFrame) << '\n';
+ output << fill << " Source crop: " <<
+ frectString(mSourceCrop) << '\n';
+ output << fill << " Transform: " << to_string(mTransform);
+ output << " Blend mode: " << to_string(mBlendMode);
+ if (mPlaneAlpha != 1.0f) {
+ output << " Alpha: " <<
+ alphaString(mPlaneAlpha) << '\n';
+ } else {
+ output << '\n';
+ }
+ output << regionStrings(mVisibleRegion, mSurfaceDamage);
+ }
+ return output.str();
+}
+
+static int getHwc1Blending(HWC2::BlendMode blendMode) {
+ switch (blendMode) {
+ case BlendMode::Coverage: return HWC_BLENDING_COVERAGE;
+ case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT;
+ default: return HWC_BLENDING_NONE;
+ }
+}
+
+void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer) {
+ auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion();
+ hwc1Layer.blending = getHwc1Blending(mBlendMode);
+ hwc1Layer.displayFrame = mDisplayFrame;
+
+ auto pendingAlpha = mPlaneAlpha;
+ if (minorVersion < 2) {
+ mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f;
+ } else {
+ hwc1Layer.planeAlpha =
+ static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f);
+ }
+
+ if (minorVersion < 3) {
+ auto pending = mSourceCrop;
+ hwc1Layer.sourceCropi.left =
+ static_cast<int32_t>(std::ceil(pending.left));
+ hwc1Layer.sourceCropi.top =
+ static_cast<int32_t>(std::ceil(pending.top));
+ hwc1Layer.sourceCropi.right =
+ static_cast<int32_t>(std::floor(pending.right));
+ hwc1Layer.sourceCropi.bottom =
+ static_cast<int32_t>(std::floor(pending.bottom));
+ } else {
+ hwc1Layer.sourceCropf = mSourceCrop;
+ }
+
+ hwc1Layer.transform = static_cast<uint32_t>(mTransform);
+
+ auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen;
+ hwc1VisibleRegion.numRects = mVisibleRegion.size();
+ hwc_rect_t* rects = mDisplay.GetRects(hwc1VisibleRegion.numRects);
+ hwc1VisibleRegion.rects = rects;
+ for (size_t i = 0; i < mVisibleRegion.size(); i++) {
+ rects[i] = mVisibleRegion[i];
+ }
+}
+
+void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer) {
+ // If the device does not support background color it is likely to make
+ // assumption regarding backgroundColor and handle (both fields occupy
+ // the same location in hwc_layer_1_t union).
+ // To not confuse these devices we don't set background color and we
+ // make sure handle is a null pointer.
+ if (hasUnsupportedBackgroundColor()) {
+ hwc1Layer.handle = nullptr;
+ } else {
+ hwc1Layer.backgroundColor = mColor;
+ }
+}
+
+void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer) {
+ hwc1Layer.sidebandStream = mSidebandStream;
+}
+
+void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer) {
+ hwc1Layer.handle = mBuffer.getBuffer();
+ hwc1Layer.acquireFenceFd = mBuffer.getFence();
+}
+
+void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer) {
+ // HWC1 never supports color transforms or dataspaces and only sometimes
+ // supports plane alpha (depending on the version). These require us to drop
+ // some or all layers to client composition.
+ if (mHasUnsupportedPlaneAlpha || mDisplay.hasColorTransform() ||
+ hasUnsupportedBackgroundColor()) {
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags = HWC_SKIP_LAYER;
+ return;
+ }
+
+ hwc1Layer.flags = 0;
+ switch (mCompositionType) {
+ case Composition::Client:
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags |= HWC_SKIP_LAYER;
+ break;
+ case Composition::Device:
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ break;
+ case Composition::SolidColor:
+ // In theory the following line should work, but since the HWC1
+ // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1
+ // devices may not work correctly. To be on the safe side, we
+ // fall back to client composition.
+ //
+ // hwc1Layer.compositionType = HWC_BACKGROUND;
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags |= HWC_SKIP_LAYER;
+ break;
+ case Composition::Cursor:
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) {
+ hwc1Layer.hints |= HWC_IS_CURSOR_LAYER;
+ }
+ break;
+ case Composition::Sideband:
+ if (mDisplay.getDevice().getHwc1MinorVersion() < 4) {
+ hwc1Layer.compositionType = HWC_SIDEBAND;
+ } else {
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags |= HWC_SKIP_LAYER;
+ }
+ break;
+ default:
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags |= HWC_SKIP_LAYER;
+ break;
+ }
+ ALOGV("Layer %" PRIu64 " %s set to %d", mId,
+ to_string(mCompositionType).c_str(),
+ hwc1Layer.compositionType);
+ ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, " and skipping");
+}
+
+// Adapter helpers
+
+void HWC2On1Adapter::populateCapabilities() {
+ if (mHwc1MinorVersion >= 3U) {
+ int supportedTypes = 0;
+ auto result = mHwc1Device->query(mHwc1Device,
+ HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes);
+ if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) {
+ ALOGI("Found support for HWC virtual displays");
+ mHwc1SupportsVirtualDisplays = true;
+ }
+ }
+ if (mHwc1MinorVersion >= 4U) {
+ mCapabilities.insert(Capability::SidebandStream);
+ }
+
+ // Check for HWC background color layer support.
+ if (mHwc1MinorVersion >= 1U) {
+ int backgroundColorSupported = 0;
+ auto result = mHwc1Device->query(mHwc1Device,
+ HWC_BACKGROUND_LAYER_SUPPORTED,
+ &backgroundColorSupported);
+ if ((result == 0) && (backgroundColorSupported == 1)) {
+ ALOGV("Found support for HWC background color");
+ mHwc1SupportsBackgroundColor = true;
+ }
+ }
+
+ // Some devices might have HWC1 retire fences that accurately emulate
+ // HWC2 present fences when they are deferred, but it's not very reliable.
+ // To be safe, we indicate PresentFenceIsNotReliable for all HWC1 devices.
+ mCapabilities.insert(Capability::PresentFenceIsNotReliable);
+}
+
+HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id) {
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ auto display = mDisplays.find(id);
+ if (display == mDisplays.end()) {
+ return nullptr;
+ }
+
+ return display->second.get();
+}
+
+std::tuple<HWC2On1Adapter::Layer*, Error> HWC2On1Adapter::getLayer(
+ hwc2_display_t displayId, hwc2_layer_t layerId) {
+ auto display = getDisplay(displayId);
+ if (!display) {
+ return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay);
+ }
+
+ auto layerEntry = mLayers.find(layerId);
+ if (layerEntry == mLayers.end()) {
+ return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
+ }
+
+ auto layer = layerEntry->second;
+ if (layer->getDisplay().getId() != displayId) {
+ return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
+ }
+ return std::make_tuple(layer.get(), Error::None);
+}
+
+void HWC2On1Adapter::populatePrimary() {
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ auto display = std::make_shared<Display>(*this, HWC2::DisplayType::Physical);
+ mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId();
+ display->setHwc1Id(HWC_DISPLAY_PRIMARY);
+ display->populateConfigs();
+ mDisplays.emplace(display->getId(), std::move(display));
+}
+
+bool HWC2On1Adapter::prepareAllDisplays() {
+ ATRACE_CALL();
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ for (const auto& displayPair : mDisplays) {
+ auto& display = displayPair.second;
+ if (!display->prepare()) {
+ return false;
+ }
+ }
+
+ if (mHwc1DisplayMap.count(HWC_DISPLAY_PRIMARY) == 0) {
+ ALOGE("prepareAllDisplays: Unable to find primary HWC1 display");
+ return false;
+ }
+
+ // Build an array of hwc_display_contents_1 to call prepare() on HWC1.
+ mHwc1Contents.clear();
+
+ // Always push the primary display
+ auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY];
+ auto& primaryDisplay = mDisplays[primaryDisplayId];
+ mHwc1Contents.push_back(primaryDisplay->getDisplayContents());
+
+ // Push the external display, if present
+ if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) {
+ auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL];
+ auto& externalDisplay = mDisplays[externalDisplayId];
+ mHwc1Contents.push_back(externalDisplay->getDisplayContents());
+ } else {
+ // Even if an external display isn't present, we still need to send
+ // at least two displays down to HWC1
+ mHwc1Contents.push_back(nullptr);
+ }
+
+ // Push the hardware virtual display, if supported and present
+ if (mHwc1MinorVersion >= 3) {
+ if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) {
+ auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL];
+ auto& virtualDisplay = mDisplays[virtualDisplayId];
+ mHwc1Contents.push_back(virtualDisplay->getDisplayContents());
+ } else {
+ mHwc1Contents.push_back(nullptr);
+ }
+ }
+
+ for (auto& displayContents : mHwc1Contents) {
+ if (!displayContents) {
+ continue;
+ }
+
+ ALOGV("Display %zd layers:", mHwc1Contents.size() - 1);
+ for (size_t l = 0; l < displayContents->numHwLayers; ++l) {
+ auto& layer = displayContents->hwLayers[l];
+ ALOGV(" %zd: %d", l, layer.compositionType);
+ }
+ }
+
+ ALOGV("Calling HWC1 prepare");
+ {
+ ATRACE_NAME("HWC1 prepare");
+ mHwc1Device->prepare(mHwc1Device, mHwc1Contents.size(),
+ mHwc1Contents.data());
+ }
+
+ for (size_t c = 0; c < mHwc1Contents.size(); ++c) {
+ auto& contents = mHwc1Contents[c];
+ if (!contents) {
+ continue;
+ }
+ ALOGV("Display %zd layers:", c);
+ for (size_t l = 0; l < contents->numHwLayers; ++l) {
+ ALOGV(" %zd: %d", l, contents->hwLayers[l].compositionType);
+ }
+ }
+
+ // Return the received contents to their respective displays
+ for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+ if (mHwc1Contents[hwc1Id] == nullptr) {
+ continue;
+ }
+
+ auto displayId = mHwc1DisplayMap[hwc1Id];
+ auto& display = mDisplays[displayId];
+ display->generateChanges();
+ }
+
+ return true;
+}
+
+void dumpHWC1Message(hwc_composer_device_1* device, size_t numDisplays,
+ hwc_display_contents_1_t** displays) {
+ ALOGV("*****************************");
+ size_t displayId = 0;
+ while (displayId < numDisplays) {
+ hwc_display_contents_1_t* display = displays[displayId];
+
+ ALOGV("hwc_display_contents_1_t[%zu] @0x%p", displayId, display);
+ if (display == nullptr) {
+ displayId++;
+ continue;
+ }
+ ALOGV(" retirefd:0x%08x", display->retireFenceFd);
+ ALOGV(" outbuf :0x%p", display->outbuf);
+ ALOGV(" outbuffd:0x%08x", display->outbufAcquireFenceFd);
+ ALOGV(" flags :0x%08x", display->flags);
+ for(size_t layerId=0 ; layerId < display->numHwLayers ; layerId++) {
+ hwc_layer_1_t& layer = display->hwLayers[layerId];
+ ALOGV(" Layer[%zu]:", layerId);
+ ALOGV(" composition : 0x%08x", layer.compositionType);
+ ALOGV(" hints : 0x%08x", layer.hints);
+ ALOGV(" flags : 0x%08x", layer.flags);
+ ALOGV(" handle : 0x%p", layer.handle);
+ ALOGV(" transform : 0x%08x", layer.transform);
+ ALOGV(" blending : 0x%08x", layer.blending);
+ ALOGV(" sourceCropf : %f, %f, %f, %f",
+ layer.sourceCropf.left,
+ layer.sourceCropf.top,
+ layer.sourceCropf.right,
+ layer.sourceCropf.bottom);
+ ALOGV(" displayFrame : %d, %d, %d, %d",
+ layer.displayFrame.left,
+ layer.displayFrame.left,
+ layer.displayFrame.left,
+ layer.displayFrame.left);
+ hwc_region_t& visReg = layer.visibleRegionScreen;
+ ALOGV(" visibleRegionScreen: #0x%08zx[@0x%p]",
+ visReg.numRects,
+ visReg.rects);
+ for (size_t visRegId=0; visRegId < visReg.numRects ; visRegId++) {
+ if (layer.visibleRegionScreen.rects == nullptr) {
+ ALOGV(" null");
+ } else {
+ ALOGV(" visibleRegionScreen[%zu] %d, %d, %d, %d",
+ visRegId,
+ visReg.rects[visRegId].left,
+ visReg.rects[visRegId].top,
+ visReg.rects[visRegId].right,
+ visReg.rects[visRegId].bottom);
+ }
+ }
+ ALOGV(" acquireFenceFd : 0x%08x", layer.acquireFenceFd);
+ ALOGV(" releaseFenceFd : 0x%08x", layer.releaseFenceFd);
+ ALOGV(" planeAlpha : 0x%08x", layer.planeAlpha);
+ if (getMinorVersion(device) < 5)
+ continue;
+ ALOGV(" surfaceDamage : #0x%08zx[@0x%p]",
+ layer.surfaceDamage.numRects,
+ layer.surfaceDamage.rects);
+ for (size_t sdId=0; sdId < layer.surfaceDamage.numRects ; sdId++) {
+ if (layer.surfaceDamage.rects == nullptr) {
+ ALOGV(" null");
+ } else {
+ ALOGV(" surfaceDamage[%zu] %d, %d, %d, %d",
+ sdId,
+ layer.surfaceDamage.rects[sdId].left,
+ layer.surfaceDamage.rects[sdId].top,
+ layer.surfaceDamage.rects[sdId].right,
+ layer.surfaceDamage.rects[sdId].bottom);
+ }
+ }
+ }
+ displayId++;
+ }
+ ALOGV("-----------------------------");
+}
+
+Error HWC2On1Adapter::setAllDisplays() {
+ ATRACE_CALL();
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ // Make sure we're ready to validate
+ for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+ if (mHwc1Contents[hwc1Id] == nullptr) {
+ continue;
+ }
+
+ auto displayId = mHwc1DisplayMap[hwc1Id];
+ auto& display = mDisplays[displayId];
+ Error error = display->set(*mHwc1Contents[hwc1Id]);
+ if (error != Error::None) {
+ ALOGE("setAllDisplays: Failed to set display %zd: %s", hwc1Id,
+ to_string(error).c_str());
+ return error;
+ }
+ }
+
+ ALOGV("Calling HWC1 set");
+ {
+ ATRACE_NAME("HWC1 set");
+ //dumpHWC1Message(mHwc1Device, mHwc1Contents.size(), mHwc1Contents.data());
+ mHwc1Device->set(mHwc1Device, mHwc1Contents.size(),
+ mHwc1Contents.data());
+ }
+
+ // Add retire and release fences
+ for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+ if (mHwc1Contents[hwc1Id] == nullptr) {
+ continue;
+ }
+
+ auto displayId = mHwc1DisplayMap[hwc1Id];
+ auto& display = mDisplays[displayId];
+ auto retireFenceFd = mHwc1Contents[hwc1Id]->retireFenceFd;
+ ALOGV("setAllDisplays: Adding retire fence %d to display %zd",
+ retireFenceFd, hwc1Id);
+ display->addRetireFence(mHwc1Contents[hwc1Id]->retireFenceFd);
+ display->addReleaseFences(*mHwc1Contents[hwc1Id]);
+ }
+
+ return Error::None;
+}
+
+void HWC2On1Adapter::hwc1Invalidate() {
+ ALOGV("Received hwc1Invalidate");
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ // If the HWC2-side callback hasn't been registered yet, buffer this until
+ // it is registered.
+ if (mCallbacks.count(Callback::Refresh) == 0) {
+ mHasPendingInvalidate = true;
+ return;
+ }
+
+ const auto& callbackInfo = mCallbacks[Callback::Refresh];
+ std::vector<hwc2_display_t> displays;
+ for (const auto& displayPair : mDisplays) {
+ displays.emplace_back(displayPair.first);
+ }
+
+ // Call back without the state lock held.
+ lock.unlock();
+
+ auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(callbackInfo.pointer);
+ for (auto display : displays) {
+ refresh(callbackInfo.data, display);
+ }
+}
+
+void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp) {
+ ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp);
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ // If the HWC2-side callback hasn't been registered yet, buffer this until
+ // it is registered.
+ if (mCallbacks.count(Callback::Vsync) == 0) {
+ mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp);
+ return;
+ }
+
+ if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+ ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId);
+ return;
+ }
+
+ const auto& callbackInfo = mCallbacks[Callback::Vsync];
+ auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+
+ // Call back without the state lock held.
+ lock.unlock();
+
+ auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer);
+ vsync(callbackInfo.data, displayId, timestamp);
+}
+
+void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) {
+ ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected);
+
+ if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) {
+ ALOGE("hwc1Hotplug: Received hotplug for non-external display");
+ return;
+ }
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ // If the HWC2-side callback hasn't been registered yet, buffer this until
+ // it is registered
+ if (mCallbacks.count(Callback::Hotplug) == 0) {
+ mPendingHotplugs.emplace_back(hwc1DisplayId, connected);
+ return;
+ }
+
+ hwc2_display_t displayId = UINT64_MAX;
+ if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+ if (connected == 0) {
+ ALOGW("hwc1Hotplug: Received disconnect for unconnected display");
+ return;
+ }
+
+ // Create a new display on connect
+ auto display = std::make_shared<HWC2On1Adapter::Display>(*this,
+ HWC2::DisplayType::Physical);
+ display->setHwc1Id(HWC_DISPLAY_EXTERNAL);
+ display->populateConfigs();
+ displayId = display->getId();
+ mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId;
+ mDisplays.emplace(displayId, std::move(display));
+ } else {
+ if (connected != 0) {
+ ALOGW("hwc1Hotplug: Received connect for previously connected "
+ "display");
+ return;
+ }
+
+ // Disconnect an existing display
+ displayId = mHwc1DisplayMap[hwc1DisplayId];
+ mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL);
+ mDisplays.erase(displayId);
+ }
+
+ const auto& callbackInfo = mCallbacks[Callback::Hotplug];
+
+ // Call back without the state lock held
+ lock.unlock();
+
+ auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer);
+ auto hwc2Connected = (connected == 0) ?
+ HWC2::Connection::Disconnected : HWC2::Connection::Connected;
+ hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected));
+}
+} // namespace android
diff --git a/libs/hwc2on1adapter/MiniFence.cpp b/libs/hwc2on1adapter/MiniFence.cpp
new file mode 100644
index 0000000..dfbe4d6
--- /dev/null
+++ b/libs/hwc2on1adapter/MiniFence.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "hwc2on1adapter/MiniFence.h"
+
+#include <unistd.h>
+
+namespace android {
+
+const sp<MiniFence> MiniFence::NO_FENCE = sp<MiniFence>(new MiniFence);
+
+MiniFence::MiniFence() :
+ mFenceFd(-1) {
+}
+
+MiniFence::MiniFence(int fenceFd) :
+ mFenceFd(fenceFd) {
+}
+
+MiniFence::~MiniFence() {
+ if (mFenceFd != -1) {
+ close(mFenceFd);
+ }
+}
+
+int MiniFence::dup() const {
+ return ::dup(mFenceFd);
+}
+}
diff --git a/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h b/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h
new file mode 100644
index 0000000..3badfce
--- /dev/null
+++ b/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h
@@ -0,0 +1,738 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_HWC2_ON_1_ADAPTER_H
+#define ANDROID_SF_HWC2_ON_1_ADAPTER_H
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "MiniFence.h"
+
+#include <atomic>
+#include <map>
+#include <mutex>
+#include <queue>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+struct hwc_composer_device_1;
+struct hwc_display_contents_1;
+struct hwc_layer_1;
+
+namespace android {
+
+// For devices unable to provide an implementation of HWC2 (see hwcomposer2.h),
+// we provide an adapter able to talk to HWC1 (see hwcomposer.h). It translates
+// streamed function calls ala HWC2 model to batched array of structs calls ala
+// HWC1 model.
+class HWC2On1Adapter : public hwc2_device_t
+{
+public:
+ explicit HWC2On1Adapter(struct hwc_composer_device_1* hwc1Device);
+ ~HWC2On1Adapter();
+
+ struct hwc_composer_device_1* getHwc1Device() const { return mHwc1Device; }
+ uint8_t getHwc1MinorVersion() const { return mHwc1MinorVersion; }
+
+private:
+ static inline HWC2On1Adapter* getAdapter(hwc2_device_t* device) {
+ return static_cast<HWC2On1Adapter*>(device);
+ }
+
+ // getCapabilities
+
+ void doGetCapabilities(uint32_t* outCount,
+ int32_t* /*hwc2_capability_t*/ outCapabilities);
+ static void getCapabilitiesHook(hwc2_device_t* device, uint32_t* outCount,
+ int32_t* /*hwc2_capability_t*/ outCapabilities) {
+ getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
+ }
+
+ bool supportsBackgroundColor() {
+ return mHwc1SupportsBackgroundColor;
+ }
+
+ // getFunction
+
+ hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor);
+ static hwc2_function_pointer_t getFunctionHook(hwc2_device_t* device,
+ int32_t intDesc) {
+ auto descriptor = static_cast<HWC2::FunctionDescriptor>(intDesc);
+ return getAdapter(device)->doGetFunction(descriptor);
+ }
+
+ // Device functions
+
+ HWC2::Error createVirtualDisplay(uint32_t width, uint32_t height,
+ hwc2_display_t* outDisplay);
+ static int32_t createVirtualDisplayHook(hwc2_device_t* device,
+ uint32_t width, uint32_t height, int32_t* /*format*/,
+ hwc2_display_t* outDisplay) {
+ // HWC1 implementations cannot override the buffer format requested by
+ // the consumer
+ auto error = getAdapter(device)->createVirtualDisplay(width, height,
+ outDisplay);
+ return static_cast<int32_t>(error);
+ }
+
+ HWC2::Error destroyVirtualDisplay(hwc2_display_t display);
+ static int32_t destroyVirtualDisplayHook(hwc2_device_t* device,
+ hwc2_display_t display) {
+ auto error = getAdapter(device)->destroyVirtualDisplay(display);
+ return static_cast<int32_t>(error);
+ }
+
+ std::string mDumpString;
+ void dump(uint32_t* outSize, char* outBuffer);
+ static void dumpHook(hwc2_device_t* device, uint32_t* outSize,
+ char* outBuffer) {
+ getAdapter(device)->dump(outSize, outBuffer);
+ }
+
+ uint32_t getMaxVirtualDisplayCount();
+ static uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* device) {
+ return getAdapter(device)->getMaxVirtualDisplayCount();
+ }
+
+ HWC2::Error registerCallback(HWC2::Callback descriptor,
+ hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer);
+ static int32_t registerCallbackHook(hwc2_device_t* device,
+ int32_t intDesc, hwc2_callback_data_t callbackData,
+ hwc2_function_pointer_t pointer) {
+ auto descriptor = static_cast<HWC2::Callback>(intDesc);
+ auto error = getAdapter(device)->registerCallback(descriptor,
+ callbackData, pointer);
+ return static_cast<int32_t>(error);
+ }
+
+ // Display functions
+
+ class Layer;
+
+ class SortLayersByZ {
+ public:
+ bool operator()(const std::shared_ptr<Layer>& lhs,
+ const std::shared_ptr<Layer>& rhs);
+ };
+
+ // The semantics of the fences returned by the device differ between
+ // hwc1.set() and hwc2.present(). Read hwcomposer.h and hwcomposer2.h
+ // for more information.
+ //
+ // Release fences in hwc1 are obtained on set() for a frame n and signaled
+ // when the layer buffer is not needed for read operations anymore
+ // (typically on frame n+1). In HWC2, release fences are obtained with a
+ // special call after present() for frame n. These fences signal
+ // on frame n: More specifically, the fence for a given buffer provided in
+ // frame n will signal when the prior buffer is no longer required.
+ //
+ // A retire fence (HWC1) is signaled when a composition is replaced
+ // on the panel whereas a present fence (HWC2) is signaled when a
+ // composition starts to be displayed on a panel.
+ //
+ // The HWC2to1Adapter emulates the new fence semantics for a frame
+ // n by returning the fence from frame n-1. For frame 0, the adapter
+ // returns NO_FENCE.
+ class DeferredFence {
+ public:
+ DeferredFence()
+ : mFences({MiniFence::NO_FENCE, MiniFence::NO_FENCE}) {}
+
+ void add(int32_t fenceFd) {
+ mFences.emplace(new MiniFence(fenceFd));
+ mFences.pop();
+ }
+
+ const sp<MiniFence>& get() const {
+ return mFences.front();
+ }
+
+ private:
+ // There are always two fences in this queue.
+ std::queue<sp<MiniFence>> mFences;
+ };
+
+ class FencedBuffer {
+ public:
+ FencedBuffer() : mBuffer(nullptr), mFence(MiniFence::NO_FENCE) {}
+
+ void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; }
+ void setFence(int fenceFd) { mFence = new MiniFence(fenceFd); }
+
+ buffer_handle_t getBuffer() const { return mBuffer; }
+ int getFence() const { return mFence->dup(); }
+
+ private:
+ buffer_handle_t mBuffer;
+ sp<MiniFence> mFence;
+ };
+
+ class Display {
+ public:
+ Display(HWC2On1Adapter& device, HWC2::DisplayType type);
+
+ hwc2_display_t getId() const { return mId; }
+ HWC2On1Adapter& getDevice() const { return mDevice; }
+
+ // Does not require locking because it is set before adding the
+ // Displays to the Adapter's list of displays
+ void setHwc1Id(int32_t id) { mHwc1Id = id; }
+ int32_t getHwc1Id() const { return mHwc1Id; }
+
+ // HWC2 Display functions
+ HWC2::Error acceptChanges();
+ HWC2::Error createLayer(hwc2_layer_t* outLayerId);
+ HWC2::Error destroyLayer(hwc2_layer_t layerId);
+ HWC2::Error getActiveConfig(hwc2_config_t* outConfigId);
+ HWC2::Error getAttribute(hwc2_config_t configId,
+ HWC2::Attribute attribute, int32_t* outValue);
+ HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements,
+ hwc2_layer_t* outLayers, int32_t* outTypes);
+ HWC2::Error getColorModes(uint32_t* outNumModes, int32_t* outModes);
+ HWC2::Error getConfigs(uint32_t* outNumConfigs,
+ hwc2_config_t* outConfigIds);
+ HWC2::Error getDozeSupport(int32_t* outSupport);
+ HWC2::Error getHdrCapabilities(uint32_t* outNumTypes,
+ int32_t* outTypes, float* outMaxLuminance,
+ float* outMaxAverageLuminance, float* outMinLuminance);
+ HWC2::Error getName(uint32_t* outSize, char* outName);
+ HWC2::Error getReleaseFences(uint32_t* outNumElements,
+ hwc2_layer_t* outLayers, int32_t* outFences);
+ HWC2::Error getRequests(int32_t* outDisplayRequests,
+ uint32_t* outNumElements, hwc2_layer_t* outLayers,
+ int32_t* outLayerRequests);
+ HWC2::Error getType(int32_t* outType);
+
+ // Since HWC1 "presents" (called "set" in HWC1) all Displays
+ // at once, the first call to any Display::present will trigger
+ // present() on all Displays in the Device. Subsequent calls without
+ // first calling validate() are noop (except for duping/returning
+ // the retire fence).
+ HWC2::Error present(int32_t* outRetireFence);
+
+ HWC2::Error setActiveConfig(hwc2_config_t configId);
+ HWC2::Error setClientTarget(buffer_handle_t target,
+ int32_t acquireFence, int32_t dataspace,
+ hwc_region_t damage);
+ HWC2::Error setColorMode(android_color_mode_t mode);
+ HWC2::Error setColorTransform(android_color_transform_t hint);
+ HWC2::Error setOutputBuffer(buffer_handle_t buffer,
+ int32_t releaseFence);
+ HWC2::Error setPowerMode(HWC2::PowerMode mode);
+ HWC2::Error setVsyncEnabled(HWC2::Vsync enabled);
+
+ // Since HWC1 "validates" (called "prepare" in HWC1) all Displays
+ // at once, the first call to any Display::validate() will trigger
+ // validate() on all other Displays in the Device.
+ HWC2::Error validate(uint32_t* outNumTypes,
+ uint32_t* outNumRequests);
+
+ HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z);
+
+ HWC2::Error getClientTargetSupport(uint32_t width, uint32_t height,
+ int32_t format, int32_t dataspace);
+
+ // Read configs from HWC1 device
+ void populateConfigs();
+
+ // Set configs for a virtual display
+ void populateConfigs(uint32_t width, uint32_t height);
+
+ bool prepare();
+
+ // Called after hwc.prepare() with responses from the device.
+ void generateChanges();
+
+ bool hasChanges() const;
+ HWC2::Error set(hwc_display_contents_1& hwcContents);
+ void addRetireFence(int fenceFd);
+ void addReleaseFences(const hwc_display_contents_1& hwcContents);
+
+ bool hasColorTransform() const;
+
+ std::string dump() const;
+
+ // Return a rect from the pool allocated during validate()
+ hwc_rect_t* GetRects(size_t numRects);
+
+ hwc_display_contents_1* getDisplayContents();
+
+ void markGeometryChanged() { mGeometryChanged = true; }
+ void resetGeometryMarker() { mGeometryChanged = false;}
+ private:
+ class Config {
+ public:
+ Config(Display& display)
+ : mDisplay(display),
+ mId(0),
+ mAttributes() {}
+
+ bool isOnDisplay(const Display& display) const {
+ return display.getId() == mDisplay.getId();
+ }
+
+ void setAttribute(HWC2::Attribute attribute, int32_t value);
+ int32_t getAttribute(HWC2::Attribute attribute) const;
+
+ void setHwc1Id(uint32_t id);
+ bool hasHwc1Id(uint32_t id) const;
+ HWC2::Error getColorModeForHwc1Id(uint32_t id,
+ android_color_mode_t *outMode) const;
+ HWC2::Error getHwc1IdForColorMode(android_color_mode_t mode,
+ uint32_t* outId) const;
+
+ void setId(hwc2_config_t id) { mId = id; }
+ hwc2_config_t getId() const { return mId; }
+
+ // Attempts to merge two configs that differ only in color
+ // mode. Returns whether the merge was successful
+ bool merge(const Config& other);
+
+ std::set<android_color_mode_t> getColorModes() const;
+
+ // splitLine divides the output into two lines suitable for
+ // dumpsys SurfaceFlinger
+ std::string toString(bool splitLine = false) const;
+
+ private:
+ Display& mDisplay;
+ hwc2_config_t mId;
+ std::unordered_map<HWC2::Attribute, int32_t> mAttributes;
+
+ // Maps from color transform to HWC1 config ID
+ std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids;
+ };
+
+ // Stores changes requested from the device upon calling prepare().
+ // Handles change request to:
+ // - Layer composition type.
+ // - Layer hints.
+ class Changes {
+ public:
+ uint32_t getNumTypes() const {
+ return static_cast<uint32_t>(mTypeChanges.size());
+ }
+
+ uint32_t getNumLayerRequests() const {
+ return static_cast<uint32_t>(mLayerRequests.size());
+ }
+
+ const std::unordered_map<hwc2_layer_t, HWC2::Composition>&
+ getTypeChanges() const {
+ return mTypeChanges;
+ }
+
+ const std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>&
+ getLayerRequests() const {
+ return mLayerRequests;
+ }
+
+ void addTypeChange(hwc2_layer_t layerId,
+ HWC2::Composition type) {
+ mTypeChanges.insert({layerId, type});
+ }
+
+ void clearTypeChanges() { mTypeChanges.clear(); }
+
+ void addLayerRequest(hwc2_layer_t layerId,
+ HWC2::LayerRequest request) {
+ mLayerRequests.insert({layerId, request});
+ }
+
+ private:
+ std::unordered_map<hwc2_layer_t, HWC2::Composition>
+ mTypeChanges;
+ std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
+ mLayerRequests;
+ };
+
+ std::shared_ptr<const Config>
+ getConfig(hwc2_config_t configId) const;
+
+ void populateColorModes();
+ void initializeActiveConfig();
+
+ // Creates a bi-directional mapping between index in HWC1
+ // prepare/set array and Layer object. Stores mapping in
+ // mHwc1LayerMap and also updates Layer's attribute mHwc1Id.
+ void assignHwc1LayerIds();
+
+ // Called after a response to prepare() has been received:
+ // Ingest composition type changes requested by the device.
+ void updateTypeChanges(const struct hwc_layer_1& hwc1Layer,
+ const Layer& layer);
+
+ // Called after a response to prepare() has been received:
+ // Ingest layer hint changes requested by the device.
+ void updateLayerRequests(const struct hwc_layer_1& hwc1Layer,
+ const Layer& layer);
+
+ // Set all fields in HWC1 comm array for layer containing the
+ // HWC_FRAMEBUFFER_TARGET (always the last layer).
+ void prepareFramebufferTarget();
+
+ // Display ID generator.
+ static std::atomic<hwc2_display_t> sNextId;
+ const hwc2_display_t mId;
+
+
+ HWC2On1Adapter& mDevice;
+
+ // The state of this display should only be modified from
+ // SurfaceFlinger's main loop, with the exception of when dump is
+ // called. To prevent a bad state from crashing us during a dump
+ // call, all public calls into Display must acquire this mutex.
+ //
+ // It is recursive because we don't want to deadlock in validate
+ // (or present) when we call HWC2On1Adapter::prepareAllDisplays
+ // (or setAllDisplays), which calls back into Display functions
+ // which require locking.
+ mutable std::recursive_mutex mStateMutex;
+
+ // Allocate RAM able to store all layers and rects used for
+ // communication with HWC1. Place allocated RAM in variable
+ // mHwc1RequestedContents.
+ void allocateRequestedContents();
+
+ // Array of structs exchanged between client and hwc1 device.
+ // Sent to device upon calling prepare().
+ std::unique_ptr<hwc_display_contents_1> mHwc1RequestedContents;
+ private:
+ DeferredFence mRetireFence;
+
+ // Will only be non-null after the Display has been validated and
+ // before it has been presented
+ std::unique_ptr<Changes> mChanges;
+
+ int32_t mHwc1Id;
+
+ std::vector<std::shared_ptr<Config>> mConfigs;
+ std::shared_ptr<const Config> mActiveConfig;
+ std::set<android_color_mode_t> mColorModes;
+ android_color_mode_t mActiveColorMode;
+ std::string mName;
+ HWC2::DisplayType mType;
+ HWC2::PowerMode mPowerMode;
+ HWC2::Vsync mVsyncEnabled;
+
+ // Used to populate HWC1 HWC_FRAMEBUFFER_TARGET layer
+ FencedBuffer mClientTarget;
+
+
+ FencedBuffer mOutputBuffer;
+
+ bool mHasColorTransform;
+
+ // All layers this Display is aware of.
+ std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers;
+
+ // Mapping between layer index in array of hwc_display_contents_1*
+ // passed to HWC1 during validate/set and Layer object.
+ std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap;
+
+ // All communication with HWC1 via prepare/set is done with one
+ // alloc. This pointer is pointing to a pool of hwc_rect_t.
+ size_t mNumAvailableRects;
+ hwc_rect_t* mNextAvailableRect;
+
+ // True if any of the Layers contained in this Display have been
+ // updated with anything other than a buffer since last call to
+ // Display::set()
+ bool mGeometryChanged;
+ };
+
+ // Utility template calling a Display object method directly based on the
+ // hwc2_display_t displayId parameter.
+ template <typename ...Args>
+ static int32_t callDisplayFunction(hwc2_device_t* device,
+ hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...),
+ Args... args) {
+ auto display = getAdapter(device)->getDisplay(displayId);
+ if (!display) {
+ return static_cast<int32_t>(HWC2::Error::BadDisplay);
+ }
+ auto error = ((*display).*member)(std::forward<Args>(args)...);
+ return static_cast<int32_t>(error);
+ }
+
+ template <typename MF, MF memFunc, typename ...Args>
+ static int32_t displayHook(hwc2_device_t* device, hwc2_display_t displayId,
+ Args... args) {
+ return HWC2On1Adapter::callDisplayFunction(device, displayId, memFunc,
+ std::forward<Args>(args)...);
+ }
+
+ static int32_t getDisplayAttributeHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_config_t config,
+ int32_t intAttribute, int32_t* outValue) {
+ auto attribute = static_cast<HWC2::Attribute>(intAttribute);
+ return callDisplayFunction(device, display, &Display::getAttribute,
+ config, attribute, outValue);
+ }
+
+ static int32_t setColorTransformHook(hwc2_device_t* device,
+ hwc2_display_t display, const float* /*matrix*/,
+ int32_t /*android_color_transform_t*/ intHint) {
+ // We intentionally throw away the matrix, because if the hint is
+ // anything other than IDENTITY, we have to fall back to client
+ // composition anyway
+ auto hint = static_cast<android_color_transform_t>(intHint);
+ return callDisplayFunction(device, display, &Display::setColorTransform,
+ hint);
+ }
+
+ static int32_t setColorModeHook(hwc2_device_t* device,
+ hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) {
+ auto mode = static_cast<android_color_mode_t>(intMode);
+ return callDisplayFunction(device, display, &Display::setColorMode,
+ mode);
+ }
+
+ static int32_t setPowerModeHook(hwc2_device_t* device,
+ hwc2_display_t display, int32_t intMode) {
+ auto mode = static_cast<HWC2::PowerMode>(intMode);
+ return callDisplayFunction(device, display, &Display::setPowerMode,
+ mode);
+ }
+
+ static int32_t setVsyncEnabledHook(hwc2_device_t* device,
+ hwc2_display_t display, int32_t intEnabled) {
+ auto enabled = static_cast<HWC2::Vsync>(intEnabled);
+ return callDisplayFunction(device, display, &Display::setVsyncEnabled,
+ enabled);
+ }
+
+ class Layer {
+ public:
+ explicit Layer(Display& display);
+
+ bool operator==(const Layer& other) { return mId == other.mId; }
+ bool operator!=(const Layer& other) { return !(*this == other); }
+
+ hwc2_layer_t getId() const { return mId; }
+ Display& getDisplay() const { return mDisplay; }
+
+ // HWC2 Layer functions
+ HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence);
+ HWC2::Error setCursorPosition(int32_t x, int32_t y);
+ HWC2::Error setSurfaceDamage(hwc_region_t damage);
+
+ // HWC2 Layer state functions
+ HWC2::Error setBlendMode(HWC2::BlendMode mode);
+ HWC2::Error setColor(hwc_color_t color);
+ HWC2::Error setCompositionType(HWC2::Composition type);
+ HWC2::Error setDataspace(android_dataspace_t dataspace);
+ HWC2::Error setDisplayFrame(hwc_rect_t frame);
+ HWC2::Error setPlaneAlpha(float alpha);
+ HWC2::Error setSidebandStream(const native_handle_t* stream);
+ HWC2::Error setSourceCrop(hwc_frect_t crop);
+ HWC2::Error setTransform(HWC2::Transform transform);
+ HWC2::Error setVisibleRegion(hwc_region_t visible);
+ HWC2::Error setZ(uint32_t z);
+
+ HWC2::Composition getCompositionType() const {
+ return mCompositionType;
+ }
+ uint32_t getZ() const { return mZ; }
+
+ void addReleaseFence(int fenceFd);
+ const sp<MiniFence>& getReleaseFence() const;
+
+ void setHwc1Id(size_t id) { mHwc1Id = id; }
+ size_t getHwc1Id() const { return mHwc1Id; }
+
+ // Write state to HWC1 communication struct.
+ void applyState(struct hwc_layer_1& hwc1Layer);
+
+ std::string dump() const;
+
+ std::size_t getNumVisibleRegions() { return mVisibleRegion.size(); }
+
+ std::size_t getNumSurfaceDamages() { return mSurfaceDamage.size(); }
+
+ // True if a layer cannot be properly rendered by the device due
+ // to usage of SolidColor (a.k.a BackgroundColor in HWC1).
+ bool hasUnsupportedBackgroundColor() {
+ return (mCompositionType == HWC2::Composition::SolidColor &&
+ !mDisplay.getDevice().supportsBackgroundColor());
+ }
+ private:
+ void applyCommonState(struct hwc_layer_1& hwc1Layer);
+ void applySolidColorState(struct hwc_layer_1& hwc1Layer);
+ void applySidebandState(struct hwc_layer_1& hwc1Layer);
+ void applyBufferState(struct hwc_layer_1& hwc1Layer);
+ void applyCompositionType(struct hwc_layer_1& hwc1Layer);
+
+ static std::atomic<hwc2_layer_t> sNextId;
+ const hwc2_layer_t mId;
+ Display& mDisplay;
+
+ FencedBuffer mBuffer;
+ std::vector<hwc_rect_t> mSurfaceDamage;
+
+ HWC2::BlendMode mBlendMode;
+ hwc_color_t mColor;
+ HWC2::Composition mCompositionType;
+ hwc_rect_t mDisplayFrame;
+ float mPlaneAlpha;
+ const native_handle_t* mSidebandStream;
+ hwc_frect_t mSourceCrop;
+ HWC2::Transform mTransform;
+ std::vector<hwc_rect_t> mVisibleRegion;
+
+ uint32_t mZ;
+
+ DeferredFence mReleaseFence;
+
+ size_t mHwc1Id;
+ bool mHasUnsupportedPlaneAlpha;
+ };
+
+ // Utility tempate calling a Layer object method based on ID parameters:
+ // hwc2_display_t displayId
+ // and
+ // hwc2_layer_t layerId
+ template <typename ...Args>
+ static int32_t callLayerFunction(hwc2_device_t* device,
+ hwc2_display_t displayId, hwc2_layer_t layerId,
+ HWC2::Error (Layer::*member)(Args...), Args... args) {
+ auto result = getAdapter(device)->getLayer(displayId, layerId);
+ auto error = std::get<HWC2::Error>(result);
+ if (error == HWC2::Error::None) {
+ auto layer = std::get<Layer*>(result);
+ error = ((*layer).*member)(std::forward<Args>(args)...);
+ }
+ return static_cast<int32_t>(error);
+ }
+
+ template <typename MF, MF memFunc, typename ...Args>
+ static int32_t layerHook(hwc2_device_t* device, hwc2_display_t displayId,
+ hwc2_layer_t layerId, Args... args) {
+ return HWC2On1Adapter::callLayerFunction(device, displayId, layerId,
+ memFunc, std::forward<Args>(args)...);
+ }
+
+ // Layer state functions
+
+ static int32_t setLayerBlendModeHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, int32_t intMode) {
+ auto mode = static_cast<HWC2::BlendMode>(intMode);
+ return callLayerFunction(device, display, layer,
+ &Layer::setBlendMode, mode);
+ }
+
+ static int32_t setLayerCompositionTypeHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, int32_t intType) {
+ auto type = static_cast<HWC2::Composition>(intType);
+ return callLayerFunction(device, display, layer,
+ &Layer::setCompositionType, type);
+ }
+
+ static int32_t setLayerDataspaceHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, int32_t intDataspace) {
+ auto dataspace = static_cast<android_dataspace_t>(intDataspace);
+ return callLayerFunction(device, display, layer, &Layer::setDataspace,
+ dataspace);
+ }
+
+ static int32_t setLayerTransformHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) {
+ auto transform = static_cast<HWC2::Transform>(intTransform);
+ return callLayerFunction(device, display, layer, &Layer::setTransform,
+ transform);
+ }
+
+ static int32_t setLayerZOrderHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, uint32_t z) {
+ return callDisplayFunction(device, display, &Display::updateLayerZ,
+ layer, z);
+ }
+
+ // Adapter internals
+
+ void populateCapabilities();
+ Display* getDisplay(hwc2_display_t id);
+ std::tuple<Layer*, HWC2::Error> getLayer(hwc2_display_t displayId,
+ hwc2_layer_t layerId);
+ void populatePrimary();
+
+ bool prepareAllDisplays();
+ std::vector<struct hwc_display_contents_1*> mHwc1Contents;
+ HWC2::Error setAllDisplays();
+
+ // Callbacks
+ void hwc1Invalidate();
+ void hwc1Vsync(int hwc1DisplayId, int64_t timestamp);
+ void hwc1Hotplug(int hwc1DisplayId, int connected);
+
+ // These are set in the constructor and before any asynchronous events are
+ // possible
+
+ struct hwc_composer_device_1* const mHwc1Device;
+ const uint8_t mHwc1MinorVersion;
+ bool mHwc1SupportsVirtualDisplays;
+ bool mHwc1SupportsBackgroundColor;
+
+ class Callbacks;
+ const std::unique_ptr<Callbacks> mHwc1Callbacks;
+
+ std::unordered_set<HWC2::Capability> mCapabilities;
+
+ // These are only accessed from the main SurfaceFlinger thread (not from
+ // callbacks or dump
+
+ std::map<hwc2_layer_t, std::shared_ptr<Layer>> mLayers;
+
+ // A HWC1 supports only one virtual display.
+ std::shared_ptr<Display> mHwc1VirtualDisplay;
+
+ // These are potentially accessed from multiple threads, and are protected
+ // by this mutex. This needs to be recursive, since the HWC1 implementation
+ // can call back into the invalidate callback on the same thread that is
+ // calling prepare.
+ std::recursive_timed_mutex mStateMutex;
+
+ struct CallbackInfo {
+ hwc2_callback_data_t data;
+ hwc2_function_pointer_t pointer;
+ };
+ std::unordered_map<HWC2::Callback, CallbackInfo> mCallbacks;
+ bool mHasPendingInvalidate;
+
+ // There is a small gap between the time the HWC1 module is started and
+ // when the callbacks for vsync and hotplugs are registered by the
+ // HWC2on1Adapter. To prevent losing events they are stored in these arrays
+ // and fed to the callback as soon as possible.
+ std::vector<std::pair<int, int64_t>> mPendingVsyncs;
+ std::vector<std::pair<int, int>> mPendingHotplugs;
+
+ // Mapping between HWC1 display id and Display objects.
+ std::map<hwc2_display_t, std::shared_ptr<Display>> mDisplays;
+
+ // Map HWC1 display type (HWC_DISPLAY_PRIMARY, HWC_DISPLAY_EXTERNAL,
+ // HWC_DISPLAY_VIRTUAL) to Display IDs generated by HWC2on1Adapter objects.
+ std::unordered_map<int, hwc2_display_t> mHwc1DisplayMap;
+};
+
+} // namespace android
+
+#endif
diff --git a/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h b/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h
new file mode 100644
index 0000000..75de764
--- /dev/null
+++ b/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h
@@ -0,0 +1,59 @@
+/*
+ * 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 MINIFENCE_H
+#define MINIFENCE_H
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+/* MiniFence is a minimal re-implementation of Fence from libui. It exists to
+ * avoid linking the HWC2on1Adapter to libui and satisfy Treble requirements.
+ */
+class MiniFence : public LightRefBase<MiniFence> {
+public:
+ static const sp<MiniFence> NO_FENCE;
+
+ // Construct a new MiniFence object with an invalid file descriptor.
+ MiniFence();
+
+ // Construct a new MiniFence object to manage a given fence file descriptor.
+ // When the new MiniFence object is destructed the file descriptor will be
+ // closed.
+ explicit MiniFence(int fenceFd);
+
+ // Not copyable or movable.
+ MiniFence(const MiniFence& rhs) = delete;
+ MiniFence& operator=(const MiniFence& rhs) = delete;
+ MiniFence(MiniFence&& rhs) = delete;
+ MiniFence& operator=(MiniFence&& rhs) = delete;
+
+ // Return a duplicate of the fence file descriptor. The caller is
+ // responsible for closing the returned file descriptor. On error, -1 will
+ // be returned and errno will indicate the problem.
+ int dup() const;
+
+private:
+ // Only allow instantiation using ref counting.
+ friend class LightRefBase<MiniFence>;
+ ~MiniFence();
+
+ int mFenceFd;
+
+};
+}
+#endif //MINIFENCE_H
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
new file mode 100644
index 0000000..9294419
--- /dev/null
+++ b/libs/input/Android.bp
@@ -0,0 +1,67 @@
+// 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.
+
+// libinput is partially built for the host (used by build time keymap validation tool)
+
+cc_library {
+ name: "libinput",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "Input.cpp",
+ "InputDevice.cpp",
+ "Keyboard.cpp",
+ "KeyCharacterMap.cpp",
+ "KeyLayoutMap.cpp",
+ "VirtualKeyMap.cpp",
+ ],
+
+ clang: true,
+
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "IInputFlinger.cpp",
+ "InputTransport.cpp",
+ "VelocityControl.cpp",
+ "VelocityTracker.cpp",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ ],
+
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ },
+ host: {
+ shared: {
+ enabled: false,
+ },
+ },
+ },
+}
+
+subdirs = ["tests"]
diff --git a/libs/input/Android.mk b/libs/input/Android.mk
deleted file mode 100644
index 746de66..0000000
--- a/libs/input/Android.mk
+++ /dev/null
@@ -1,82 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# libinput is partially built for the host (used by build time keymap validation tool)
-# These files are common to host and target builds.
-
-commonSources := \
- Input.cpp \
- InputDevice.cpp \
- Keyboard.cpp \
- KeyCharacterMap.cpp \
- KeyLayoutMap.cpp \
- VirtualKeyMap.cpp
-
-deviceSources := \
- $(commonSources) \
- IInputFlinger.cpp \
- InputTransport.cpp \
- VelocityControl.cpp \
- VelocityTracker.cpp
-
-hostSources := \
- $(commonSources)
-
-# For the host
-# =====================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= $(hostSources)
-
-LOCAL_MODULE:= libinput
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-# For the device
-# =====================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= $(deviceSources)
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libcutils \
- libutils \
- libbinder
-
-LOCAL_MODULE:= libinput
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index e009731..003e73d 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -28,7 +28,7 @@
class BpInputFlinger : public BpInterface<IInputFlinger> {
public:
- BpInputFlinger(const sp<IBinder>& impl) :
+ explicit BpInputFlinger(const sp<IBinder>& impl) :
BpInterface<IInputFlinger>(impl) { }
virtual status_t doSomething() {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index d755ed3..4287abe 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,6 +21,7 @@
#include <ctype.h>
#include <input/InputDevice.h>
+#include <input/InputEventLabels.h>
namespace android {
@@ -87,17 +88,23 @@
const String8& name, InputDeviceConfigurationFileType type) {
// Search system repository.
String8 path;
- path.setTo(getenv("ANDROID_ROOT"));
- path.append("/usr/");
- appendInputDeviceConfigurationFileRelativePath(path, name, type);
+
+ // Treblized input device config files will be located /odm/usr or /vendor/usr.
+ const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
+ for (size_t i = 0; i < size(rootsForPartition); i++) {
+ path.setTo(rootsForPartition[i]);
+ path.append("/usr/");
+ appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBE
- ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
+ ALOGD("Probing for system provided input device configuration file: path='%s'",
+ path.string());
#endif
- if (!access(path.string(), R_OK)) {
+ if (!access(path.string(), R_OK)) {
#if DEBUG_PROBE
- ALOGD("Found");
+ ALOGD("Found");
#endif
- return path;
+ return path;
+ }
}
// Search user repository.
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 2dff4e0..af1c0af 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -19,19 +19,18 @@
// Log debug messages about touch event resampling
#define DEBUG_RESAMPLING 0
-
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <math.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <cutils/log.h>
#include <cutils/properties.h>
-#include <input/InputTransport.h>
+#include <log/log.h>
+#include <input/InputTransport.h>
namespace android {
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 9a01395..07f2289 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -208,7 +208,6 @@
}
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
- int32_t mask;
switch (keyCode) {
case AKEYCODE_ALT_LEFT:
return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
new file mode 100644
index 0000000..029a420
--- /dev/null
+++ b/libs/input/tests/Android.bp
@@ -0,0 +1,28 @@
+// Build the unit tests.
+cc_test {
+ name: "libinput_tests",
+ test_per_src: true,
+ srcs: [
+ "InputChannel_test.cpp",
+ "InputEvent_test.cpp",
+ "InputPublisherAndConsumer_test.cpp",
+ ],
+ shared_libs: [
+ "libinput",
+ "libcutils",
+ "libutils",
+ "libbinder",
+ "libui",
+ ]
+}
+
+// NOTE: This is a compile time test, and does not need to be
+// run. All assertions are static_asserts and will fail during
+// buildtime if something's wrong.
+cc_library_static {
+ name: "StructLayout_test",
+ srcs: ["StructLayout_test.cpp"],
+ cflags: [
+ "-O0",
+ ],
+}
diff --git a/libs/input/tests/Android.mk b/libs/input/tests/Android.mk
deleted file mode 100644
index 5bfa3d4..0000000
--- a/libs/input/tests/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH:= $(call my-dir)
-
-# Build the unit tests.
-test_src_files := \
- InputChannel_test.cpp \
- InputEvent_test.cpp \
- InputPublisherAndConsumer_test.cpp
-
-shared_libraries := \
- libinput \
- libcutils \
- libutils \
- libbinder \
- libui \
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-# NOTE: This is a compile time test, and does not need to be
-# run. All assertions are static_asserts and will fail during
-# buildtime if something's wrong.
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_SRC_FILES := StructLayout_test.cpp
-LOCAL_MODULE := StructLayout_test
-LOCAL_CFLAGS := -std=c++11 -O0
-LOCAL_MULTILIB := both
-include $(BUILD_STATIC_LIBRARY)
-
-
-# Build the manual test programs.
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 8d73f45..81b9953 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -20,7 +20,7 @@
namespace android {
#define CHECK_OFFSET(type, member, expected_offset) \
- static_assert((offsetof(type, member) == expected_offset), "")
+ static_assert((offsetof(type, member) == (expected_offset)), "")
struct Foo {
uint32_t dummy;
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
new file mode 100644
index 0000000..3ef8b4a
--- /dev/null
+++ b/libs/math/Android.bp
@@ -0,0 +1,21 @@
+// 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.
+
+cc_library_static {
+ name: "libmath",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+subdirs = ["tests"]
diff --git a/libs/math/MODULE_LICENSE_APACHE2 b/libs/math/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/math/MODULE_LICENSE_APACHE2
diff --git a/libs/math/NOTICE b/libs/math/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/math/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libs/math/include/math/TMatHelpers.h b/libs/math/include/math/TMatHelpers.h
new file mode 100644
index 0000000..5cb725d
--- /dev/null
+++ b/libs/math/include/math/TMatHelpers.h
@@ -0,0 +1,644 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <exception>
+#include <iomanip>
+#include <stdexcept>
+
+#include <math/quat.h>
+#include <math/TVecHelpers.h>
+
+#include <utils/String8.h>
+
+#ifndef LIKELY
+#define LIKELY_DEFINED_LOCAL
+#ifdef __cplusplus
+# define LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
+#endif
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/mat*.h
+ */
+
+
+/*
+ * Matrix utilities
+ */
+
+namespace matrix {
+
+inline constexpr int transpose(int v) { return v; }
+inline constexpr float transpose(float v) { return v; }
+inline constexpr double transpose(double v) { return v; }
+
+inline constexpr int trace(int v) { return v; }
+inline constexpr float trace(float v) { return v; }
+inline constexpr double trace(double v) { return v; }
+
+/*
+ * Matrix inversion
+ */
+template<typename MATRIX>
+MATRIX PURE gaussJordanInverse(const MATRIX& src) {
+ typedef typename MATRIX::value_type T;
+ static constexpr unsigned int N = MATRIX::NUM_ROWS;
+ MATRIX tmp(src);
+ MATRIX inverted(1);
+
+ for (size_t i = 0; i < N; ++i) {
+ // look for largest element in i'th column
+ size_t swap = i;
+ T t = std::abs(tmp[i][i]);
+ for (size_t j = i + 1; j < N; ++j) {
+ const T t2 = std::abs(tmp[j][i]);
+ if (t2 > t) {
+ swap = j;
+ t = t2;
+ }
+ }
+
+ if (swap != i) {
+ // swap columns.
+ std::swap(tmp[i], tmp[swap]);
+ std::swap(inverted[i], inverted[swap]);
+ }
+
+ const T denom(tmp[i][i]);
+ for (size_t k = 0; k < N; ++k) {
+ tmp[i][k] /= denom;
+ inverted[i][k] /= denom;
+ }
+
+ // Factor out the lower triangle
+ for (size_t j = 0; j < N; ++j) {
+ if (j != i) {
+ const T d = tmp[j][i];
+ for (size_t k = 0; k < N; ++k) {
+ tmp[j][k] -= tmp[i][k] * d;
+ inverted[j][k] -= inverted[i][k] * d;
+ }
+ }
+ }
+ }
+
+ return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// 2x2 matrix inverse is easy.
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) {
+ typedef typename MATRIX::value_type T;
+
+ // Assuming the input matrix is:
+ // | a b |
+ // | c d |
+ //
+ // The analytic inverse is
+ // | d -b |
+ // | -c a | / (a d - b c)
+ //
+ // Importantly, our matrices are column-major!
+
+ MATRIX inverted(MATRIX::NO_INIT);
+
+ const T a = x[0][0];
+ const T c = x[0][1];
+ const T b = x[1][0];
+ const T d = x[1][1];
+
+ const T det((a * d) - (b * c));
+ inverted[0][0] = d / det;
+ inverted[0][1] = -c / det;
+ inverted[1][0] = -b / det;
+ inverted[1][1] = a / det;
+ return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// From the Wikipedia article on matrix inversion's section on fast 3x3
+// matrix inversion:
+// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) {
+ typedef typename MATRIX::value_type T;
+
+ // Assuming the input matrix is:
+ // | a b c |
+ // | d e f |
+ // | g h i |
+ //
+ // The analytic inverse is
+ // | A B C |^T
+ // | D E F |
+ // | G H I | / determinant
+ //
+ // Which is
+ // | A D G |
+ // | B E H |
+ // | C F I | / determinant
+ //
+ // Where:
+ // A = (ei - fh), B = (fg - di), C = (dh - eg)
+ // D = (ch - bi), E = (ai - cg), F = (bg - ah)
+ // G = (bf - ce), H = (cd - af), I = (ae - bd)
+ //
+ // and the determinant is a*A + b*B + c*C (The rule of Sarrus)
+ //
+ // Importantly, our matrices are column-major!
+
+ MATRIX inverted(MATRIX::NO_INIT);
+
+ const T a = x[0][0];
+ const T b = x[1][0];
+ const T c = x[2][0];
+ const T d = x[0][1];
+ const T e = x[1][1];
+ const T f = x[2][1];
+ const T g = x[0][2];
+ const T h = x[1][2];
+ const T i = x[2][2];
+
+ // Do the full analytic inverse
+ const T A = e * i - f * h;
+ const T B = f * g - d * i;
+ const T C = d * h - e * g;
+ inverted[0][0] = A; // A
+ inverted[0][1] = B; // B
+ inverted[0][2] = C; // C
+ inverted[1][0] = c * h - b * i; // D
+ inverted[1][1] = a * i - c * g; // E
+ inverted[1][2] = b * g - a * h; // F
+ inverted[2][0] = b * f - c * e; // G
+ inverted[2][1] = c * d - a * f; // H
+ inverted[2][2] = a * e - b * d; // I
+
+ const T det(a * A + b * B + c * C);
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ inverted[col][row] /= det;
+ }
+ }
+
+ return inverted;
+}
+
+/**
+ * Inversion function which switches on the matrix size.
+ * @warning This function assumes the matrix is invertible. The result is
+ * undefined if it is not. It is the responsibility of the caller to
+ * make sure the matrix is not singular.
+ */
+template <typename MATRIX>
+inline constexpr MATRIX PURE inverse(const MATRIX& matrix) {
+ static_assert(MATRIX::NUM_ROWS == MATRIX::NUM_COLS, "only square matrices can be inverted");
+ return (MATRIX::NUM_ROWS == 2) ? fastInverse2<MATRIX>(matrix) :
+ ((MATRIX::NUM_ROWS == 3) ? fastInverse3<MATRIX>(matrix) :
+ gaussJordanInverse<MATRIX>(matrix));
+}
+
+template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
+CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
+ // pre-requisite:
+ // lhs : D columns, R rows
+ // rhs : C columns, D rows
+ // res : C columns, R rows
+
+ static_assert(MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS,
+ "matrices can't be multiplied. invalid dimensions.");
+ static_assert(MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS,
+ "invalid dimension of matrix multiply result.");
+ static_assert(MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS,
+ "invalid dimension of matrix multiply result.");
+
+ MATRIX_R res(MATRIX_R::NO_INIT);
+ for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) {
+ res[col] = lhs * rhs[col];
+ }
+ return res;
+}
+
+// transpose. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE transpose(const MATRIX& m) {
+ // for now we only handle square matrix transpose
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices");
+ MATRIX result(MATRIX::NO_INIT);
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) {
+ result[col][row] = transpose(m[row][col]);
+ }
+ }
+ return result;
+}
+
+// trace. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) {
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices");
+ typename MATRIX::value_type result(0);
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ result += trace(m[col][col]);
+ }
+ return result;
+}
+
+// diag. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) {
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices");
+ typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ result[col] = m[col][col];
+ }
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// This is taken from the Imath MatrixAlgo code, and is identical to Eigen.
+template <typename MATRIX>
+TQuaternion<typename MATRIX::value_type> extractQuat(const MATRIX& mat) {
+ typedef typename MATRIX::value_type T;
+
+ TQuaternion<T> quat(TQuaternion<T>::NO_INIT);
+
+ // Compute the trace to see if it is positive or not.
+ const T trace = mat[0][0] + mat[1][1] + mat[2][2];
+
+ // check the sign of the trace
+ if (LIKELY(trace > 0)) {
+ // trace is positive
+ T s = std::sqrt(trace + 1);
+ quat.w = T(0.5) * s;
+ s = T(0.5) / s;
+ quat.x = (mat[1][2] - mat[2][1]) * s;
+ quat.y = (mat[2][0] - mat[0][2]) * s;
+ quat.z = (mat[0][1] - mat[1][0]) * s;
+ } else {
+ // trace is negative
+
+ // Find the index of the greatest diagonal
+ size_t i = 0;
+ if (mat[1][1] > mat[0][0]) { i = 1; }
+ if (mat[2][2] > mat[i][i]) { i = 2; }
+
+ // Get the next indices: (n+1)%3
+ static constexpr size_t next_ijk[3] = { 1, 2, 0 };
+ size_t j = next_ijk[i];
+ size_t k = next_ijk[j];
+ T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1);
+ quat[i] = T(0.5) * s;
+ if (s != 0) {
+ s = T(0.5) / s;
+ }
+ quat.w = (mat[j][k] - mat[k][j]) * s;
+ quat[j] = (mat[i][j] + mat[j][i]) * s;
+ quat[k] = (mat[i][k] + mat[k][i]) * s;
+ }
+ return quat;
+}
+
+template <typename MATRIX>
+String8 asString(const MATRIX& m) {
+ String8 s;
+ for (size_t c = 0; c < MATRIX::col_size(); c++) {
+ s.append("| ");
+ for (size_t r = 0; r < MATRIX::row_size(); r++) {
+ s.appendFormat("%7.2f ", m[r][c]);
+ }
+ s.append("|\n");
+ }
+ return s;
+}
+
+} // namespace matrix
+
+// -------------------------------------------------------------------------------------
+
+/*
+ * TMatProductOperators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class BASE, typename T>
+class TMatProductOperators {
+public:
+ // multiply by a scalar
+ BASE<T>& operator *= (T v) {
+ BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ lhs[col] *= v;
+ }
+ return lhs;
+ }
+
+ // matrix *= matrix
+ template<typename U>
+ const BASE<T>& operator *= (const BASE<U>& rhs) {
+ BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+ lhs = matrix::multiply<BASE<T> >(lhs, rhs);
+ return lhs;
+ }
+
+ // divide by a scalar
+ BASE<T>& operator /= (T v) {
+ BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ lhs[col] /= v;
+ }
+ return lhs;
+ }
+
+ // matrix * matrix, result is a matrix of the same type than the lhs matrix
+ template<typename U>
+ friend CONSTEXPR BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
+ return matrix::multiply<BASE<T> >(lhs, rhs);
+ }
+};
+
+/*
+ * TMatSquareFunctions implements functions on a matrix of type BASE<T>.
+ *
+ * BASE only needs to implement:
+ * - operator[]
+ * - col_type
+ * - row_type
+ * - COL_SIZE
+ * - ROW_SIZE
+ *
+ * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template<template<typename U> class BASE, typename T>
+class TMatSquareFunctions {
+public:
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ friend inline CONSTEXPR BASE<T> PURE inverse(const BASE<T>& matrix) {
+ return matrix::inverse(matrix);
+ }
+ friend inline constexpr BASE<T> PURE transpose(const BASE<T>& m) {
+ return matrix::transpose(m);
+ }
+ friend inline constexpr T PURE trace(const BASE<T>& m) {
+ return matrix::trace(m);
+ }
+};
+
+template<template<typename U> class BASE, typename T>
+class TMatHelpers {
+public:
+ constexpr inline size_t getColumnSize() const { return BASE<T>::COL_SIZE; }
+ constexpr inline size_t getRowSize() const { return BASE<T>::ROW_SIZE; }
+ constexpr inline size_t getColumnCount() const { return BASE<T>::NUM_COLS; }
+ constexpr inline size_t getRowCount() const { return BASE<T>::NUM_ROWS; }
+ constexpr inline size_t size() const { return BASE<T>::ROW_SIZE; } // for TVec*<>
+
+ // array access
+ constexpr T const* asArray() const {
+ return &static_cast<BASE<T> const &>(*this)[0][0];
+ }
+
+ // element access
+ inline constexpr T const& operator()(size_t row, size_t col) const {
+ return static_cast<BASE<T> const &>(*this)[col][row];
+ }
+
+ inline T& operator()(size_t row, size_t col) {
+ return static_cast<BASE<T>&>(*this)[col][row];
+ }
+
+ template <typename VEC>
+ static CONSTEXPR BASE<T> translate(const VEC& t) {
+ BASE<T> r;
+ r[BASE<T>::NUM_COLS-1] = t;
+ return r;
+ }
+
+ template <typename VEC>
+ static constexpr BASE<T> scale(const VEC& s) {
+ return BASE<T>(s);
+ }
+
+ friend inline CONSTEXPR BASE<T> PURE abs(BASE<T> m) {
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ m[col] = abs(m[col]);
+ }
+ return m;
+ }
+};
+
+// functions for 3x3 and 4x4 matrices
+template<template<typename U> class BASE, typename T>
+class TMatTransform {
+public:
+ inline constexpr TMatTransform() {
+ static_assert(BASE<T>::NUM_ROWS == 3 || BASE<T>::NUM_ROWS == 4, "3x3 or 4x4 matrices only");
+ }
+
+ template <typename A, typename VEC>
+ static CONSTEXPR BASE<T> rotate(A radian, const VEC& about) {
+ BASE<T> r;
+ T c = std::cos(radian);
+ T s = std::sin(radian);
+ if (about.x == 1 && about.y == 0 && about.z == 0) {
+ r[1][1] = c; r[2][2] = c;
+ r[1][2] = s; r[2][1] = -s;
+ } else if (about.x == 0 && about.y == 1 && about.z == 0) {
+ r[0][0] = c; r[2][2] = c;
+ r[2][0] = s; r[0][2] = -s;
+ } else if (about.x == 0 && about.y == 0 && about.z == 1) {
+ r[0][0] = c; r[1][1] = c;
+ r[0][1] = s; r[1][0] = -s;
+ } else {
+ VEC nabout = normalize(about);
+ typename VEC::value_type x = nabout.x;
+ typename VEC::value_type y = nabout.y;
+ typename VEC::value_type z = nabout.z;
+ T nc = 1 - c;
+ T xy = x * y;
+ T yz = y * z;
+ T zx = z * x;
+ T xs = x * s;
+ T ys = y * s;
+ T zs = z * s;
+ r[0][0] = x*x*nc + c; r[1][0] = xy*nc - zs; r[2][0] = zx*nc + ys;
+ r[0][1] = xy*nc + zs; r[1][1] = y*y*nc + c; r[2][1] = yz*nc - xs;
+ r[0][2] = zx*nc - ys; r[1][2] = yz*nc + xs; r[2][2] = z*z*nc + c;
+
+ // Clamp results to -1, 1.
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+ }
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Create a matrix from euler angles using YPR around YXZ respectively
+ * @param yaw about Y axis
+ * @param pitch about X axis
+ * @param roll about Z axis
+ */
+ template <
+ typename Y, typename P, typename R,
+ typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+ >
+ static CONSTEXPR BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
+ return eulerZYX(roll, pitch, yaw);
+ }
+
+ /**
+ * Create a matrix from euler angles using YPR around ZYX respectively
+ * @param roll about X axis
+ * @param pitch about Y axis
+ * @param yaw about Z axis
+ *
+ * The euler angles are applied in ZYX order. i.e: a vector is first rotated
+ * about X (roll) then Y (pitch) and then Z (yaw).
+ */
+ template <
+ typename Y, typename P, typename R,
+ typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+ >
+ static CONSTEXPR BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
+ BASE<T> r;
+ T cy = std::cos(yaw);
+ T sy = std::sin(yaw);
+ T cp = std::cos(pitch);
+ T sp = std::sin(pitch);
+ T cr = std::cos(roll);
+ T sr = std::sin(roll);
+ T cc = cr * cy;
+ T cs = cr * sy;
+ T sc = sr * cy;
+ T ss = sr * sy;
+ r[0][0] = cp * cy;
+ r[0][1] = cp * sy;
+ r[0][2] = -sp;
+ r[1][0] = sp * sc - cs;
+ r[1][1] = sp * ss + cc;
+ r[1][2] = cp * sr;
+ r[2][0] = sp * cc + ss;
+ r[2][1] = sp * cs - sc;
+ r[2][2] = cp * cr;
+
+ // Clamp results to -1, 1.
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+ }
+ }
+ return r;
+ }
+
+ TQuaternion<T> toQuaternion() const {
+ return matrix::extractQuat(static_cast<const BASE<T>&>(*this));
+ }
+};
+
+
+template <template<typename T> class BASE, typename T>
+class TMatDebug {
+public:
+ friend std::ostream& operator<<(std::ostream& stream, const BASE<T>& m) {
+ for (size_t row = 0; row < BASE<T>::NUM_ROWS; ++row) {
+ if (row != 0) {
+ stream << std::endl;
+ }
+ if (row == 0) {
+ stream << "/ ";
+ } else if (row == BASE<T>::NUM_ROWS-1) {
+ stream << "\\ ";
+ } else {
+ stream << "| ";
+ }
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ stream << std::setw(10) << std::to_string(m[col][row]);
+ }
+ if (row == 0) {
+ stream << " \\";
+ } else if (row == BASE<T>::NUM_ROWS-1) {
+ stream << " /";
+ } else {
+ stream << " |";
+ }
+ }
+ return stream;
+ }
+
+ String8 asString() const {
+ return matrix::asString(static_cast<const BASE<T>&>(*this));
+ }
+};
+
+// -------------------------------------------------------------------------------------
+} // namespace details
+} // namespace android
+
+#ifdef LIKELY_DEFINED_LOCAL
+#undef LIKELY_DEFINED_LOCAL
+#undef LIKELY
+#undef UNLIKELY
+#endif //LIKELY_DEFINED_LOCAL
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/TQuatHelpers.h b/libs/math/include/math/TQuatHelpers.h
new file mode 100644
index 0000000..f0a71ae
--- /dev/null
+++ b/libs/math/include/math/TQuatHelpers.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright 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.
+ */
+
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <iostream>
+
+#include <math/vec3.h>
+
+#define PURE __attribute__((pure))
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/quat.h
+ */
+
+
+/*
+ * TQuatProductOperators implements basic arithmetic and basic compound assignment
+ * operators on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class QUATERNION, typename T>
+class TQuatProductOperators {
+public:
+ /* compound assignment from a another quaternion of the same size but different
+ * element type.
+ */
+ template <typename OTHER>
+ QUATERNION<T>& operator *= (const QUATERNION<OTHER>& r) {
+ QUATERNION<T>& q = static_cast<QUATERNION<T>&>(*this);
+ q = q * r;
+ return q;
+ }
+
+ /* compound assignment products by a scalar
+ */
+ QUATERNION<T>& operator *= (T v) {
+ QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+ for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+ lhs[i] *= v;
+ }
+ return lhs;
+ }
+ QUATERNION<T>& operator /= (T v) {
+ QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+ for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+ lhs[i] /= v;
+ }
+ return lhs;
+ }
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ /* The operators below handle operation between quaternion of the same size
+ * but of a different element type.
+ */
+ template<typename RT>
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(const QUATERNION<T>& q, const QUATERNION<RT>& r) {
+ // could be written as:
+ // return QUATERNION<T>(
+ // q.w*r.w - dot(q.xyz, r.xyz),
+ // q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz));
+
+ return QUATERNION<T>(
+ q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+ q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+ q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+ q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+ }
+
+ template<typename RT>
+ friend inline
+ constexpr TVec3<T> PURE operator *(const QUATERNION<T>& q, const TVec3<RT>& v) {
+ // note: if q is known to be a unit quaternion, then this simplifies to:
+ // TVec3<T> t = 2 * cross(q.xyz, v)
+ // return v + (q.w * t) + cross(q.xyz, t)
+ return imaginary(q * QUATERNION<T>(v, 0) * inverse(q));
+ }
+
+
+ /* For quaternions, we use explicit "by a scalar" products because it's much faster
+ * than going (implicitly) through the quaternion multiplication.
+ * For reference: we could use the code below instead, but it would be a lot slower.
+ * friend inline
+ * constexpr BASE<T> PURE operator *(const BASE<T>& q, const BASE<T>& r) {
+ * return BASE<T>(
+ * q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+ * q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+ * q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+ * q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+ *
+ */
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(QUATERNION<T> q, T scalar) {
+ // don't pass q by reference because we need a copy anyways
+ return q *= scalar;
+ }
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(T scalar, QUATERNION<T> q) {
+ // don't pass q by reference because we need a copy anyways
+ return q *= scalar;
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE operator /(QUATERNION<T> q, T scalar) {
+ // don't pass q by reference because we need a copy anyways
+ return q /= scalar;
+ }
+};
+
+
+/*
+ * TQuatFunctions implements functions on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatFunctions {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ template<typename RT>
+ friend inline
+ constexpr T PURE dot(const QUATERNION<T>& p, const QUATERNION<RT>& q) {
+ return p.x * q.x +
+ p.y * q.y +
+ p.z * q.z +
+ p.w * q.w;
+ }
+
+ friend inline
+ constexpr T PURE norm(const QUATERNION<T>& q) {
+ return std::sqrt( dot(q, q) );
+ }
+
+ friend inline
+ constexpr T PURE length(const QUATERNION<T>& q) {
+ return norm(q);
+ }
+
+ friend inline
+ constexpr T PURE length2(const QUATERNION<T>& q) {
+ return dot(q, q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE normalize(const QUATERNION<T>& q) {
+ return length(q) ? q / length(q) : QUATERNION<T>(1);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE conj(const QUATERNION<T>& q) {
+ return QUATERNION<T>(q.w, -q.x, -q.y, -q.z);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE inverse(const QUATERNION<T>& q) {
+ return conj(q) * (1 / dot(q, q));
+ }
+
+ friend inline
+ constexpr T PURE real(const QUATERNION<T>& q) {
+ return q.w;
+ }
+
+ friend inline
+ constexpr TVec3<T> PURE imaginary(const QUATERNION<T>& q) {
+ return q.xyz;
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE unreal(const QUATERNION<T>& q) {
+ return QUATERNION<T>(q.xyz, 0);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE cross(const QUATERNION<T>& p, const QUATERNION<T>& q) {
+ return unreal(p*q);
+ }
+
+ friend inline
+ QUATERNION<T> PURE exp(const QUATERNION<T>& q) {
+ const T nq(norm(q.xyz));
+ return std::exp(q.w)*QUATERNION<T>((sin(nq)/nq)*q.xyz, cos(nq));
+ }
+
+ friend inline
+ QUATERNION<T> PURE log(const QUATERNION<T>& q) {
+ const T nq(norm(q));
+ return QUATERNION<T>((std::acos(q.w/nq)/norm(q.xyz))*q.xyz, log(nq));
+ }
+
+ friend inline
+ QUATERNION<T> PURE pow(const QUATERNION<T>& q, T a) {
+ // could also be computed as: exp(a*log(q));
+ const T nq(norm(q));
+ const T theta(a*std::acos(q.w / nq));
+ return std::pow(nq, a) * QUATERNION<T>(normalize(q.xyz) * std::sin(theta), std::cos(theta));
+ }
+
+ friend inline
+ QUATERNION<T> PURE slerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ // could also be computed as: pow(q * inverse(p), t) * p;
+ const T d = dot(p, q);
+ const T npq = sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q||
+ const T a = std::acos(std::abs(d) / npq);
+ const T a0 = a * (1 - t);
+ const T a1 = a * t;
+ const T isina = 1 / sin(a);
+ const T s0 = std::sin(a0) * isina;
+ const T s1 = std::sin(a1) * isina;
+ // ensure we're taking the "short" side
+ return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE lerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ return ((1 - t) * p) + (t * q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE nlerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ return normalize(lerp(p, q, t));
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE positive(const QUATERNION<T>& q) {
+ return q.w < 0 ? -q : q;
+ }
+};
+
+/*
+ * TQuatDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatDebug {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ friend std::ostream& operator<< (std::ostream& stream, const QUATERNION<T>& q) {
+ return stream << "< " << q.w << " + " << q.x << "i + " << q.y << "j + " << q.z << "k >";
+ }
+};
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+} // namespace details
+} // namespace android
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
new file mode 100644
index 0000000..20f852f
--- /dev/null
+++ b/libs/math/include/math/TVecHelpers.h
@@ -0,0 +1,608 @@
+/*
+ * Copyright 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.
+ */
+
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <limits>
+#include <iostream>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/vec{2|3|4}.h
+ */
+
+/*
+ * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class VECTOR, typename T>
+class TVecAddOperators {
+public:
+ /* compound assignment from a another vector of the same size but different
+ * element type.
+ */
+ template<typename OTHER>
+ VECTOR<T>& operator +=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] += v[i];
+ }
+ return lhs;
+ }
+ template<typename OTHER>
+ VECTOR<T>& operator -=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] -= v[i];
+ }
+ return lhs;
+ }
+
+ /* compound assignment from a another vector of the same type.
+ * These operators can be used for implicit conversion and handle operations
+ * like "vector *= scalar" by letting the compiler implicitly convert a scalar
+ * to a vector (assuming the BASE<T> allows it).
+ */
+ VECTOR<T>& operator +=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] += v[i];
+ }
+ return lhs;
+ }
+ VECTOR<T>& operator -=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] -= v[i];
+ }
+ return lhs;
+ }
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ /* The operators below handle operation between vectors of the same size
+ * but of a different element type.
+ */
+ template<typename RT>
+ friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv += rv;
+ }
+ template<typename RT>
+ friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv -= rv;
+ }
+
+ /* The operators below (which are not templates once this class is instanced,
+ * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
+ * These handle operations like "vector + scalar" and "scalar + vector" by
+ * letting the compiler implicitly convert a scalar to a vector (assuming
+ * the BASE<T> allows it).
+ */
+ friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv += rv;
+ }
+ friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv -= rv;
+ }
+};
+
+template<template<typename T> class VECTOR, typename T>
+class TVecProductOperators {
+public:
+ /* compound assignment from a another vector of the same size but different
+ * element type.
+ */
+ template<typename OTHER>
+ VECTOR<T>& operator *=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] *= v[i];
+ }
+ return lhs;
+ }
+ template<typename OTHER>
+ VECTOR<T>& operator /=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] /= v[i];
+ }
+ return lhs;
+ }
+
+ /* compound assignment from a another vector of the same type.
+ * These operators can be used for implicit conversion and handle operations
+ * like "vector *= scalar" by letting the compiler implicitly convert a scalar
+ * to a vector (assuming the BASE<T> allows it).
+ */
+ VECTOR<T>& operator *=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] *= v[i];
+ }
+ return lhs;
+ }
+ VECTOR<T>& operator /=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] /= v[i];
+ }
+ return lhs;
+ }
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ /* The operators below handle operation between vectors of the same size
+ * but of a different element type.
+ */
+ template<typename RT>
+ friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv *= rv;
+ }
+ template<typename RT>
+ friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv /= rv;
+ }
+
+ /* The operators below (which are not templates once this class is instanced,
+ * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
+ * These handle operations like "vector * scalar" and "scalar * vector" by
+ * letting the compiler implicitly convert a scalar to a vector (assuming
+ * the BASE<T> allows it).
+ */
+ friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv *= rv;
+ }
+ friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv /= rv;
+ }
+};
+
+/*
+ * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ *
+ * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecUnaryOperators {
+public:
+ VECTOR<T>& operator ++() {
+ VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < rhs.size(); i++) {
+ ++rhs[i];
+ }
+ return rhs;
+ }
+
+ VECTOR<T>& operator --() {
+ VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < rhs.size(); i++) {
+ --rhs[i];
+ }
+ return rhs;
+ }
+
+ CONSTEXPR VECTOR<T> operator -() const {
+ VECTOR<T> r(VECTOR<T>::NO_INIT);
+ VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+ for (size_t i = 0; i < r.size(); i++) {
+ r[i] = -rv[i];
+ }
+ return r;
+ }
+};
+
+/*
+ * TVecComparisonOperators implements relational/comparison operators
+ * on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecComparisonOperators {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ template<typename RT>
+ friend inline
+ bool PURE operator ==(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++)
+ if (lv[i] != rv[i])
+ return false;
+ return true;
+ }
+
+ template<typename RT>
+ friend inline
+ bool PURE operator !=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return !operator ==(lv, rv);
+ }
+
+ template<typename RT>
+ friend inline
+ bool PURE operator >(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++) {
+ if (lv[i] == rv[i]) {
+ continue;
+ }
+ return lv[i] > rv[i];
+ }
+ return false;
+ }
+
+ template<typename RT>
+ friend inline
+ constexpr bool PURE operator <=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return !(lv > rv);
+ }
+
+ template<typename RT>
+ friend inline
+ bool PURE operator <(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++) {
+ if (lv[i] == rv[i]) {
+ continue;
+ }
+ return lv[i] < rv[i];
+ }
+ return false;
+ }
+
+ template<typename RT>
+ friend inline
+ constexpr bool PURE operator >=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return !(lv < rv);
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] == rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] != rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] < rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] <= rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] > rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] >= rv[i];
+ }
+ return r;
+ }
+};
+
+/*
+ * TVecFunctions implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecFunctions {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ template<typename RT>
+ friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ T r(0);
+ for (size_t i = 0; i < lv.size(); i++) {
+ //r = std::fma(lv[i], rv[i], r);
+ r += lv[i] * rv[i];
+ }
+ return r;
+ }
+
+ friend inline constexpr T PURE norm(const VECTOR<T>& lv) {
+ return std::sqrt(dot(lv, lv));
+ }
+
+ friend inline constexpr T PURE length(const VECTOR<T>& lv) {
+ return norm(lv);
+ }
+
+ friend inline constexpr T PURE norm2(const VECTOR<T>& lv) {
+ return dot(lv, lv);
+ }
+
+ friend inline constexpr T PURE length2(const VECTOR<T>& lv) {
+ return norm2(lv);
+ }
+
+ template<typename RT>
+ friend inline constexpr T PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return length(rv - lv);
+ }
+
+ template<typename RT>
+ friend inline constexpr T PURE distance2(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return length2(rv - lv);
+ }
+
+ friend inline constexpr VECTOR<T> PURE normalize(const VECTOR<T>& lv) {
+ return lv * (T(1) / length(lv));
+ }
+
+ friend inline constexpr VECTOR<T> PURE rcp(VECTOR<T> v) {
+ return T(1) / v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE abs(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::abs(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE floor(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::floor(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE ceil(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::ceil(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE round(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::round(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE inversesqrt(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = T(1) / std::sqrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE sqrt(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::sqrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE pow(VECTOR<T> v, T p) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::pow(v[i], p);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE saturate(const VECTOR<T>& lv) {
+ return clamp(lv, T(0), T(1));
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) {
+ for (size_t i = 0; i< v.size(); i++) {
+ v[i] = std::min(max, std::max(min, v[i]));
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) {
+ for (size_t i = 0; i<lv.size(); i++) {
+ //a[i] = std::fma(lv[i], rv[i], a[i]);
+ a[i] += (lv[i] * rv[i]);
+ }
+ return a;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::min(u[i], v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::max(u[i], v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR T PURE max(const VECTOR<T>& v) {
+ T r(std::numeric_limits<T>::lowest());
+ for (size_t i = 0; i < v.size(); i++) {
+ r = std::max(r, v[i]);
+ }
+ return r;
+ }
+
+ friend inline CONSTEXPR T PURE min(const VECTOR<T>& v) {
+ T r(std::numeric_limits<T>::max());
+ for (size_t i = 0; i < v.size(); i++) {
+ r = std::min(r, v[i]);
+ }
+ return r;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE apply(VECTOR<T> v, const std::function<T(T)>& f) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = f(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR bool PURE any(const VECTOR<T>& v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ if (v[i] != T(0)) return true;
+ }
+ return false;
+ }
+
+ friend inline CONSTEXPR bool PURE all(const VECTOR<T>& v) {
+ bool result = true;
+ for (size_t i = 0; i < v.size(); i++) {
+ result &= (v[i] != T(0));
+ }
+ return result;
+ }
+
+ template<typename R>
+ friend inline CONSTEXPR VECTOR<R> PURE map(VECTOR<T> v, const std::function<R(T)>& f) {
+ VECTOR<R> result;
+ for (size_t i = 0; i < v.size(); i++) {
+ result[i] = f(v[i]);
+ }
+ return result;
+ }
+};
+
+/*
+ * TVecDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecDebug {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ friend std::ostream& operator<<(std::ostream& stream, const VECTOR<T>& v) {
+ stream << "< ";
+ for (size_t i = 0; i < v.size() - 1; i++) {
+ stream << T(v[i]) << ", ";
+ }
+ stream << T(v[v.size() - 1]) << " >";
+ return stream;
+ }
+};
+
+#undef CONSTEXPR
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+} // namespace details
+} // namespace android
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
new file mode 100644
index 0000000..615b840
--- /dev/null
+++ b/libs/math/include/math/half.h
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <iosfwd>
+#include <limits>
+#include <type_traits>
+
+#ifndef LIKELY
+#define LIKELY_DEFINED_LOCAL
+#ifdef __cplusplus
+# define LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
+#endif
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+
+/*
+ * half-float
+ *
+ * 1 5 10
+ * +-+------+------------+
+ * |s|eee.ee|mm.mmmm.mmmm|
+ * +-+------+------------+
+ *
+ * minimum (denormal) value: 2^-24 = 5.96e-8
+ * minimum (normal) value: 2^-14 = 6.10e-5
+ * maximum value: 2-2^-10 = 65504
+ *
+ * Integers between 0 and 2048 can be represented exactly
+ */
+class half {
+ struct fp16 {
+ uint16_t bits = 0;
+ fp16() noexcept = default;
+ explicit constexpr fp16(uint16_t b) noexcept : bits(b) { }
+ void setS(unsigned int s) noexcept { bits = uint16_t((bits & 0x7FFF) | (s<<15)); }
+ void setE(unsigned int s) noexcept { bits = uint16_t((bits & 0xE3FF) | (s<<10)); }
+ void setM(unsigned int s) noexcept { bits = uint16_t((bits & 0xFC00) | (s<< 0)); }
+ constexpr unsigned int getS() const noexcept { return bits >> 15u; }
+ constexpr unsigned int getE() const noexcept { return (bits >> 10u) & 0x1Fu; }
+ constexpr unsigned int getM() const noexcept { return bits & 0x3FFu; }
+ };
+ struct fp32 {
+ union {
+ uint32_t bits = 0;
+ float fp;
+ };
+ fp32() noexcept = default;
+ explicit constexpr fp32(float f) : fp(f) { }
+ void setS(unsigned int s) noexcept { bits = uint32_t((bits & 0x7FFFFFFF) | (s<<31)); }
+ void setE(unsigned int s) noexcept { bits = uint32_t((bits & 0x807FFFFF) | (s<<23)); }
+ void setM(unsigned int s) noexcept { bits = uint32_t((bits & 0xFF800000) | (s<< 0)); }
+ constexpr unsigned int getS() const noexcept { return bits >> 31u; }
+ constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; }
+ constexpr unsigned int getM() const noexcept { return bits & 0x7FFFFFu; }
+ };
+
+public:
+ CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
+ CONSTEXPR operator float() const noexcept { return htof(mBits); }
+
+ uint16_t getBits() const noexcept { return mBits.bits; }
+ unsigned int getExponent() const noexcept { return mBits.getE(); }
+ unsigned int getMantissa() const noexcept { return mBits.getM(); }
+
+private:
+ friend class std::numeric_limits<half>;
+ friend CONSTEXPR half operator"" _hf(long double v);
+
+ enum Binary { binary };
+ explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { }
+ static CONSTEXPR fp16 ftoh(float v) noexcept;
+ static CONSTEXPR float htof(fp16 v) noexcept;
+ fp16 mBits;
+};
+
+inline CONSTEXPR half::fp16 half::ftoh(float v) noexcept {
+ fp16 out;
+ fp32 in(v);
+ if (UNLIKELY(in.getE() == 0xFF)) { // inf or nan
+ out.setE(0x1F);
+ out.setM(in.getM() ? 0x200 : 0);
+ } else {
+ int e = static_cast<int>(in.getE()) - 127 + 15;
+ if (e >= 0x1F) {
+ // overflow
+ out.setE(0x31); // +/- inf
+ } else if (e <= 0) {
+ // underflow
+ // flush to +/- 0
+ } else {
+ unsigned int m = in.getM();
+ out.setE(uint16_t(e));
+ out.setM(m >> 13);
+ if (m & 0x1000) {
+ // rounding
+ out.bits++;
+ }
+ }
+ }
+ out.setS(in.getS());
+ return out;
+}
+
+inline CONSTEXPR float half::htof(half::fp16 in) noexcept {
+ fp32 out;
+ if (UNLIKELY(in.getE() == 0x1F)) { // inf or nan
+ out.setE(0xFF);
+ out.setM(in.getM() ? 0x400000 : 0);
+ } else {
+ if (in.getE() == 0) {
+ if (in.getM()) {
+ // TODO: denormal half float, treat as zero for now
+ // (it's stupid because they can be represented as regular float)
+ }
+ } else {
+ int e = static_cast<int>(in.getE()) - 15 + 127;
+ unsigned int m = in.getM();
+ out.setE(uint32_t(e));
+ out.setM(m << 13);
+ }
+ }
+ out.setS(in.getS());
+ return out.fp;
+}
+
+inline CONSTEXPR android::half operator"" _hf(long double v) {
+ return android::half(android::half::binary, android::half::ftoh(static_cast<float>(v)).bits);
+}
+
+} // namespace android
+
+namespace std {
+
+template<> struct is_floating_point<android::half> : public std::true_type {};
+
+template<>
+class numeric_limits<android::half> {
+public:
+ typedef android::half type;
+
+ static constexpr const bool is_specialized = true;
+ static constexpr const bool is_signed = true;
+ static constexpr const bool is_integer = false;
+ static constexpr const bool is_exact = false;
+ static constexpr const bool has_infinity = true;
+ static constexpr const bool has_quiet_NaN = true;
+ static constexpr const bool has_signaling_NaN = false;
+ static constexpr const float_denorm_style has_denorm = denorm_absent;
+ static constexpr const bool has_denorm_loss = true;
+ static constexpr const bool is_iec559 = false;
+ static constexpr const bool is_bounded = true;
+ static constexpr const bool is_modulo = false;
+ static constexpr const bool traps = false;
+ static constexpr const bool tinyness_before = false;
+ static constexpr const float_round_style round_style = round_indeterminate;
+
+ static constexpr const int digits = 11;
+ static constexpr const int digits10 = 3;
+ static constexpr const int max_digits10 = 5;
+ static constexpr const int radix = 2;
+ static constexpr const int min_exponent = -13;
+ static constexpr const int min_exponent10 = -4;
+ static constexpr const int max_exponent = 16;
+ static constexpr const int max_exponent10 = 4;
+
+ inline static constexpr type round_error() noexcept { return android::half(android::half::binary, 0x3800); }
+ inline static constexpr type min() noexcept { return android::half(android::half::binary, 0x0400); }
+ inline static constexpr type max() noexcept { return android::half(android::half::binary, 0x7bff); }
+ inline static constexpr type lowest() noexcept { return android::half(android::half::binary, 0xfbff); }
+ inline static constexpr type epsilon() noexcept { return android::half(android::half::binary, 0x1400); }
+ inline static constexpr type infinity() noexcept { return android::half(android::half::binary, 0x7c00); }
+ inline static constexpr type quiet_NaN() noexcept { return android::half(android::half::binary, 0x7fff); }
+ inline static constexpr type denorm_min() noexcept { return android::half(android::half::binary, 0x0001); }
+ inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); }
+};
+
+} // namespace std
+
+#ifdef LIKELY_DEFINED_LOCAL
+#undef LIKELY_DEFINED_LOCAL
+#undef LIKELY
+#undef UNLIKELY
+#endif // LIKELY_DEFINED_LOCAL
+
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat2.h b/libs/math/include/math/mat2.h
new file mode 100644
index 0000000..3e6cd4c
--- /dev/null
+++ b/libs/math/include/math/mat2.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <math/TMatHelpers.h>
+#include <math/vec2.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+/**
+ * A 2x2 column-major matrix class.
+ *
+ * Conceptually a 2x2 matrix is a an array of 2 column vec2:
+ *
+ * mat2 m =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0] & m[1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0][0] & m[1][0] \\
+ * m[0][1] & m[1][1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m(0,0) & m(0,1) \\
+ * m(1,0) & m(1,1) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2.
+ *
+ */
+template <typename T>
+class TMat22 : public TVecUnaryOperators<TMat22, T>,
+ public TVecComparisonOperators<TMat22, T>,
+ public TVecAddOperators<TMat22, T>,
+ public TMatProductOperators<TMat22, T>,
+ public TMatSquareFunctions<TMat22, T>,
+ public TMatHelpers<TMat22, T>,
+ public TMatDebug<TMat22, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ typedef TVec2<T> col_type;
+ typedef TVec2<T> row_type;
+
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+ /*
+ * <-- N columns -->
+ *
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
+ *
+ * COL_SIZE = M
+ * ROW_SIZE = N
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+ */
+
+ col_type m_value[NUM_COLS];
+
+public:
+ // array access
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
+
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TMat22(const TMat22&) = default;
+ ~TMat22() = default;
+ TMat22& operator = (const TMat22&) = default;
+
+ /**
+ * constructors
+ */
+
+ /**
+ * leaves object uninitialized. use with caution.
+ */
+ explicit constexpr TMat22(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
+
+
+ /**
+ * initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * 1 & 0 \\
+ * 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat22();
+
+ /**
+ * initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v & 0 \\
+ * 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template<typename U>
+ explicit CONSTEXPR TMat22(U v);
+
+ /**
+ * sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v[0] & 0 \\
+ * 0 & v[1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(const TVec2<U>& v);
+
+ /**
+ * construct from another matrix of the same size
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(const TMat22<U>& rhs);
+
+ /**
+ * construct from 2 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v0 & v1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename A, typename B>
+ CONSTEXPR TMat22(const TVec2<A>& v0, const TVec2<B>& v1);
+
+ /** construct from 4 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0][0] & m[1][0] \\
+ * m[0][1] & m[1][1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <
+ typename A, typename B,
+ typename C, typename D>
+ CONSTEXPR TMat22(A m00, B m01, C m10, D m11);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(U const* rawArray);
+
+ /**
+ * Rotate by radians in the 2D plane
+ */
+ static CONSTEXPR TMat22<T> rotate(T radian) {
+ TMat22<T> r(TMat22<T>::NO_INIT);
+ T c = std::cos(radian);
+ T s = std::sin(radian);
+ r[0][0] = c; r[1][1] = c;
+ r[0][1] = s; r[1][0] = -s;
+ return r;
+ }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat22<T>::TMat22() {
+ m_value[0] = col_type(1, 0);
+ m_value[1] = col_type(0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U v) {
+ m_value[0] = col_type(v, 0);
+ m_value[1] = col_type(0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<U>& v) {
+ m_value[0] = col_type(v.x, 0);
+ m_value[1] = col_type(0, v.y);
+}
+
+// construct from 4 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+ typename A, typename B,
+ typename C, typename D>
+CONSTEXPR TMat22<T>::TMat22( A m00, B m01, C m10, D m11) {
+ m_value[0] = col_type(m00, m01);
+ m_value[1] = col_type(m10, m11);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(const TMat22<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
+}
+
+// Construct from 2 column vectors.
+template <typename T>
+template <typename A, typename B>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<A>& v0, const TVec2<B>& v1) {
+ m_value[0] = v0;
+ m_value[1] = v1;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::col_type PURE operator *(const TMat22<T>& lhs, const TVec2<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat22<U>::col_type result;
+ for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
+ return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::row_type PURE operator *(const TVec2<U>& lhs, const TMat22<T>& rhs) {
+ typename TMat22<U>::row_type result(TMat22<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
+ return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(TMat22<T> lhs, U rhs) {
+ return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(U lhs, const TMat22<T>& rhs) {
+ return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat22<T>::col_type PURE diag(const TMat22<T>& m) {
+ return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat22<double> mat2d;
+typedef details::TMat22<float> mat2;
+typedef details::TMat22<float> mat2f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h
new file mode 100644
index 0000000..5c8a9b2
--- /dev/null
+++ b/libs/math/include/math/mat3.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <math/quat.h>
+#include <math/TMatHelpers.h>
+#include <math/vec3.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 3x3 column-major matrix class.
+ *
+ * Conceptually a 3x3 matrix is a an array of 3 column vec3:
+ *
+ * mat3 m =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0] & m[1] & m[2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0][0] & m[1][0] & m[2][0] \\
+ * m[0][1] & m[1][1] & m[2][1] \\
+ * m[0][2] & m[1][2] & m[2][2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m(0,0) & m(0,1) & m(0,2) \\
+ * m(1,0) & m(1,1) & m(1,2) \\
+ * m(2,0) & m(2,1) & m(2,2) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3.
+ *
+ */
+template <typename T>
+class TMat33 : public TVecUnaryOperators<TMat33, T>,
+ public TVecComparisonOperators<TMat33, T>,
+ public TVecAddOperators<TMat33, T>,
+ public TMatProductOperators<TMat33, T>,
+ public TMatSquareFunctions<TMat33, T>,
+ public TMatTransform<TMat33, T>,
+ public TMatHelpers<TMat33, T>,
+ public TMatDebug<TMat33, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ typedef TVec3<T> col_type;
+ typedef TVec3<T> row_type;
+
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+ /*
+ * <-- N columns -->
+ *
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
+ *
+ * COL_SIZE = M
+ * ROW_SIZE = N
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+ */
+
+ col_type m_value[NUM_COLS];
+
+public:
+ // array access
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
+
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TMat33(const TMat33&) = default;
+ ~TMat33() = default;
+ TMat33& operator = (const TMat33&) = default;
+
+ /**
+ * constructors
+ */
+
+ /**
+ * leaves object uninitialized. use with caution.
+ */
+ explicit constexpr TMat33(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
+
+
+ /**
+ * initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * 1 & 0 & 0 \\
+ * 0 & 1 & 0 \\
+ * 0 & 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat33();
+
+ /**
+ * initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v & 0 & 0 \\
+ * 0 & v & 0 \\
+ * 0 & 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template<typename U>
+ explicit CONSTEXPR TMat33(U v);
+
+ /**
+ * sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v[0] & 0 & 0 \\
+ * 0 & v[1] & 0 \\
+ * 0 & 0 & v[2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TVec3<U>& v);
+
+ /**
+ * construct from another matrix of the same size
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TMat33<U>& rhs);
+
+ /**
+ * construct from 3 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v0 & v1 & v2 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename A, typename B, typename C>
+ CONSTEXPR TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2);
+
+ /** construct from 9 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0][0] & m[1][0] & m[2][0] \\
+ * m[0][1] & m[1][1] & m[2][1] \\
+ * m[0][2] & m[1][2] & m[2][2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <
+ typename A, typename B, typename C,
+ typename D, typename E, typename F,
+ typename G, typename H, typename I>
+ CONSTEXPR TMat33(
+ A m00, B m01, C m02,
+ D m10, E m11, F m12,
+ G m20, H m21, I m22);
+
+ /**
+ * construct from a quaternion
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TQuaternion<U>& q);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(U const* rawArray);
+
+ /**
+ * orthogonalize only works on matrices of size 3x3
+ */
+ friend inline
+ CONSTEXPR TMat33 orthogonalize(const TMat33& m) {
+ TMat33 ret(TMat33::NO_INIT);
+ ret[0] = normalize(m[0]);
+ ret[2] = normalize(cross(ret[0], m[1]));
+ ret[1] = normalize(cross(ret[2], ret[0]));
+ return ret;
+ }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat33<T>::TMat33() {
+ m_value[0] = col_type(1, 0, 0);
+ m_value[1] = col_type(0, 1, 0);
+ m_value[2] = col_type(0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U v) {
+ m_value[0] = col_type(v, 0, 0);
+ m_value[1] = col_type(0, v, 0);
+ m_value[2] = col_type(0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<U>& v) {
+ m_value[0] = col_type(v.x, 0, 0);
+ m_value[1] = col_type(0, v.y, 0);
+ m_value[2] = col_type(0, 0, v.z);
+}
+
+// construct from 9 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+ typename A, typename B, typename C,
+ typename D, typename E, typename F,
+ typename G, typename H, typename I>
+CONSTEXPR TMat33<T>::TMat33(
+ A m00, B m01, C m02,
+ D m10, E m11, F m12,
+ G m20, H m21, I m22) {
+ m_value[0] = col_type(m00, m01, m02);
+ m_value[1] = col_type(m10, m11, m12);
+ m_value[2] = col_type(m20, m21, m22);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TMat33<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
+}
+
+// Construct from 3 column vectors.
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2) {
+ m_value[0] = v0;
+ m_value[1] = v1;
+ m_value[2] = v2;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TQuaternion<U>& q) {
+ const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+ const U s = n > 0 ? 2/n : 0;
+ const U x = s*q.x;
+ const U y = s*q.y;
+ const U z = s*q.z;
+ const U xx = x*q.x;
+ const U xy = x*q.y;
+ const U xz = x*q.z;
+ const U xw = x*q.w;
+ const U yy = y*q.y;
+ const U yz = y*q.z;
+ const U yw = y*q.w;
+ const U zz = z*q.z;
+ const U zw = z*q.w;
+ m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw); // NOLINT
+ m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw); // NOLINT
+ m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy); // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::col_type PURE operator *(const TMat33<T>& lhs, const TVec3<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat33<U>::col_type result;
+ for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
+ return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::row_type PURE operator *(const TVec3<U>& lhs, const TMat33<T>& rhs) {
+ typename TMat33<U>::row_type result(TMat33<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
+ return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(TMat33<T> lhs, U rhs) {
+ return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(U lhs, const TMat33<T>& rhs) {
+ return rhs * lhs;
+}
+
+//------------------------------------------------------------------------------
+template <typename T>
+CONSTEXPR TMat33<T> orthogonalize(const TMat33<T>& m) {
+ TMat33<T> ret(TMat33<T>::NO_INIT);
+ ret[0] = normalize(m[0]);
+ ret[2] = normalize(cross(ret[0], m[1]));
+ ret[1] = normalize(cross(ret[2], ret[0]));
+ return ret;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat33<T>::col_type PURE diag(const TMat33<T>& m) {
+ return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat33<double> mat3d;
+typedef details::TMat33<float> mat3;
+typedef details::TMat33<float> mat3f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h
new file mode 100644
index 0000000..6119ba7
--- /dev/null
+++ b/libs/math/include/math/mat4.h
@@ -0,0 +1,586 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <math/mat3.h>
+#include <math/quat.h>
+#include <math/TMatHelpers.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 4x4 column-major matrix class.
+ *
+ * Conceptually a 4x4 matrix is a an array of 4 column double4:
+ *
+ * mat4 m =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0] & m[1] & m[2] & m[3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+ * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+ * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+ * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m(0,0) & m(0,1) & m(0,2) & m(0,3) \\
+ * m(1,0) & m(1,1) & m(1,2) & m(1,3) \\
+ * m(2,0) & m(2,1) & m(2,2) & m(2,3) \\
+ * m(3,0) & m(3,1) & m(3,2) & m(3,3) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4.
+ *
+ */
+template <typename T>
+class TMat44 : public TVecUnaryOperators<TMat44, T>,
+ public TVecComparisonOperators<TMat44, T>,
+ public TVecAddOperators<TMat44, T>,
+ public TMatProductOperators<TMat44, T>,
+ public TMatSquareFunctions<TMat44, T>,
+ public TMatTransform<TMat44, T>,
+ public TMatHelpers<TMat44, T>,
+ public TMatDebug<TMat44, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ typedef TVec4<T> col_type;
+ typedef TVec4<T> row_type;
+
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+ /*
+ * <-- N columns -->
+ *
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
+ *
+ * COL_SIZE = M
+ * ROW_SIZE = N
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+ */
+
+ col_type m_value[NUM_COLS];
+
+public:
+ // array access
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
+
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TMat44(const TMat44&) = default;
+ ~TMat44() = default;
+ TMat44& operator = (const TMat44&) = default;
+
+ /*
+ * constructors
+ */
+
+ // leaves object uninitialized. use with caution.
+ explicit constexpr TMat44(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
+
+ /** initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * 1 & 0 & 0 & 0 \\
+ * 0 & 1 & 0 & 0 \\
+ * 0 & 0 & 1 & 0 \\
+ * 0 & 0 & 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat44();
+
+ /** initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v & 0 & 0 & 0 \\
+ * 0 & v & 0 & 0 \\
+ * 0 & 0 & v & 0 \\
+ * 0 & 0 & 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template<typename U>
+ explicit CONSTEXPR TMat44(U v);
+
+ /** sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v[0] & 0 & 0 & 0 \\
+ * 0 & v[1] & 0 & 0 \\
+ * 0 & 0 & v[2] & 0 \\
+ * 0 & 0 & 0 & v[3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TVec4<U>& v);
+
+ // construct from another matrix of the same size
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TMat44<U>& rhs);
+
+ /** construct from 4 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v0 & v1 & v2 & v3 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename A, typename B, typename C, typename D>
+ CONSTEXPR TMat44(const TVec4<A>& v0, const TVec4<B>& v1, const TVec4<C>& v2, const TVec4<D>& v3);
+
+ /** construct from 16 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+ * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+ * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+ * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <
+ typename A, typename B, typename C, typename D,
+ typename E, typename F, typename G, typename H,
+ typename I, typename J, typename K, typename L,
+ typename M, typename N, typename O, typename P>
+ CONSTEXPR TMat44(
+ A m00, B m01, C m02, D m03,
+ E m10, F m11, G m12, H m13,
+ I m20, J m21, K m22, L m23,
+ M m30, N m31, O m32, P m33);
+
+ /**
+ * construct from a quaternion
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TQuaternion<U>& q);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(U const* rawArray);
+
+ /**
+ * construct from a 3x3 matrix
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TMat33<U>& matrix);
+
+ /**
+ * construct from a 3x3 matrix and 3d translation
+ */
+ template <typename U, typename V>
+ CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec3<V>& translation);
+
+ /**
+ * construct from a 3x3 matrix and 4d last column.
+ */
+ template <typename U, typename V>
+ CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec4<V>& column3);
+
+ /*
+ * helpers
+ */
+
+ static CONSTEXPR TMat44 ortho(T left, T right, T bottom, T top, T near, T far);
+
+ static CONSTEXPR TMat44 frustum(T left, T right, T bottom, T top, T near, T far);
+
+ enum class Fov {
+ HORIZONTAL,
+ VERTICAL
+ };
+ static CONSTEXPR TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL);
+
+ template <typename A, typename B, typename C>
+ static CONSTEXPR TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up);
+
+ template <typename A>
+ static CONSTEXPR TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) {
+ TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 };
+ return r.xyz / r.w;
+ }
+
+ template <typename A>
+ static CONSTEXPR TVec4<A> project(const TMat44& projectionMatrix, TVec4<A> vertice) {
+ vertice = projectionMatrix * vertice;
+ return { vertice.xyz / vertice.w, 1 };
+ }
+
+ /**
+ * Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix
+ */
+ inline constexpr TMat33<T> upperLeft() const {
+ return TMat33<T>(m_value[0].xyz, m_value[1].xyz, m_value[2].xyz);
+ }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat44<T>::TMat44() {
+ m_value[0] = col_type(1, 0, 0, 0);
+ m_value[1] = col_type(0, 1, 0, 0);
+ m_value[2] = col_type(0, 0, 1, 0);
+ m_value[3] = col_type(0, 0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U v) {
+ m_value[0] = col_type(v, 0, 0, 0);
+ m_value[1] = col_type(0, v, 0, 0);
+ m_value[2] = col_type(0, 0, v, 0);
+ m_value[3] = col_type(0, 0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat44<T>::TMat44(const TVec4<U>& v) {
+ m_value[0] = col_type(v.x, 0, 0, 0);
+ m_value[1] = col_type(0, v.y, 0, 0);
+ m_value[2] = col_type(0, 0, v.z, 0);
+ m_value[3] = col_type(0, 0, 0, v.w);
+}
+
+// construct from 16 scalars
+template<typename T>
+template <
+ typename A, typename B, typename C, typename D,
+ typename E, typename F, typename G, typename H,
+ typename I, typename J, typename K, typename L,
+ typename M, typename N, typename O, typename P>
+CONSTEXPR TMat44<T>::TMat44(
+ A m00, B m01, C m02, D m03,
+ E m10, F m11, G m12, H m13,
+ I m20, J m21, K m22, L m23,
+ M m30, N m31, O m32, P m33) {
+ m_value[0] = col_type(m00, m01, m02, m03);
+ m_value[1] = col_type(m10, m11, m12, m13);
+ m_value[2] = col_type(m20, m21, m22, m23);
+ m_value[3] = col_type(m30, m31, m32, m33);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat44<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
+}
+
+// Construct from 4 column vectors.
+template <typename T>
+template <typename A, typename B, typename C, typename D>
+CONSTEXPR TMat44<T>::TMat44(
+ const TVec4<A>& v0, const TVec4<B>& v1,
+ const TVec4<C>& v2, const TVec4<D>& v3) {
+ m_value[0] = col_type(v0);
+ m_value[1] = col_type(v1);
+ m_value[2] = col_type(v2);
+ m_value[3] = col_type(v3);
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TQuaternion<U>& q) {
+ const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+ const U s = n > 0 ? 2/n : 0;
+ const U x = s*q.x;
+ const U y = s*q.y;
+ const U z = s*q.z;
+ const U xx = x*q.x;
+ const U xy = x*q.y;
+ const U xz = x*q.z;
+ const U xw = x*q.w;
+ const U yy = y*q.y;
+ const U yz = y*q.z;
+ const U yw = y*q.w;
+ const U zz = z*q.z;
+ const U zw = z*q.w;
+ m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw, 0);
+ m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw, 0); // NOLINT
+ m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy, 0); // NOLINT
+ m_value[3] = col_type( 0, 0, 0, 1); // NOLINT
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+ m_value[3] = col_type( 0, 0, 0, 1); // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec3<V>& v) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+ m_value[3] = col_type( v[0], v[1], v[2], 1); // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec4<V>& v) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); // NOLINT
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); // NOLINT
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); // NOLINT
+ m_value[3] = col_type( v[0], v[1], v[2], v[3]); // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------------------
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
+ TMat44<T> m;
+ m[0][0] = 2 / (right - left);
+ m[1][1] = 2 / (top - bottom);
+ m[2][2] = -2 / (far - near);
+ m[3][0] = -(right + left) / (right - left);
+ m[3][1] = -(top + bottom) / (top - bottom);
+ m[3][2] = -(far + near) / (far - near);
+ return m;
+}
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
+ TMat44<T> m;
+ m[0][0] = (2 * near) / (right - left);
+ m[1][1] = (2 * near) / (top - bottom);
+ m[2][0] = (right + left) / (right - left);
+ m[2][1] = (top + bottom) / (top - bottom);
+ m[2][2] = -(far + near) / (far - near);
+ m[2][3] = -1;
+ m[3][2] = -(2 * far * near) / (far - near);
+ m[3][3] = 0;
+ return m;
+}
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) {
+ T h;
+ T w;
+
+ if (direction == TMat44::Fov::VERTICAL) {
+ h = std::tan(fov * M_PI / 360.0f) * near;
+ w = h * aspect;
+ } else {
+ w = std::tan(fov * M_PI / 360.0f) * near;
+ h = w / aspect;
+ }
+ return frustum(-w, w, -h, h, near, far);
+}
+
+/*
+ * Returns a matrix representing the pose of a virtual camera looking towards -Z in its
+ * local Y-up coordinate system. "eye" is where the camera is located, "center" is the points its
+ * looking at and "up" defines where the Y axis of the camera's local coordinate system is.
+ */
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) {
+ TVec3<T> z_axis(normalize(center - eye));
+ TVec3<T> norm_up(normalize(up));
+ if (std::abs(dot(z_axis, norm_up)) > 0.999) {
+ // Fix up vector if we're degenerate (looking straight up, basically)
+ norm_up = { norm_up.z, norm_up.x, norm_up.y };
+ }
+ TVec3<T> x_axis(normalize(cross(z_axis, norm_up)));
+ TVec3<T> y_axis(cross(x_axis, z_axis));
+ return TMat44<T>(
+ TVec4<T>(x_axis, 0),
+ TVec4<T>(y_axis, 0),
+ TVec4<T>(-z_axis, 0),
+ TVec4<T>(eye, 1));
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec4<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat44<T>::col_type result;
+ for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
+ return result;
+}
+
+// mat44 * vec3, result is vec3( mat44 * {vec3, 1} )
+template <typename T, typename U>
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec3<U>& rhs) {
+ return lhs * TVec4<U>{ rhs, 1 };
+}
+
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<U>::row_type PURE operator *(const TVec4<U>& lhs, const TMat44<T>& rhs) {
+ typename TMat44<U>::row_type result(TMat44<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
+ return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template <typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(TMat44<T> lhs, U rhs) {
+ return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template <typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(U lhs, const TMat44<T>& rhs) {
+ return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+typename TMat44<T>::col_type PURE diag(const TMat44<T>& m) {
+ return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat44<double> mat4d;
+typedef details::TMat44<float> mat4;
+typedef details::TMat44<float> mat4f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h
new file mode 100644
index 0000000..1936a2b
--- /dev/null
+++ b/libs/math/include/math/quat.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <math/half.h>
+#include <math/TQuatHelpers.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifndef PURE
+#define PURE __attribute__((pure))
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TQuaternion : public TVecAddOperators<TQuaternion, T>,
+ public TVecUnaryOperators<TQuaternion, T>,
+ public TVecComparisonOperators<TQuaternion, T>,
+ public TQuatProductOperators<TQuaternion, T>,
+ public TQuatFunctions<TQuaternion, T>,
+ public TQuatDebug<TQuaternion, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ /*
+ * quaternion internals stored as:
+ *
+ * q = w + xi + yj + zk
+ *
+ * q[0] = x;
+ * q[1] = y;
+ * q[2] = z;
+ * q[3] = w;
+ *
+ */
+ union {
+ struct { T x, y, z, w; };
+ TVec4<T> xyzw;
+ TVec3<T> xyz;
+ TVec2<T> xy;
+ };
+
+ enum { SIZE = 4 };
+ inline constexpr static size_type size() { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TQuaternion(const TQuaternion&) = default;
+ ~TQuaternion() = default;
+ TQuaternion& operator = (const TQuaternion&) = default;
+
+ // constructors
+
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TQuaternion(no_init) : xyzw(TVec4<T>::NO_INIT) {}
+
+ // default constructor. sets all values to zero.
+ constexpr TQuaternion() : x(0), y(0), z(0), w(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A>
+ constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {
+ static_assert(std::is_arithmetic<A>::value, "requires arithmetic type");
+ }
+
+ // initialize from 4 values to w + xi + yj + zk
+ template<typename A, typename B, typename C, typename D>
+ constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) { }
+
+ // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w
+ template<typename A, typename B>
+ constexpr TQuaternion(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+ // initialize from a double4
+ template<typename A>
+ constexpr explicit TQuaternion(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+ // initialize from a quaternion of a different type
+ template<typename A>
+ constexpr explicit TQuaternion(const TQuaternion<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+ // conjugate operator
+ constexpr TQuaternion operator~() const {
+ return conj(*this);
+ }
+
+ // constructs a quaternion from an axis and angle
+ template <typename A, typename B>
+ constexpr static TQuaternion PURE fromAxisAngle(const TVec3<A>& axis, B angle) {
+ return TQuaternion(std::sin(angle*0.5) * normalize(axis), std::cos(angle*0.5));
+ }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TQuaternion<double> quatd;
+typedef details::TQuaternion<float> quat;
+typedef details::TQuaternion<float> quatf;
+typedef details::TQuaternion<half> quath;
+
+constexpr inline quat operator"" _i(long double v) {
+ return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(long double v) {
+ return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(long double v) {
+ return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quat operator"" _i(unsigned long long v) { // NOLINT
+ return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(unsigned long long v) { // NOLINT
+ return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(unsigned long long v) { // NOLINT
+ return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quatd operator"" _id(long double v) {
+ return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(long double v) {
+ return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(long double v) {
+ return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+constexpr inline quatd operator"" _id(unsigned long long v) { // NOLINT
+ return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(unsigned long long v) { // NOLINT
+ return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(unsigned long long v) { // NOLINT
+ return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
+
+#undef PURE
diff --git a/libs/math/include/math/scalar.h b/libs/math/include/math/scalar.h
new file mode 100644
index 0000000..2eced92
--- /dev/null
+++ b/libs/math/include/math/scalar.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <cmath>
+
+namespace android {
+
+template<typename T>
+static constexpr T saturate(T v) noexcept {
+ return T(std::min(T(1), std::max(T(0), v)));
+}
+
+template<typename T>
+static constexpr T clamp(T v, T min, T max) noexcept {
+ return T(std::min(max, std::max(min, v)));
+}
+
+template<typename T>
+static constexpr T mix(T x, T y, T a) noexcept {
+ return x * (T(1) - a) + y * a;
+}
+
+template<typename T>
+static constexpr T lerp(T x, T y, T a) noexcept {
+ return mix(x, y, a);
+}
+
+} // namespace std
diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h
new file mode 100644
index 0000000..a347633
--- /dev/null
+++ b/libs/math/include/math/vec2.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <math/TVecHelpers.h>
+#include <math/half.h>
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <type_traits>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec2 : public TVecProductOperators<TVec2, T>,
+ public TVecAddOperators<TVec2, T>,
+ public TVecUnaryOperators<TVec2, T>,
+ public TVecComparisonOperators<TVec2, T>,
+ public TVecFunctions<TVec2, T>,
+ public TVecDebug<TVec2, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ union {
+ struct { T x, y; };
+ struct { T s, t; };
+ struct { T r, g; };
+ };
+
+ static constexpr size_t SIZE = 2;
+ inline constexpr size_type size() const { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TVec2(const TVec2&) = default;
+ ~TVec2() = default;
+ TVec2& operator = (const TVec2&) = default;
+
+ // constructors
+
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TVec2(no_init) { }
+
+ // default constructor
+ constexpr TVec2() : x(0), y(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec2(A v) : x(v), y(v) { }
+
+ template<typename A, typename B>
+ constexpr TVec2(A x, B y) : x(x), y(y) { }
+
+ template<typename A>
+ explicit
+ constexpr TVec2(const TVec2<A>& v) : x(v.x), y(v.y) { }
+
+ // cross product works only on vectors of size 2 or 3
+ template<typename RT>
+ friend inline
+ constexpr value_type cross(const TVec2& u, const TVec2<RT>& v) {
+ return value_type(u.x*v.y - u.y*v.x);
+ }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec2<double> double2;
+typedef details::TVec2<float> float2;
+typedef details::TVec2<float> vec2;
+typedef details::TVec2<half> half2;
+typedef details::TVec2<int32_t> int2;
+typedef details::TVec2<uint32_t> uint2;
+typedef details::TVec2<int16_t> short2;
+typedef details::TVec2<uint16_t> ushort2;
+typedef details::TVec2<int8_t> byte2;
+typedef details::TVec2<uint8_t> ubyte2;
+typedef details::TVec2<bool> bool2;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h
new file mode 100644
index 0000000..009fd84
--- /dev/null
+++ b/libs/math/include/math/vec3.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <math/vec2.h>
+#include <math/half.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec3 : public TVecProductOperators<TVec3, T>,
+ public TVecAddOperators<TVec3, T>,
+ public TVecUnaryOperators<TVec3, T>,
+ public TVecComparisonOperators<TVec3, T>,
+ public TVecFunctions<TVec3, T>,
+ public TVecDebug<TVec3, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ union {
+ struct { T x, y, z; };
+ struct { T s, t, p; };
+ struct { T r, g, b; };
+ TVec2<T> xy;
+ TVec2<T> st;
+ TVec2<T> rg;
+ };
+
+ static constexpr size_t SIZE = 3;
+ inline constexpr size_type size() const { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TVec3(const TVec3&) = default;
+ ~TVec3() = default;
+ TVec3& operator = (const TVec3&) = default;
+
+ // constructors
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TVec3(no_init) { }
+
+ // default constructor
+ constexpr TVec3() : x(0), y(0), z(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec3(A v) : x(v), y(v), z(v) { }
+
+ template<typename A, typename B, typename C>
+ constexpr TVec3(A x, B y, C z) : x(x), y(y), z(z) { }
+
+ template<typename A, typename B>
+ constexpr TVec3(const TVec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
+
+ template<typename A>
+ explicit
+ constexpr TVec3(const TVec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
+
+ // cross product works only on vectors of size 3
+ template <typename RT>
+ friend inline
+ constexpr TVec3 cross(const TVec3& u, const TVec3<RT>& v) {
+ return TVec3(
+ u.y*v.z - u.z*v.y,
+ u.z*v.x - u.x*v.z,
+ u.x*v.y - u.y*v.x);
+ }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec3<double> double3;
+typedef details::TVec3<float> float3;
+typedef details::TVec3<float> vec3;
+typedef details::TVec3<half> half3;
+typedef details::TVec3<int32_t> int3;
+typedef details::TVec3<uint32_t> uint3;
+typedef details::TVec3<int16_t> short3;
+typedef details::TVec3<uint16_t> ushort3;
+typedef details::TVec3<int8_t> byte3;
+typedef details::TVec3<uint8_t> ubyte3;
+typedef details::TVec3<bool> bool3;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec4.h b/libs/math/include/math/vec4.h
new file mode 100644
index 0000000..1e279fe
--- /dev/null
+++ b/libs/math/include/math/vec4.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <math/vec3.h>
+#include <math/half.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec4 : public TVecProductOperators<TVec4, T>,
+ public TVecAddOperators<TVec4, T>,
+ public TVecUnaryOperators<TVec4, T>,
+ public TVecComparisonOperators<TVec4, T>,
+ public TVecFunctions<TVec4, T>,
+ public TVecDebug<TVec4, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ union {
+ struct { T x, y, z, w; };
+ struct { T s, t, p, q; };
+ struct { T r, g, b, a; };
+ TVec2<T> xy;
+ TVec2<T> st;
+ TVec2<T> rg;
+ TVec3<T> xyz;
+ TVec3<T> stp;
+ TVec3<T> rgb;
+ };
+
+ static constexpr size_t SIZE = 4;
+ inline constexpr size_type size() const { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TVec4(const TVec4&) = default;
+ ~TVec4() = default;
+ TVec4& operator = (const TVec4&) = default;
+
+ // constructors
+
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TVec4(no_init) { }
+
+ // default constructor
+ constexpr TVec4() : x(0), y(0), z(0), w(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec4(A v) : x(v), y(v), z(v), w(v) { }
+
+ template<typename A, typename B, typename C, typename D>
+ constexpr TVec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
+
+ template<typename A, typename B, typename C>
+ constexpr TVec4(const TVec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
+
+ template<typename A, typename B>
+ constexpr TVec4(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+ template<typename A>
+ explicit
+ constexpr TVec4(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec4<double> double4;
+typedef details::TVec4<float> float4;
+typedef details::TVec4<float> vec4;
+typedef details::TVec4<half> half4;
+typedef details::TVec4<int32_t> int4;
+typedef details::TVec4<uint32_t> uint4;
+typedef details::TVec4<int16_t> short4;
+typedef details::TVec4<uint16_t> ushort4;
+typedef details::TVec4<int8_t> byte4;
+typedef details::TVec4<uint8_t> ubyte4;
+typedef details::TVec4<bool> bool4;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
new file mode 100644
index 0000000..0ed24a2
--- /dev/null
+++ b/libs/math/tests/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2014 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.
+//
+
+cc_test {
+ name: "vec_test",
+ srcs: ["vec_test.cpp"],
+ static_libs: ["libmath"],
+}
+
+cc_test {
+ name: "mat_test",
+ srcs: ["mat_test.cpp"],
+ static_libs: ["libmath"],
+}
+
+cc_test {
+ name: "half_test",
+ srcs: ["half_test.cpp"],
+ static_libs: ["libmath"],
+}
+
+cc_test {
+ name: "quat_test",
+ srcs: ["quat_test.cpp"],
+ static_libs: ["libmath"],
+}
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
new file mode 100644
index 0000000..496a7ef
--- /dev/null
+++ b/libs/math/tests/half_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "HalfTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/half.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HalfTest : public testing::Test {
+protected:
+};
+
+TEST_F(HalfTest, Basics) {
+
+ EXPECT_EQ(2UL, sizeof(half));
+
+ // test +/- zero
+ EXPECT_EQ(0x0000, half( 0.0f).getBits());
+ EXPECT_EQ(0x8000, half(-0.0f).getBits());
+
+ // test nan
+ EXPECT_EQ(0x7e00, half(NAN).getBits());
+
+ // test +/- infinity
+ EXPECT_EQ(0x7C00, half( std::numeric_limits<float>::infinity()).getBits());
+ EXPECT_EQ(0xFC00, half(-std::numeric_limits<float>::infinity()).getBits());
+
+ // test a few known values
+ EXPECT_EQ(0x3C01, half(1.0009765625).getBits());
+ EXPECT_EQ(0xC000, half(-2).getBits());
+ EXPECT_EQ(0x0400, half(6.10352e-5).getBits());
+ EXPECT_EQ(0x7BFF, half(65504).getBits());
+ EXPECT_EQ(0x3555, half(1.0f/3).getBits());
+
+ // numeric limits
+ EXPECT_EQ(0x7C00, std::numeric_limits<half>::infinity().getBits());
+ EXPECT_EQ(0x0400, std::numeric_limits<half>::min().getBits());
+ EXPECT_EQ(0x7BFF, std::numeric_limits<half>::max().getBits());
+ EXPECT_EQ(0xFBFF, std::numeric_limits<half>::lowest().getBits());
+
+ // denormals (flushed to zero)
+ EXPECT_EQ(0x0000, half( 6.09756e-5).getBits()); // if handled, should be: 0x03FF
+ EXPECT_EQ(0x0000, half( 5.96046e-8).getBits()); // if handled, should be: 0x0001
+ EXPECT_EQ(0x8000, half(-6.09756e-5).getBits()); // if handled, should be: 0x83FF
+ EXPECT_EQ(0x8000, half(-5.96046e-8).getBits()); // if handled, should be: 0x8001
+
+ // test all exactly representable integers
+ for (int i=-2048 ; i<= 2048 ; ++i) {
+ half h = i;
+ EXPECT_EQ(i, float(h));
+ }
+}
+
+TEST_F(HalfTest, Literals) {
+ half one = 1.0_hf;
+ half pi = 3.1415926_hf;
+ half minusTwo = -2.0_hf;
+
+ EXPECT_EQ(half(1.0f), one);
+ EXPECT_EQ(half(3.1415926), pi);
+ EXPECT_EQ(half(-2.0f), minusTwo);
+}
+
+
+TEST_F(HalfTest, Vec) {
+ float4 f4(1,2,3,4);
+ half4 h4(f4);
+ half3 h3(f4.xyz);
+ half2 h2(f4.xy);
+
+ EXPECT_EQ(f4, h4);
+ EXPECT_EQ(f4.xyz, h3);
+ EXPECT_EQ(f4.xy, h2);
+}
+
+}; // namespace android
diff --git a/libs/math/tests/mat_test.cpp b/libs/math/tests/mat_test.cpp
new file mode 100644
index 0000000..c365366
--- /dev/null
+++ b/libs/math/tests/mat_test.cpp
@@ -0,0 +1,692 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "MatTest"
+
+#include <stdlib.h>
+
+#include <limits>
+#include <random>
+#include <functional>
+
+#include <gtest/gtest.h>
+
+#include <math/mat2.h>
+#include <math/mat4.h>
+
+namespace android {
+
+class MatTest : public testing::Test {
+protected:
+};
+
+TEST_F(MatTest, Basics) {
+ mat4 m0;
+ EXPECT_EQ(sizeof(mat4), sizeof(float)*16);
+}
+
+TEST_F(MatTest, ComparisonOps) {
+ mat4 m0;
+ mat4 m1(2);
+
+ EXPECT_TRUE(m0 == m0);
+ EXPECT_TRUE(m0 != m1);
+ EXPECT_FALSE(m0 != m0);
+ EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(MatTest, Constructors) {
+ mat4 m0;
+ ASSERT_EQ(m0[0].x, 1);
+ ASSERT_EQ(m0[0].y, 0);
+ ASSERT_EQ(m0[0].z, 0);
+ ASSERT_EQ(m0[0].w, 0);
+ ASSERT_EQ(m0[1].x, 0);
+ ASSERT_EQ(m0[1].y, 1);
+ ASSERT_EQ(m0[1].z, 0);
+ ASSERT_EQ(m0[1].w, 0);
+ ASSERT_EQ(m0[2].x, 0);
+ ASSERT_EQ(m0[2].y, 0);
+ ASSERT_EQ(m0[2].z, 1);
+ ASSERT_EQ(m0[2].w, 0);
+ ASSERT_EQ(m0[3].x, 0);
+ ASSERT_EQ(m0[3].y, 0);
+ ASSERT_EQ(m0[3].z, 0);
+ ASSERT_EQ(m0[3].w, 1);
+
+ mat4 m1(2);
+ mat4 m2(vec4(2));
+ mat4 m3(m2);
+
+ EXPECT_EQ(m1, m2);
+ EXPECT_EQ(m2, m3);
+ EXPECT_EQ(m3, m1);
+
+ mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4));
+}
+
+TEST_F(MatTest, ArithmeticOps) {
+ mat4 m0;
+ mat4 m1(2);
+ mat4 m2(vec4(2));
+
+ m1 += m2;
+ EXPECT_EQ(mat4(4), m1);
+
+ m2 -= m1;
+ EXPECT_EQ(mat4(-2), m2);
+
+ m1 *= 2;
+ EXPECT_EQ(mat4(8), m1);
+
+ m1 /= 2;
+ EXPECT_EQ(mat4(4), m1);
+
+ m0 = -m0;
+ EXPECT_EQ(mat4(-1), m0);
+}
+
+TEST_F(MatTest, UnaryOps) {
+ const mat4 identity;
+ mat4 m0;
+
+ m0 = -m0;
+ EXPECT_EQ(mat4(vec4(-1, 0, 0, 0),
+ vec4(0, -1, 0, 0),
+ vec4(0, 0, -1,